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