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