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