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