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