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