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