1 /* 2 * Copyright (c) 2011, 2020, 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.lwawt.macosx; 27 28 29 import java.awt.*; 30 import java.awt.geom.Rectangle2D; 31 import java.awt.image.BufferedImage; 32 import java.awt.print.*; 33 import java.security.AccessController; 34 import java.security.PrivilegedAction; 35 36 import javax.print.*; 37 import javax.print.attribute.PrintRequestAttributeSet; 38 import javax.print.attribute.HashPrintRequestAttributeSet; 39 import javax.print.attribute.standard.Copies; 40 import javax.print.attribute.standard.Media; 41 import javax.print.attribute.standard.MediaPrintableArea; 42 import javax.print.attribute.standard.MediaSize; 43 import javax.print.attribute.standard.MediaSizeName; 44 import javax.print.attribute.standard.PageRanges; 45 import javax.print.attribute.Attribute; 46 47 import sun.java2d.*; 48 import sun.print.*; 49 50 public final class CPrinterJob extends RasterPrinterJob { 51 // NOTE: This uses RasterPrinterJob as a base, but it doesn't use 52 // all of the RasterPrinterJob functions. RasterPrinterJob will 53 // break down printing to pieces that aren't necessary under MacOSX 54 // printing, such as controlling the # of copies and collating. These 55 // are handled by the native printing. RasterPrinterJob is kept for 56 // future compatibility and the state keeping that it handles. 57 58 private static String sShouldNotReachHere = "Should not reach here."; 59 60 private volatile SecondaryLoop printingLoop; 61 62 private boolean noDefaultPrinter = false; 63 64 private static Font defaultFont; 65 66 private String tray = null; 67 68 // This is the NSPrintInfo for this PrinterJob. Protect multi thread 69 // access to it. It is used by the pageDialog, jobDialog, and printLoop. 70 // This way the state of these items is shared across these calls. 71 // PageFormat data is passed in and set on the fNSPrintInfo on a per call 72 // basis. 73 private long fNSPrintInfo = -1; 74 private Object fNSPrintInfoLock = new Object(); 75 76 static { 77 // AWT has to be initialized for the native code to function correctly. Toolkit.getDefaultToolkit()78 Toolkit.getDefaultToolkit(); 79 } 80 81 /** 82 * Presents a dialog to the user for changing the properties of 83 * the print job. 84 * This method will display a native dialog if a native print 85 * service is selected, and user choice of printers will be restricted 86 * to these native print services. 87 * To present the cross platform print dialog for all services, 88 * including native ones instead use 89 * {@code printDialog(PrintRequestAttributeSet)}. 90 * <p> 91 * PrinterJob implementations which can use PrintService's will update 92 * the PrintService for this PrinterJob to reflect the new service 93 * selected by the user. 94 * @return {@code true} if the user does not cancel the dialog; 95 * {@code false} otherwise. 96 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 97 * returns true. 98 * @see java.awt.GraphicsEnvironment#isHeadless 99 */ 100 @Override printDialog()101 public boolean printDialog() throws HeadlessException { 102 if (GraphicsEnvironment.isHeadless()) { 103 throw new HeadlessException(); 104 } 105 106 if (noDefaultPrinter) { 107 return false; 108 } 109 110 if (attributes == null) { 111 attributes = new HashPrintRequestAttributeSet(); 112 } 113 114 if (getPrintService() instanceof StreamPrintService) { 115 return super.printDialog(attributes); 116 } 117 118 return jobSetup(getPageable(), checkAllowedToPrintToFile()); 119 } 120 121 /** 122 * Displays a dialog that allows modification of a 123 * {@code PageFormat} instance. 124 * The {@code page} argument is used to initialize controls 125 * in the page setup dialog. 126 * If the user cancels the dialog then this method returns the 127 * original {@code page} object unmodified. 128 * If the user okays the dialog then this method returns a new 129 * {@code PageFormat} object with the indicated changes. 130 * In either case, the original {@code page} object is 131 * not modified. 132 * @param page the default {@code PageFormat} presented to the 133 * user for modification 134 * @return the original {@code page} object if the dialog 135 * is cancelled; a new {@code PageFormat} object 136 * containing the format indicated by the user if the 137 * dialog is acknowledged. 138 * @exception HeadlessException if GraphicsEnvironment.isHeadless() 139 * returns true. 140 * @see java.awt.GraphicsEnvironment#isHeadless 141 * @since 1.2 142 */ 143 @Override pageDialog(PageFormat page)144 public PageFormat pageDialog(PageFormat page) throws HeadlessException { 145 if (GraphicsEnvironment.isHeadless()) { 146 throw new HeadlessException(); 147 } 148 149 if (noDefaultPrinter) { 150 return page; 151 } 152 153 if (getPrintService() instanceof StreamPrintService) { 154 return super.pageDialog(page); 155 } 156 157 PageFormat pageClone = (PageFormat) page.clone(); 158 boolean doIt = pageSetup(pageClone, null); 159 return doIt ? pageClone : page; 160 } 161 162 /** 163 * Clones the {@code PageFormat} argument and alters the 164 * clone to describe a default page size and orientation. 165 * @param page the {@code PageFormat} to be cloned and altered 166 * @return clone of {@code page}, altered to describe a default 167 * {@code PageFormat}. 168 */ 169 @Override defaultPage(PageFormat page)170 public PageFormat defaultPage(PageFormat page) { 171 PageFormat newPage = (PageFormat)page.clone(); 172 getDefaultPage(newPage); 173 return newPage; 174 } 175 176 @Override setAttributes(PrintRequestAttributeSet attributes)177 protected void setAttributes(PrintRequestAttributeSet attributes) throws PrinterException { 178 super.setAttributes(attributes); 179 180 if (attributes == null) { 181 return; 182 } 183 Attribute attr = attributes.get(Media.class); 184 if (attr instanceof CustomMediaTray) { 185 CustomMediaTray customTray = (CustomMediaTray) attr; 186 tray = customTray.getChoiceName(); 187 } 188 189 PageRanges pageRangesAttr = (PageRanges)attributes.get(PageRanges.class); 190 if (isSupportedValue(pageRangesAttr, attributes)) { 191 SunPageSelection rangeSelect = (SunPageSelection)attributes.get(SunPageSelection.class); 192 // If rangeSelect is not null, we are using AWT's print dialog that has 193 // All, Selection, and Range radio buttons 194 if (rangeSelect == null || rangeSelect == SunPageSelection.RANGE) { 195 int[][] range = pageRangesAttr.getMembers(); 196 // setPageRange will set firstPage and lastPage as called in getFirstPage 197 // and getLastPage 198 setPageRange(range[0][0] - 1, range[0][1] - 1); 199 } else { 200 // if rangeSelect is SunPageSelection.ALL 201 // then setPageRange appropriately 202 setPageRange(-1, -1); 203 } 204 } 205 } 206 setPageRangeAttribute(int from, int to, boolean isRangeSet)207 private void setPageRangeAttribute(int from, int to, boolean isRangeSet) { 208 if (attributes != null) { 209 // since native Print use zero-based page indices, 210 // we need to store in 1-based format in attributes set 211 // but setPageRange again uses zero-based indices so it should be 212 // 1 less than pageRanges attribute 213 if (isRangeSet) { 214 attributes.add(new PageRanges(from+1, to+1)); 215 attributes.add(SunPageSelection.RANGE); 216 setPageRange(from, to); 217 } else { 218 attributes.add(SunPageSelection.ALL); 219 } 220 } 221 } 222 setCopiesAttribute(int copies)223 private void setCopiesAttribute(int copies) { 224 if (attributes != null) { 225 attributes.add(new Copies(copies)); 226 super.setCopies(copies); 227 } 228 } 229 230 volatile boolean onEventThread; 231 232 @Override cancelDoc()233 protected void cancelDoc() throws PrinterAbortException { 234 super.cancelDoc(); 235 if (printingLoop != null) { 236 printingLoop.exit(); 237 } 238 } 239 completePrintLoop()240 private void completePrintLoop() { 241 Runnable r = new Runnable() { public void run() { 242 synchronized(this) { 243 performingPrinting = false; 244 } 245 if (printingLoop != null) { 246 printingLoop.exit(); 247 } 248 }}; 249 250 if (onEventThread) { 251 try { EventQueue.invokeAndWait(r); } catch (Exception e) { e.printStackTrace(); } 252 } else { 253 r.run(); 254 } 255 } 256 257 boolean isPrintToFile = false; setPrintToFile(boolean printToFile)258 private void setPrintToFile(boolean printToFile) { 259 isPrintToFile = printToFile; 260 } 261 262 @Override print(PrintRequestAttributeSet attributes)263 public void print(PrintRequestAttributeSet attributes) throws PrinterException { 264 // NOTE: Some of this code is copied from RasterPrinterJob. 265 266 // this code uses javax.print APIs 267 // this will make it print directly to the printer 268 // this will not work if the user clicks on the "Preview" button 269 // However if the printer is a StreamPrintService, its the right path. 270 PrintService psvc = getPrintService(); 271 272 if (psvc == null && !isPrintToFile) { 273 throw new PrinterException("No print service found."); 274 } 275 276 if (psvc instanceof StreamPrintService) { 277 spoolToService(psvc, attributes); 278 return; 279 } 280 281 282 setAttributes(attributes); 283 // throw exception for invalid destination 284 if (destinationAttr != null) { 285 validateDestination(destinationAttr); 286 } 287 288 /* Get the range of pages we are to print. If the 289 * last page to print is unknown, then we print to 290 * the end of the document. Note that firstPage 291 * and lastPage are 0 based page indices. 292 */ 293 294 int firstPage = getFirstPage(); 295 int lastPage = getLastPage(); 296 if(lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES) { 297 int totalPages = mDocument.getNumberOfPages(); 298 if (totalPages != Pageable.UNKNOWN_NUMBER_OF_PAGES) { 299 lastPage = mDocument.getNumberOfPages() - 1; 300 } 301 } 302 303 try { 304 synchronized (this) { 305 performingPrinting = true; 306 userCancelled = false; 307 } 308 309 //Add support for PageRange 310 PageRanges pr = (attributes == null) ? null 311 : (PageRanges)attributes.get(PageRanges.class); 312 int[][] prMembers = (pr == null) ? new int[0][0] : pr.getMembers(); 313 int loopi = 0; 314 do { 315 if (EventQueue.isDispatchThread()) { 316 // This is an AWT EventQueue, and this print rendering loop needs to block it. 317 318 onEventThread = true; 319 320 printingLoop = AccessController.doPrivileged(new PrivilegedAction<SecondaryLoop>() { 321 @Override 322 public SecondaryLoop run() { 323 return Toolkit.getDefaultToolkit() 324 .getSystemEventQueue() 325 .createSecondaryLoop(); 326 } 327 }); 328 329 try { 330 // Fire off the print rendering loop on the AppKit thread, and don't have 331 // it wait and block this thread. 332 if (printLoop(false, firstPage, lastPage)) { 333 // Start a secondary loop on EDT until printing operation is finished or cancelled 334 printingLoop.enter(); 335 } 336 } catch (Exception e) { 337 e.printStackTrace(); 338 } 339 } else { 340 // Fire off the print rendering loop on the AppKit, and block this thread 341 // until it is done. 342 // But don't actually block... we need to come back here! 343 onEventThread = false; 344 345 try { 346 printLoop(true, firstPage, lastPage); 347 } catch (Exception e) { 348 e.printStackTrace(); 349 } 350 } 351 if (++loopi < prMembers.length) { 352 firstPage = prMembers[loopi][0]-1; 353 lastPage = prMembers[loopi][1] -1; 354 } 355 } while (loopi < prMembers.length); 356 } finally { 357 synchronized (this) { 358 // NOTE: Native code shouldn't allow exceptions out while 359 // printing. They should cancel the print loop. 360 performingPrinting = false; 361 notify(); 362 } 363 if (printingLoop != null) { 364 printingLoop.exit(); 365 } 366 } 367 368 // Normalize the collated, # copies, numPages, first/last pages. Need to 369 // make note of pageRangesAttr. 370 371 // Set up NSPrintInfo with the java settings (PageFormat & Paper). 372 373 // Create an NSView for printing. Have knowsPageRange return YES, and give the correct 374 // range, or MAX? if unknown. Have rectForPage do a peekGraphics check before returning 375 // the rectangle. Have drawRect do the real render of the page. Have printJobTitle do 376 // the right thing. 377 378 // Call NSPrintOperation, it will call NSView.drawRect: for each page. 379 380 // NSView.drawRect: will create a CPrinterGraphics with the current CGContextRef, and then 381 // pass this Graphics onto the Printable with the appropriate PageFormat and index. 382 383 // Need to be able to cancel the NSPrintOperation (using code from RasterPrinterJob, be 384 // sure to initialize userCancelled and performingPrinting member variables). 385 386 // Extensions available from AppKit: Print to PDF or EPS file! 387 } 388 389 /** 390 * Returns the resolution in dots per inch across the width 391 * of the page. 392 */ 393 @Override getXRes()394 protected double getXRes() { 395 // NOTE: This is not used in the CPrinterJob code path. 396 return 0; 397 } 398 399 /** 400 * Returns the resolution in dots per inch down the height 401 * of the page. 402 */ 403 @Override getYRes()404 protected double getYRes() { 405 // NOTE: This is not used in the CPrinterJob code path. 406 return 0; 407 } 408 409 /** 410 * Must be obtained from the current printer. 411 * Value is in device pixels. 412 * Not adjusted for orientation of the paper. 413 */ 414 @Override getPhysicalPrintableX(Paper p)415 protected double getPhysicalPrintableX(Paper p) { 416 // NOTE: This is not used in the CPrinterJob code path. 417 return 0; 418 } 419 420 /** 421 * Must be obtained from the current printer. 422 * Value is in device pixels. 423 * Not adjusted for orientation of the paper. 424 */ 425 @Override getPhysicalPrintableY(Paper p)426 protected double getPhysicalPrintableY(Paper p) { 427 // NOTE: This is not used in the CPrinterJob code path. 428 return 0; 429 } 430 431 /** 432 * Must be obtained from the current printer. 433 * Value is in device pixels. 434 * Not adjusted for orientation of the paper. 435 */ 436 @Override getPhysicalPrintableWidth(Paper p)437 protected double getPhysicalPrintableWidth(Paper p) { 438 // NOTE: This is not used in the CPrinterJob code path. 439 return 0; 440 } 441 442 /** 443 * Must be obtained from the current printer. 444 * Value is in device pixels. 445 * Not adjusted for orientation of the paper. 446 */ 447 @Override getPhysicalPrintableHeight(Paper p)448 protected double getPhysicalPrintableHeight(Paper p) { 449 // NOTE: This is not used in the CPrinterJob code path. 450 return 0; 451 } 452 453 /** 454 * Must be obtained from the current printer. 455 * Value is in device pixels. 456 * Not adjusted for orientation of the paper. 457 */ 458 @Override getPhysicalPageWidth(Paper p)459 protected double getPhysicalPageWidth(Paper p) { 460 // NOTE: This is not used in the CPrinterJob code path. 461 return 0; 462 } 463 464 /** 465 * Must be obtained from the current printer. 466 * Value is in device pixels. 467 * Not adjusted for orientation of the paper. 468 */ 469 @Override getPhysicalPageHeight(Paper p)470 protected double getPhysicalPageHeight(Paper p) { 471 // NOTE: This is not used in the CPrinterJob code path. 472 return 0; 473 } 474 475 /** 476 * Begin a new page. This call's Window's 477 * StartPage routine. 478 */ startPage(PageFormat format, Printable painter, int index)479 protected void startPage(PageFormat format, Printable painter, int index) throws PrinterException { 480 // NOTE: This is not used in the CPrinterJob code path. 481 throw new PrinterException(sShouldNotReachHere); 482 } 483 484 /** 485 * End a page. 486 */ 487 @Override endPage(PageFormat format, Printable painter, int index)488 protected void endPage(PageFormat format, Printable painter, int index) throws PrinterException { 489 // NOTE: This is not used in the CPrinterJob code path. 490 throw new PrinterException(sShouldNotReachHere); 491 } 492 493 /** 494 * Prints the contents of the array of ints, 'data' 495 * to the current page. The band is placed at the 496 * location (x, y) in device coordinates on the 497 * page. The width and height of the band is 498 * specified by the caller. 499 */ 500 @Override printBand(byte[] data, int x, int y, int width, int height)501 protected void printBand(byte[] data, int x, int y, int width, int height) throws PrinterException { 502 // NOTE: This is not used in the CPrinterJob code path. 503 throw new PrinterException(sShouldNotReachHere); 504 } 505 506 /** 507 * Called by the print() method at the start of 508 * a print job. 509 */ 510 @Override startDoc()511 protected void startDoc() throws PrinterException { 512 // NOTE: This is not used in the CPrinterJob code path. 513 throw new PrinterException(sShouldNotReachHere); 514 } 515 516 /** 517 * Called by the print() method at the end of 518 * a print job. 519 */ 520 @Override endDoc()521 protected void endDoc() throws PrinterException { 522 // NOTE: This is not used in the CPrinterJob code path. 523 throw new PrinterException(sShouldNotReachHere); 524 } 525 526 /* Called by cancelDoc */ 527 @Override abortDoc()528 protected native void abortDoc(); 529 530 /** 531 * Displays the page setup dialog placing the user's 532 * settings into 'page'. 533 */ pageSetup(PageFormat page, Printable painter)534 public boolean pageSetup(PageFormat page, Printable painter) { 535 CPrinterDialog printerDialog = new CPrinterPageDialog(null, this, page, painter); 536 printerDialog.setVisible(true); 537 boolean result = printerDialog.getRetVal(); 538 printerDialog.dispose(); 539 return result; 540 } 541 542 /** 543 * Displays the print dialog and records the user's settings 544 * into this object. Return false if the user cancels the 545 * dialog. 546 * If the dialog is to use a set of attributes, useAttributes is true. 547 */ jobSetup(Pageable doc, boolean allowPrintToFile)548 private boolean jobSetup(Pageable doc, boolean allowPrintToFile) { 549 CPrinterDialog printerDialog = new CPrinterJobDialog(null, this, doc, allowPrintToFile); 550 printerDialog.setVisible(true); 551 boolean result = printerDialog.getRetVal(); 552 printerDialog.dispose(); 553 return result; 554 } 555 556 /** 557 * Alters the orientation and Paper to match defaults obtained 558 * from a printer. 559 */ getDefaultPage(PageFormat page)560 private native void getDefaultPage(PageFormat page); 561 562 /** 563 * validate the paper size against the current printer. 564 */ 565 @Override validatePaper(Paper origPaper, Paper newPaper )566 protected native void validatePaper(Paper origPaper, Paper newPaper ); 567 568 // The following methods are CPrinterJob specific. 569 570 @Override 571 @SuppressWarnings("deprecation") finalize()572 protected void finalize() { 573 synchronized (fNSPrintInfoLock) { 574 if (fNSPrintInfo != -1) { 575 dispose(fNSPrintInfo); 576 } 577 fNSPrintInfo = -1; 578 } 579 } 580 createNSPrintInfo()581 private native long createNSPrintInfo(); dispose(long printInfo)582 private native void dispose(long printInfo); 583 getNSPrintInfo()584 private long getNSPrintInfo() { 585 // This is called from the native side. 586 synchronized (fNSPrintInfoLock) { 587 if (fNSPrintInfo == -1) { 588 fNSPrintInfo = createNSPrintInfo(); 589 } 590 return fNSPrintInfo; 591 } 592 } 593 printLoop(boolean waitUntilDone, int firstPage, int lastPage)594 private native boolean printLoop(boolean waitUntilDone, int firstPage, int lastPage) throws PrinterException; 595 getPageFormat(int pageIndex)596 private PageFormat getPageFormat(int pageIndex) { 597 // This is called from the native side. 598 PageFormat page; 599 try { 600 page = getPageable().getPageFormat(pageIndex); 601 } catch (Exception e) { 602 return null; 603 } 604 return page; 605 } 606 getPrintable(int pageIndex)607 private Printable getPrintable(int pageIndex) { 608 // This is called from the native side. 609 Printable painter; 610 try { 611 painter = getPageable().getPrintable(pageIndex); 612 } catch (Exception e) { 613 return null; 614 } 615 return painter; 616 } 617 getPrinterName()618 private String getPrinterName(){ 619 // This is called from the native side. 620 PrintService service = getPrintService(); 621 if (service == null) return null; 622 return service.getName(); 623 } 624 getPrinterTray()625 private String getPrinterTray() { 626 return tray; 627 } 628 setPrinterServiceFromNative(String printerName)629 private void setPrinterServiceFromNative(String printerName) { 630 // This is called from the native side. 631 PrintService[] services = PrintServiceLookup.lookupPrintServices(DocFlavor.SERVICE_FORMATTED.PAGEABLE, null); 632 633 for (int i = 0; i < services.length; i++) { 634 PrintService service = services[i]; 635 636 if (printerName.equals(service.getName())) { 637 try { 638 setPrintService(service); 639 } catch (PrinterException e) { 640 // ignored 641 } 642 return; 643 } 644 } 645 } 646 getPageFormatArea(PageFormat page)647 private Rectangle2D getPageFormatArea(PageFormat page) { 648 Rectangle2D.Double pageFormatArea = 649 new Rectangle2D.Double(page.getImageableX(), 650 page.getImageableY(), 651 page.getImageableWidth(), 652 page.getImageableHeight()); 653 return pageFormatArea; 654 } 655 cancelCheck()656 private boolean cancelCheck() { 657 // This is called from the native side. 658 659 // This is used to avoid deadlock 660 // We would like to just call if isCancelled(), 661 // but that will block the AppKit thread against whomever is holding the synchronized lock 662 boolean cancelled = (performingPrinting && userCancelled); 663 if (cancelled) { 664 try { 665 LWCToolkit.invokeLater(new Runnable() { public void run() { 666 try { 667 cancelDoc(); 668 } catch (PrinterAbortException pae) { 669 // no-op, let the native side handle it 670 } 671 }}, null); 672 } catch (java.lang.reflect.InvocationTargetException ite) {} 673 } 674 return cancelled; 675 } 676 createFirstPassGraphics(PrinterJob printerJob, PageFormat page)677 private PeekGraphics createFirstPassGraphics(PrinterJob printerJob, PageFormat page) { 678 // This is called from the native side. 679 BufferedImage bimg = new BufferedImage((int)Math.round(page.getWidth()), (int)Math.round(page.getHeight()), BufferedImage.TYPE_INT_ARGB_PRE); 680 PeekGraphics peekGraphics = createPeekGraphics(bimg.createGraphics(), printerJob); 681 Rectangle2D pageFormatArea = getPageFormatArea(page); 682 initPrinterGraphics(peekGraphics, pageFormatArea); 683 return peekGraphics; 684 } 685 printToPathGraphics( final PeekGraphics graphics, final PrinterJob printerJob, final Printable painter, final PageFormat page, final int pageIndex, final long context)686 private void printToPathGraphics( final PeekGraphics graphics, // Always an actual PeekGraphics 687 final PrinterJob printerJob, // Always an actual CPrinterJob 688 final Printable painter, // Client class 689 final PageFormat page, // Client class 690 final int pageIndex, 691 final long context) throws PrinterException { 692 // This is called from the native side. 693 Runnable r = new Runnable() { public void run() { 694 try { 695 SurfaceData sd = CPrinterSurfaceData.createData(page, context); // Just stores page into an ivar 696 if (defaultFont == null) { 697 defaultFont = new Font("Dialog", Font.PLAIN, 12); 698 } 699 Graphics2D delegate = new SunGraphics2D(sd, Color.black, Color.white, defaultFont); 700 701 Graphics2D pathGraphics = new CPrinterGraphics(delegate, printerJob); // Just stores delegate into an ivar 702 Rectangle2D pageFormatArea = getPageFormatArea(page); 703 initPrinterGraphics(pathGraphics, pageFormatArea); 704 painter.print(pathGraphics, page, pageIndex); 705 delegate.dispose(); 706 delegate = null; 707 } catch (PrinterException pe) { throw new java.lang.reflect.UndeclaredThrowableException(pe); } 708 }}; 709 710 if (onEventThread) { 711 try { EventQueue.invokeAndWait(r); 712 } catch (java.lang.reflect.InvocationTargetException ite) { 713 Throwable te = ite.getTargetException(); 714 if (te instanceof PrinterException) throw (PrinterException)te; 715 else te.printStackTrace(); 716 } catch (Exception e) { e.printStackTrace(); } 717 } else { 718 r.run(); 719 } 720 721 } 722 723 // Returns either 1. an array of 3 object (PageFormat, Printable, PeekGraphics) or 2. null getPageformatPrintablePeekgraphics(final int pageIndex)724 private Object[] getPageformatPrintablePeekgraphics(final int pageIndex) { 725 final Object[] ret = new Object[3]; 726 final PrinterJob printerJob = this; 727 728 Runnable r = new Runnable() { public void run() { synchronized(ret) { 729 try { 730 Pageable pageable = getPageable(); 731 PageFormat pageFormat = pageable.getPageFormat(pageIndex); 732 if (pageFormat != null) { 733 Printable printable = pageable.getPrintable(pageIndex); 734 if (printable != null) { 735 BufferedImage bimg = 736 new BufferedImage( 737 (int)Math.round(pageFormat.getWidth()), 738 (int)Math.round(pageFormat.getHeight()), 739 BufferedImage.TYPE_INT_ARGB_PRE); 740 PeekGraphics peekGraphics = 741 createPeekGraphics(bimg.createGraphics(), printerJob); 742 Rectangle2D pageFormatArea = 743 getPageFormatArea(pageFormat); 744 initPrinterGraphics(peekGraphics, pageFormatArea); 745 746 // Do the assignment here! 747 ret[0] = pageFormat; 748 ret[1] = printable; 749 ret[2] = peekGraphics; 750 } 751 } 752 } catch (Exception e) {} // Original code bailed on any exception 753 }}}; 754 755 if (onEventThread) { 756 try { EventQueue.invokeAndWait(r); } catch (Exception e) { e.printStackTrace(); } 757 } else { 758 r.run(); 759 } 760 761 synchronized(ret) { 762 if (ret[2] != null) 763 return ret; 764 return null; 765 } 766 } 767 printAndGetPageFormatArea(final Printable printable, final Graphics graphics, final PageFormat pageFormat, final int pageIndex)768 private Rectangle2D printAndGetPageFormatArea(final Printable printable, final Graphics graphics, final PageFormat pageFormat, final int pageIndex) { 769 final Rectangle2D[] ret = new Rectangle2D[1]; 770 771 Runnable r = new Runnable() { public void run() { synchronized(ret) { 772 try { 773 int pageResult = printable.print(graphics, pageFormat, pageIndex); 774 if (pageResult != Printable.NO_SUCH_PAGE) { 775 ret[0] = getPageFormatArea(pageFormat); 776 } 777 } catch (Exception e) {} // Original code bailed on any exception 778 }}}; 779 780 if (onEventThread) { 781 try { EventQueue.invokeAndWait(r); } catch (Exception e) { e.printStackTrace(); } 782 } else { 783 r.run(); 784 } 785 786 synchronized(ret) { return ret[0]; } 787 } 788 789 // upcall from native detachPrintLoop(final long target, final long arg)790 private static void detachPrintLoop(final long target, final long arg) { 791 new Thread(null, () -> _safePrintLoop(target, arg), 792 "PrintLoop", 0, false).start(); 793 } _safePrintLoop(long target, long arg)794 private static native void _safePrintLoop(long target, long arg); 795 796 @Override startPage(PageFormat arg0, Printable arg1, int arg2, boolean arg3)797 protected void startPage(PageFormat arg0, Printable arg1, int arg2, boolean arg3) throws PrinterException { 798 // TODO Auto-generated method stub 799 } 800 801 @Override getMediaSize(Media media, PrintService service, PageFormat page)802 protected MediaSize getMediaSize(Media media, PrintService service, 803 PageFormat page) { 804 if (media == null || !(media instanceof MediaSizeName)) { 805 return getDefaultMediaSize(page); 806 } 807 MediaSize size = MediaSize.getMediaSizeForName((MediaSizeName) media); 808 return size != null ? size : getDefaultMediaSize(page); 809 } 810 getDefaultMediaSize(PageFormat page)811 private MediaSize getDefaultMediaSize(PageFormat page){ 812 final int inch = 72; 813 Paper paper = page.getPaper(); 814 float width = (float) (paper.getWidth() / inch); 815 float height = (float) (paper.getHeight() / inch); 816 return new MediaSize(width, height, MediaSize.INCH); 817 } 818 819 @Override getDefaultPrintableArea(PageFormat page, double w, double h)820 protected MediaPrintableArea getDefaultPrintableArea(PageFormat page, double w, double h) { 821 final float dpi = 72.0f; 822 Paper paper = page.getPaper(); 823 return new MediaPrintableArea( 824 (float) (paper.getImageableX() / dpi), 825 (float) (paper.getImageableY() / dpi), 826 (float) (paper.getImageableWidth() / dpi), 827 (float) (paper.getImageableHeight() / dpi), 828 MediaPrintableArea.INCH); 829 } 830 } 831