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