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