1 /*
2  * Copyright (c) 1997, 2021, 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.awt.windows;
27 
28 import java.awt.Color;
29 import java.awt.Font;
30 import java.awt.Graphics2D;
31 import java.awt.GraphicsEnvironment;
32 import java.awt.HeadlessException;
33 import java.awt.Toolkit;
34 import java.awt.BasicStroke;
35 import java.awt.Button;
36 import java.awt.Component;
37 import java.awt.Dimension;
38 import java.awt.event.ActionEvent;
39 import java.awt.event.ActionListener;
40 import java.awt.FileDialog;
41 import java.awt.Dialog;
42 import java.awt.Label;
43 import java.awt.Panel;
44 import java.awt.Rectangle;
45 import java.awt.Window;
46 
47 import java.awt.image.BufferedImage;
48 import java.awt.image.IndexColorModel;
49 
50 import java.awt.print.Pageable;
51 import java.awt.print.PageFormat;
52 import java.awt.print.Paper;
53 import java.awt.print.Printable;
54 import java.awt.print.PrinterJob;
55 import java.awt.print.PrinterException;
56 import javax.print.PrintService;
57 
58 import java.io.File;
59 
60 import java.util.MissingResourceException;
61 import java.util.ResourceBundle;
62 
63 import sun.awt.AWTAccessor;
64 import sun.awt.AWTAccessor.ComponentAccessor;
65 import sun.print.PeekGraphics;
66 import sun.print.PeekMetrics;
67 
68 import java.net.URI;
69 import java.net.URISyntaxException;
70 
71 import javax.print.PrintServiceLookup;
72 import javax.print.attribute.PrintRequestAttributeSet;
73 import javax.print.attribute.HashPrintRequestAttributeSet;
74 import javax.print.attribute.Attribute;
75 import javax.print.attribute.standard.Sides;
76 import javax.print.attribute.standard.Chromaticity;
77 import javax.print.attribute.standard.PrintQuality;
78 import javax.print.attribute.standard.PrinterResolution;
79 import javax.print.attribute.standard.SheetCollate;
80 import javax.print.attribute.standard.Copies;
81 import javax.print.attribute.standard.Destination;
82 import javax.print.attribute.standard.DialogOwner;
83 import javax.print.attribute.standard.OrientationRequested;
84 import javax.print.attribute.standard.Media;
85 import javax.print.attribute.standard.MediaSizeName;
86 import javax.print.attribute.standard.MediaSize;
87 import javax.print.attribute.standard.MediaTray;
88 import javax.print.attribute.standard.PageRanges;
89 
90 import sun.awt.Win32FontManager;
91 
92 import sun.print.RasterPrinterJob;
93 import sun.print.SunAlternateMedia;
94 import sun.print.SunPageSelection;
95 import sun.print.Win32MediaTray;
96 import sun.print.Win32PrintService;
97 import sun.print.PrintServiceLookupProvider;
98 import sun.print.ServiceDialog;
99 
100 import java.awt.Frame;
101 import java.io.FilePermission;
102 
103 import sun.java2d.Disposer;
104 import sun.java2d.DisposerRecord;
105 import sun.java2d.DisposerTarget;
106 
107 /**
108  * A class which initiates and executes a Win32 printer job.
109  *
110  * @author Richard Blanchard
111  */
112 public final class WPrinterJob extends RasterPrinterJob
113         implements DisposerTarget {
114 
115  /* Class Constants */
116 
117 
118 /* Instance Variables */
119 
120     /**
121      * These are Windows' ExtCreatePen End Cap Styles
122      * and must match the values in <WINGDI.h>
123      */
124     protected static final long PS_ENDCAP_ROUND  = 0x00000000;
125     protected static final long PS_ENDCAP_SQUARE   = 0x00000100;
126     protected static final long PS_ENDCAP_FLAT   =   0x00000200;
127 
128     /**
129      * These are Windows' ExtCreatePen Line Join Styles
130      * and must match the values in <WINGDI.h>
131      */
132     protected static final long PS_JOIN_ROUND   =    0x00000000;
133     protected static final long PS_JOIN_BEVEL   =    0x00001000;
134     protected static final long PS_JOIN_MITER   =    0x00002000;
135 
136     /**
137      * This is the Window's Polygon fill rule which
138      * Selects alternate mode (fills the area between odd-numbered
139      * and even-numbered polygon sides on each scan line).
140      * It must match the value in <WINGDI.h> It can be passed
141      * to setPolyFillMode().
142      */
143     protected static final int POLYFILL_ALTERNATE = 1;
144 
145     /**
146      * This is the Window's Polygon fill rule which
147      * Selects winding mode which fills any region
148      * with a nonzero winding value). It must match
149      * the value in <WINGDI.h> It can be passed
150      * to setPolyFillMode().
151      */
152     protected static final int POLYFILL_WINDING = 2;
153 
154     /**
155      * The maximum value for a Window's color component
156      * as passed to selectSolidBrush.
157      */
158     private static final int MAX_WCOLOR = 255;
159 
160     /**
161      * Flags for setting values from devmode in native code.
162      * Values must match those defined in awt_PrintControl.cpp
163      */
164     private static final int SET_DUP_VERTICAL = 0x00000010;
165     private static final int SET_DUP_HORIZONTAL = 0x00000020;
166     private static final int SET_RES_HIGH = 0x00000040;
167     private static final int SET_RES_LOW = 0x00000080;
168     private static final int SET_COLOR = 0x00000200;
169     private static final int SET_ORIENTATION = 0x00004000;
170     private static final int SET_COLLATED    = 0x00008000;
171 
172     /**
173      * Values must match those defined in wingdi.h & commdlg.h
174      */
175     private static final int PD_COLLATE = 0x00000010;
176     private static final int PD_PRINTTOFILE = 0x00000020;
177     private static final int DM_ORIENTATION   = 0x00000001;
178     private static final int DM_PAPERSIZE     = 0x00000002;
179     private static final int DM_COPIES        = 0x00000100;
180     private static final int DM_DEFAULTSOURCE = 0x00000200;
181     private static final int DM_PRINTQUALITY  = 0x00000400;
182     private static final int DM_COLOR         = 0x00000800;
183     private static final int DM_DUPLEX        = 0x00001000;
184     private static final int DM_YRESOLUTION   = 0x00002000;
185     private static final int DM_COLLATE       = 0x00008000;
186 
187     private static final short DMCOLLATE_FALSE  = 0;
188     private static final short DMCOLLATE_TRUE   = 1;
189 
190     private static final short DMORIENT_PORTRAIT  = 1;
191     private static final short DMORIENT_LANDSCAPE = 2;
192 
193     private static final short DMCOLOR_MONOCHROME = 1;
194     private static final short DMCOLOR_COLOR      = 2;
195 
196     private static final short DMRES_DRAFT  = -1;
197     private static final short DMRES_LOW    = -2;
198     private static final short DMRES_MEDIUM = -3;
199     private static final short DMRES_HIGH   = -4;
200 
201     private static final short DMDUP_SIMPLEX    = 1;
202     private static final short DMDUP_VERTICAL   = 2;
203     private static final short DMDUP_HORIZONTAL = 3;
204 
205     /**
206      * Pageable MAX pages
207      */
208     private static final int MAX_UNKNOWN_PAGES = 9999;
209 
210 
211     /* Collation and copy flags.
212      * The Windows PRINTDLG struct has a nCopies field which on return
213      * indicates how many copies of a print job an application must render.
214      * There is also a PD_COLLATE member of the flags field which if
215      * set on return indicates the application generated copies should be
216      * collated.
217      * Windows printer drivers typically - but not always - support
218      * generating multiple copies themselves, but uncollated is more
219      * universal than collated copies.
220      * When they do, they read the initial values from the PRINTDLG structure
221      * and set them into the driver's DEVMODE structure and intialise
222      * the printer DC based on that, so that when printed those settings
223      * will be used.
224      * For drivers supporting both these capabilities via DEVMODE, then on
225      * return from the Print Dialog, nCopies is set to 1 and the PD_COLLATE is
226      * cleared, so that the application will only render 1 copy and the
227      * driver takes care of the rest.
228      *
229      * Applications which want to know what's going on have to be DEVMODE
230      * savvy and peek at that.
231      * DM_COPIES flag indicates support for multiple driver copies
232      * and dmCopies is the number of copies the driver will print
233      * DM_COLLATE flag indicates support for collated driver copies and
234      * dmCollate == DMCOLLATE_TRUE indicates the option is in effect.
235      *
236      * Multiple copies from Java applications:
237      * We provide API to get & set the number of copies as well as allowing the
238      * user to choose it, so we need to be savvy about DEVMODE, so that
239      * we can accurately report back the number of copies selected by
240      * the user, as well as make use of the driver to render multiple copies.
241      *
242      * Collation and Java applications:
243      * We presently provide no API for specifying collation, but its
244      * present on the Windows Print Dialog, and when a user checks it
245      * they expect it to be obeyed.
246      * The best thing to do is to detect exactly the cases where the
247      * driver doesn't support this and render multiple copies ourselves.
248      * To support all this we need several flags which signal the
249      * printer's capabilities and the user's requests.
250      * Its questionable if we (yet) need to make a distinction between
251      * the user requesting collation and the driver supporting it.
252      * Since for now we only need to know whether we need to render the
253      * copies. However it allows the logic to be clearer.
254      * These fields are changed by native code which detects the driver's
255      * capabilities and the user's choices.
256      */
257 
258     //initialize to false because the Flags that we initialized in PRINTDLG
259     // tells GDI that we can handle our own collation and multiple copies
260      private boolean driverDoesMultipleCopies = false;
261      private boolean driverDoesCollation = false;
262      private boolean userRequestedCollation = false;
263      private boolean noDefaultPrinter = false;
264 
265     /* The HandleRecord holds the native resources that need to be freed
266      * when this WPrinterJob is GC'd.
267      */
268     static class HandleRecord implements DisposerRecord {
269         /**
270          * The Windows device context we will print into.
271          * This variable is set after the Print dialog
272          * is okayed by the user. If the user cancels
273          * the print dialog, then this variable is 0.
274          * Much of the configuration information for a printer is
275          * obtained through printer device specific handles.
276          * We need to associate these with, and free with, the mPrintDC.
277          */
278         private long mPrintDC;
279         private long mPrintHDevMode;
280         private long mPrintHDevNames;
281 
282         @Override
dispose()283         public void dispose() {
284             WPrinterJob.deleteDC(mPrintDC, mPrintHDevMode, mPrintHDevNames);
285         }
286     }
287 
288     private HandleRecord handleRecord = new HandleRecord();
289 
290     private int mPrintPaperSize;
291 
292     /* These fields are directly set in upcalls from the values
293      * obtained from calling DeviceCapabilities()
294      */
295     private int mPrintXRes;   // pixels per inch in x direction
296 
297     private int mPrintYRes;   // pixels per inch in y direction
298 
299     private int mPrintPhysX;  // x offset in pixels of printable area
300 
301     private int mPrintPhysY;  // y offset in pixels of printable area
302 
303     private int mPrintWidth;  // width in pixels of printable area
304 
305     private int mPrintHeight; // height in pixels of printable area
306 
307     private int mPageWidth;   // width in pixels of entire page
308 
309     private int mPageHeight;  // height in pixels of entire page
310 
311     /* The values of the following variables are pulled directly
312      * into native code (even bypassing getter methods) when starting a doc.
313      * So these need to be synced up from any resulting native changes
314      * by a user dialog.
315      * But the native changes call up to into the attributeset, and that
316      * should be sufficient, since before heading down to native either
317      * to (re-)display a dialog, or to print the doc, these are all
318      * re-populated from the AttributeSet,
319      * Nonetheless having them in sync with the attributeset and native
320      * state is probably safer.
321      * Also whereas the startDoc native code pulls the variables directly,
322      * the dialog code does use getter to pull some of these values.
323      * That's an inconsistency we should fix if it causes problems.
324      */
325     private int mAttSides;
326     private int mAttChromaticity;
327     private int mAttXRes;
328     private int mAttYRes;
329     private int mAttQuality;
330     private int mAttCollate;
331     private int mAttCopies;
332     private int mAttMediaSizeName;
333     private int mAttMediaTray;
334 
335     private String mDestination = null;
336 
337     /**
338      * The last color set into the print device context or
339      * {@code null} if no color has been set.
340      */
341     private Color mLastColor;
342 
343     /**
344      * The last text color set into the print device context or
345      * {@code null} if no color has been set.
346      */
347     private Color mLastTextColor;
348 
349     /**
350      * Define the most recent java font set as a GDI font in the printer
351      * device context. mLastFontFamily will be NULL if no
352      * GDI font has been set.
353      */
354     private String mLastFontFamily;
355     private float mLastFontSize;
356     private int mLastFontStyle;
357     private int mLastRotation;
358     private float mLastAwScale;
359 
360     // for AwtPrintControl::InitPrintDialog
361     private PrinterJob pjob;
362 
363     private java.awt.peer.ComponentPeer dialogOwnerPeer = null;
364 
365     private int graphicsMode;
366     private double[] worldTransform = new double[6];
367 
368  /* Static Initializations */
369 
370     static {
371         // AWT has to be initialized for the native code to function correctly.
Toolkit.getDefaultToolkit()372         Toolkit.getDefaultToolkit();
373 
initIDs()374         initIDs();
375 
Win32FontManager.registerJREFontsForPrinting()376         Win32FontManager.registerJREFontsForPrinting();
377     }
378 
379     /* Constructors */
380 
WPrinterJob()381     public WPrinterJob()
382     {
383         Disposer.addRecord(disposerReferent,
384                            handleRecord = new HandleRecord());
385         initAttributeMembers();
386     }
387 
388     /* Implement DisposerTarget. Weak references to an Object can delay
389      * its storage reclaimation marginally.
390      * It won't make the native resources be release any more quickly, but
391      * by pointing the reference held by Disposer at an object which becomes
392      * no longer strongly reachable when this WPrinterJob is no longer
393      * strongly reachable, we allow the WPrinterJob to be freed more promptly
394      * than if it were the referenced object.
395      */
396     private Object disposerReferent = new Object();
397 
398     @Override
getDisposerReferent()399     public Object getDisposerReferent() {
400         return disposerReferent;
401     }
402 
403 /* Instance Methods */
404 
405     /**
406      * Display a dialog to the user allowing the modification of a
407      * PageFormat instance.
408      * The {@code page} argument is used to initialize controls
409      * in the page setup dialog.
410      * If the user cancels the dialog, then the method returns the
411      * original {@code page} object unmodified.
412      * If the user okays the dialog then the method returns a new
413      * PageFormat object with the indicated changes.
414      * In either case the original {@code page} object will
415      * not be modified.
416      * @param     page    the default PageFormat presented to the user
417      *                    for modification
418      * @return    the original {@code page} object if the dialog
419      *            is cancelled, or a new PageFormat object containing
420      *            the format indicated by the user if the dialog is
421      *            acknowledged
422      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
423      * returns true.
424      * @see java.awt.GraphicsEnvironment#isHeadless
425      * @since     1.2
426      */
427     @Override
pageDialog(PageFormat page)428     public PageFormat pageDialog(PageFormat page) throws HeadlessException {
429         if (GraphicsEnvironment.isHeadless()) {
430             throw new HeadlessException();
431         }
432 
433         if (!(getPrintService() instanceof Win32PrintService)) {
434             return super.pageDialog(page);
435         }
436 
437         PageFormat pageClone = (PageFormat) page.clone();
438         boolean result = false;
439 
440         /*
441          * Fix for 4507585: show the native modal dialog the same way printDialog() does so
442          * that it won't block event dispatching when called on EventDispatchThread.
443          */
444         WPageDialog dialog = new WPageDialog((Frame)null, this,
445                                      pageClone, null);
446         dialog.setRetVal(false);
447         dialog.setVisible(true);
448         result = dialog.getRetVal();
449         dialog.dispose();
450 
451         // myService => current PrintService
452         if (result && (myService != null)) {
453             // It's possible that current printer is changed through
454             // the "Printer..." button so we query again from native.
455             String printerName = getNativePrintService();
456             if (!myService.getName().equals(printerName)) {
457                 // native printer is different !
458                 // we update the current PrintService
459                 try {
460                     setPrintService(PrintServiceLookupProvider.
461                                     getWin32PrintLUS().
462                                     getPrintServiceByName(printerName));
463                 } catch (PrinterException e) {
464                 }
465             }
466             // Update attributes, this will preserve the page settings.
467             //  - same code as in RasterPrinterJob.java
468             updatePageAttributes(myService, pageClone);
469 
470             return pageClone;
471         } else {
472             return page;
473         }
474     }
475 
476 
displayNativeDialog()477     private boolean displayNativeDialog() {
478         // "attributes" is required for getting the updated attributes
479         if (attributes == null) {
480             return false;
481         }
482 
483         DialogOwner dlgOwner = (DialogOwner)attributes.get(DialogOwner.class);
484         Window owner = (dlgOwner != null) ? dlgOwner.getOwner() : null;
485 
486         WPrintDialog dialog =  (owner instanceof Frame) ?
487                 new WPrintDialog((Frame)owner, this) :
488                 new WPrintDialog((Dialog)owner, this);
489 
490         dialog.setRetVal(false);
491         dialog.setVisible(true);
492         boolean prv = dialog.getRetVal();
493         dialog.dispose();
494 
495         Destination dest =
496                 (Destination)attributes.get(Destination.class);
497         if ((dest == null) || !prv){
498                 return prv;
499         } else {
500             String title = null;
501             String strBundle = "sun.print.resources.serviceui";
502             ResourceBundle rb = ResourceBundle.getBundle(strBundle);
503             try {
504                 title = rb.getString("dialog.printtofile");
505             } catch (MissingResourceException e) {
506             }
507             FileDialog fileDialog = (owner instanceof Frame) ?
508                     new FileDialog((Frame)owner, title, FileDialog.SAVE) :
509                     new FileDialog((Dialog)owner, title, FileDialog.SAVE);
510 
511             URI destURI = dest.getURI();
512             // Old code destURI.getPath() would return null for "file:out.prn"
513             // so we use getSchemeSpecificPart instead.
514             String pathName = (destURI != null) ?
515                 destURI.getSchemeSpecificPart() : null;
516             if (pathName != null) {
517                File file = new File(pathName);
518                fileDialog.setFile(file.getName());
519                File parent = file.getParentFile();
520                if (parent != null) {
521                    fileDialog.setDirectory(parent.getPath());
522                }
523             } else {
524                 fileDialog.setFile("out.prn");
525             }
526 
527             fileDialog.setVisible(true);
528             String fileName = fileDialog.getFile();
529             if (fileName == null) {
530                 fileDialog.dispose();
531                 return false;
532             }
533             String fullName = fileDialog.getDirectory() + fileName;
534             File f = new File(fullName);
535             File pFile = f.getParentFile();
536             while ((f.exists() &&
537                       (!f.isFile() || !f.canWrite())) ||
538                    ((pFile != null) &&
539                       (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {
540 
541                 if (owner instanceof Frame) {
542                     (new PrintToFileErrorDialog((Frame)owner,
543                                 ServiceDialog.getMsg("dialog.owtitle"),
544                                 ServiceDialog.getMsg("dialog.writeerror")+" "+fullName,
545                                 ServiceDialog.getMsg("button.ok"))).setVisible(true);
546                 } else {
547                     (new PrintToFileErrorDialog((Dialog)owner,
548                                 ServiceDialog.getMsg("dialog.owtitle"),
549                                 ServiceDialog.getMsg("dialog.writeerror")+" "+fullName,
550                                 ServiceDialog.getMsg("button.ok"))).setVisible(true);
551                 }
552 
553                 fileDialog.setVisible(true);
554                 fileName = fileDialog.getFile();
555                 if (fileName == null) {
556                     fileDialog.dispose();
557                     return false;
558                 }
559                 fullName = fileDialog.getDirectory() + fileName;
560                 f = new File(fullName);
561                 pFile = f.getParentFile();
562             }
563             fileDialog.dispose();
564             attributes.add(new Destination(f.toURI()));
565             return true;
566         }
567 
568     }
569 
570     /**
571      * Presents the user a dialog for changing properties of the
572      * print job interactively.
573      * @return false if the user cancels the dialog and
574      *         true otherwise.
575      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
576      * returns true.
577      * @see java.awt.GraphicsEnvironment#isHeadless
578      */
579     @Override
printDialog()580     public boolean printDialog() throws HeadlessException {
581 
582         if (GraphicsEnvironment.isHeadless()) {
583             throw new HeadlessException();
584         }
585         // current request attribute set should be reflected to the print dialog.
586         // If null, create new instance of HashPrintRequestAttributeSet.
587         if (attributes == null) {
588             attributes = new HashPrintRequestAttributeSet();
589         }
590 
591         if (!(getPrintService() instanceof Win32PrintService)) {
592             return super.printDialog(attributes);
593         }
594 
595         if (noDefaultPrinter == true) {
596             return false;
597         } else {
598             return displayNativeDialog();
599         }
600     }
601 
602      /**
603      * Associate this PrinterJob with a new PrintService.
604      *
605      * Throws {@code PrinterException} if the specified service
606      * cannot support the {@code Pageable} and
607      * </code>Printable</code> interfaces necessary to support 2D printing.
608      * @param service print service which supports 2D printing.
609      *
610      * @throws PrinterException if the specified service does not support
611      * 2D printing.
612      */
613     @Override
setPrintService(PrintService service)614     public void setPrintService(PrintService service)
615         throws PrinterException {
616         super.setPrintService(service);
617         if (!(service instanceof Win32PrintService)) {
618             return;
619         }
620         driverDoesMultipleCopies = false;
621         driverDoesCollation = false;
622         setNativePrintServiceIfNeeded(service.getName());
623     }
624 
625     /* associates this job with the specified native service */
setNativePrintService(String name)626     private native void setNativePrintService(String name)
627         throws PrinterException;
628 
629     private String lastNativeService = null;
setNativePrintServiceIfNeeded(String name)630     private void setNativePrintServiceIfNeeded(String name)
631         throws PrinterException {
632 
633         if (name != null && !(name.equals(lastNativeService))) {
634             setNativePrintService(name);
635             lastNativeService = name;
636         }
637     }
638 
639     @Override
getPrintService()640     public PrintService getPrintService() {
641         if (myService == null) {
642             String printerName = getNativePrintService();
643 
644             if (printerName != null) {
645                 myService = PrintServiceLookupProvider.getWin32PrintLUS().
646                     getPrintServiceByName(printerName);
647                 // no need to call setNativePrintService as this name is
648                 // currently set in native
649                 if (myService != null) {
650                     return myService;
651                 }
652             }
653 
654             myService = PrintServiceLookup.lookupDefaultPrintService();
655             if (myService instanceof Win32PrintService) {
656                 try {
657                     setNativePrintServiceIfNeeded(myService.getName());
658                 } catch (Exception e) {
659                     myService = null;
660                 }
661             }
662 
663           }
664           return myService;
665     }
666 
getNativePrintService()667     private native String getNativePrintService();
668 
initAttributeMembers()669     private void initAttributeMembers() {
670             mAttSides = 0;
671             mAttChromaticity = 0;
672             mAttXRes = 0;
673             mAttYRes = 0;
674             mAttQuality = 0;
675             mAttCollate = -1;
676             mAttCopies = 0;
677             mAttMediaTray = 0;
678             mAttMediaSizeName = 0;
679             mDestination = null;
680 
681     }
682 
683     /**
684      * copy the attributes to the native print job
685      * Note that this method, and hence the re-initialisation
686      * of the GDI values is done on each entry to the print dialog since
687      * an app could redisplay the print dialog for the same job and
688      * 1) the application may have changed attribute settings
689      * 2) the application may have changed the printer.
690      * In the event that the user changes the printer using the
691       dialog, then it is up to GDI to report back all changed values.
692      */
693     @Override
setAttributes(PrintRequestAttributeSet attributes)694     protected void setAttributes(PrintRequestAttributeSet attributes)
695         throws PrinterException {
696 
697         // initialize attribute values
698         initAttributeMembers();
699         super.setAttributes(attributes);
700 
701         mAttCopies = getCopiesInt();
702         mDestination = destinationAttr;
703 
704         if (attributes == null) {
705             return; // now always use attributes, so this shouldn't happen.
706         }
707         Attribute[] attrs = attributes.toArray();
708         for (int i=0; i< attrs.length; i++) {
709             Attribute attr = attrs[i];
710             try {
711                  if (attr.getCategory() == Sides.class) {
712                     setSidesAttrib(attr);
713                 }
714                 else if (attr.getCategory() == Chromaticity.class) {
715                     setColorAttrib(attr);
716                 }
717                 else if (attr.getCategory() == PrinterResolution.class) {
718                     if (myService.isAttributeValueSupported(attr, null, null)) {
719                         setResolutionAttrib(attr);
720                     }
721                 }
722                 else if (attr.getCategory() == PrintQuality.class) {
723                     setQualityAttrib(attr);
724                 }
725                 else if (attr.getCategory() == SheetCollate.class) {
726                     setCollateAttrib(attr);
727                 }  else if (attr.getCategory() == Media.class ||
728                             attr.getCategory() == SunAlternateMedia.class) {
729                     /* SunAlternateMedia is used if its a tray, and
730                      * any Media that is specified is not a tray.
731                      */
732                     if (attr.getCategory() == SunAlternateMedia.class) {
733                         Media media = (Media)attributes.get(Media.class);
734                         if (media == null ||
735                             !(media instanceof MediaTray)) {
736                             attr = ((SunAlternateMedia)attr).getMedia();
737                         }
738                     }
739                     if (attr instanceof MediaSizeName) {
740                         setWin32MediaAttrib(attr);
741                     }
742                     if (attr instanceof MediaTray) {
743                         setMediaTrayAttrib(attr);
744                     }
745                 }
746 
747             } catch (ClassCastException e) {
748             }
749         }
750     }
751 
752     /**
753      * Alters the orientation and Paper to match defaults obtained
754      * from a printer.
755      */
getDefaultPage(PageFormat page)756     private native void getDefaultPage(PageFormat page);
757 
758     /**
759      * The passed in PageFormat will be copied and altered to describe
760      * the default page size and orientation of the PrinterJob's
761      * current printer.
762      * Note: PageFormat.getPaper() returns a clone and getDefaultPage()
763      * gets that clone so it won't overwrite the original paper.
764      */
765     @Override
defaultPage(PageFormat page)766     public PageFormat defaultPage(PageFormat page) {
767         PageFormat newPage = (PageFormat)page.clone();
768         getDefaultPage(newPage);
769         return newPage;
770     }
771 
772     /**
773      * validate the paper size against the current printer.
774      */
775     @Override
validatePaper(Paper origPaper, Paper newPaper )776     protected native void validatePaper(Paper origPaper, Paper newPaper );
777 
778     /**
779      * Examine the metrics captured by the
780      * {@code PeekGraphics} instance and
781      * if capable of directly converting this
782      * print job to the printer's control language
783      * or the native OS's graphics primitives, then
784      * return a {@code PathGraphics} to perform
785      * that conversion. If there is not an object
786      * capable of the conversion then return
787      * {@code null}. Returning {@code null}
788      * causes the print job to be rasterized.
789      */
790 
791     @Override
createPathGraphics(PeekGraphics peekGraphics, PrinterJob printerJob, Printable painter, PageFormat pageFormat, int pageIndex)792     protected Graphics2D createPathGraphics(PeekGraphics peekGraphics,
793                                             PrinterJob printerJob,
794                                             Printable painter,
795                                             PageFormat pageFormat,
796                                             int pageIndex) {
797 
798         WPathGraphics pathGraphics;
799         PeekMetrics metrics = peekGraphics.getMetrics();
800 
801         /* If the application has drawn anything that
802          * out PathGraphics class can not handle then
803          * return a null PathGraphics. If the property
804          * to force the raster pipeline has been set then
805          * we also want to avoid the path (pdl) pipeline
806          * and return null.
807          */
808        if (forcePDL == false && (forceRaster == true
809                                   || metrics.hasNonSolidColors()
810                                   || metrics.hasCompositing()
811                                   )) {
812             pathGraphics = null;
813         } else {
814             BufferedImage bufferedImage = new BufferedImage(8, 8,
815                                             BufferedImage.TYPE_INT_RGB);
816             Graphics2D bufferedGraphics = bufferedImage.createGraphics();
817 
818             boolean canRedraw = peekGraphics.getAWTDrawingOnly() == false;
819             pathGraphics =  new WPathGraphics(bufferedGraphics, printerJob,
820                                               painter, pageFormat, pageIndex,
821                                               canRedraw);
822         }
823 
824         return pathGraphics;
825     }
826 
827 
828     @Override
getXRes()829     protected double getXRes() {
830         if (mAttXRes != 0) {
831             return mAttXRes;
832         } else {
833             return mPrintXRes;
834         }
835     }
836 
837     @Override
getYRes()838     protected double getYRes() {
839         if (mAttYRes != 0) {
840             return mAttYRes;
841         } else {
842             return mPrintYRes;
843         }
844     }
845 
846     @Override
getPhysicalPrintableX(Paper p)847     protected double getPhysicalPrintableX(Paper p) {
848         return mPrintPhysX;
849     }
850 
851     @Override
getPhysicalPrintableY(Paper p)852     protected double getPhysicalPrintableY(Paper p) {
853         return mPrintPhysY;
854     }
855 
856     @Override
getPhysicalPrintableWidth(Paper p)857     protected double getPhysicalPrintableWidth(Paper p) {
858         return mPrintWidth;
859     }
860 
861     @Override
getPhysicalPrintableHeight(Paper p)862     protected double getPhysicalPrintableHeight(Paper p) {
863         return mPrintHeight;
864     }
865 
866     @Override
getPhysicalPageWidth(Paper p)867     protected double getPhysicalPageWidth(Paper p) {
868         return mPageWidth;
869     }
870 
871     @Override
getPhysicalPageHeight(Paper p)872     protected double getPhysicalPageHeight(Paper p) {
873         return mPageHeight;
874     }
875 
876     /**
877      * We don't (yet) provide API to support collation, and
878      * when we do the logic here will require adjustment, but
879      * this method is currently necessary to honour user-originated
880      * collation requests - which can only originate from the print dialog.
881      * REMIND: check if this can be deleted already.
882      */
883     @Override
isCollated()884     protected boolean isCollated() {
885         return userRequestedCollation;
886     }
887 
888     /**
889      * Returns how many times the entire book should
890      * be printed by the PrintJob. If the printer
891      * itself supports collation then this method
892      * should return 1 indicating that the entire
893      * book need only be printed once and the copies
894      * will be collated and made in the printer.
895      */
896     @Override
getCollatedCopies()897     protected int getCollatedCopies() {
898         debug_println("driverDoesMultipleCopies="+driverDoesMultipleCopies
899                       +" driverDoesCollation="+driverDoesCollation);
900         if  (super.isCollated() && !driverDoesCollation) {
901             // we will do our own collation so we need to
902             // tell the printer to not collate
903             mAttCollate = 0;
904             mAttCopies = 1;
905             return getCopies();
906         }
907 
908         return 1;
909     }
910 
911     /**
912      * Returns how many times each page in the book
913      * should be consecutively printed by PrinterJob.
914      * If the underlying Window's driver will
915      * generate the copies, rather than having RasterPrinterJob
916      * iterate over the number of copies, this method always returns
917      * 1.
918      */
919     @Override
getNoncollatedCopies()920     protected int getNoncollatedCopies() {
921         if (driverDoesMultipleCopies || super.isCollated()) {
922             return 1;
923         } else {
924             return getCopies();
925         }
926     }
927 
928     /* These getter/setters are called from native code */
929 
930     /**
931      * Return the Window's device context that we are printing
932      * into.
933      */
getPrintDC()934     private long getPrintDC() {
935         return handleRecord.mPrintDC;
936     }
937 
setPrintDC(long mPrintDC)938     private void setPrintDC(long mPrintDC) {
939         handleRecord.mPrintDC = mPrintDC;
940     }
941 
getDevMode()942     private long getDevMode() {
943         return handleRecord.mPrintHDevMode;
944     }
945 
setDevMode(long mPrintHDevMode)946     private void setDevMode(long mPrintHDevMode) {
947         handleRecord.mPrintHDevMode = mPrintHDevMode;
948     }
949 
getDevNames()950     private long getDevNames() {
951         return handleRecord.mPrintHDevNames;
952     }
953 
setDevNames(long mPrintHDevNames)954     private void setDevNames(long mPrintHDevNames) {
955         handleRecord.mPrintHDevNames = mPrintHDevNames;
956     }
957 
beginPath()958     protected void beginPath() {
959         beginPath(getPrintDC());
960     }
961 
endPath()962     protected void endPath() {
963         endPath(getPrintDC());
964     }
965 
scaleTransform(float scale)966     protected void scaleTransform(float scale) {
967         graphicsMode = setAdvancedGraphicsMode();
968         getWorldTransform(worldTransform);
969         scale(scale, scale);
970     }
971 
restoreTransform()972     protected void restoreTransform() {
973         setWorldTransform(worldTransform);
974         setGraphicsMode(graphicsMode);
975     }
976 
closeFigure()977     protected void closeFigure() {
978         closeFigure(getPrintDC());
979     }
980 
fillPath()981     protected void fillPath() {
982         fillPath(getPrintDC());
983     }
984 
moveTo(float x, float y)985     protected void moveTo(float x, float y) {
986         moveTo(getPrintDC(), x, y);
987     }
988 
lineTo(float x, float y)989     protected void lineTo(float x, float y) {
990         lineTo(getPrintDC(), x, y);
991     }
992 
polyBezierTo(float control1x, float control1y, float control2x, float control2y, float endX, float endY)993     protected void polyBezierTo(float control1x, float control1y,
994                                 float control2x, float control2y,
995                                 float endX, float endY) {
996 
997         polyBezierTo(getPrintDC(),  control1x, control1y,
998                                control2x, control2y,
999                                endX, endY);
1000     }
1001 
1002     /**
1003      * Set the current polgon fill rule into the printer device context.
1004      * The {@code fillRule} should
1005      * be one of the following Windows constants:
1006      * {@code ALTERNATE} or {@code WINDING}.
1007      */
setPolyFillMode(int fillRule)1008     protected void setPolyFillMode(int fillRule) {
1009         setPolyFillMode(getPrintDC(), fillRule);
1010     }
1011 
1012     /**
1013      * Set the GDI graphics mode to {@code GM_ADVANCED}.
1014      */
setAdvancedGraphicsMode()1015     private int setAdvancedGraphicsMode() {
1016         return setAdvancedGraphicsMode(getPrintDC());
1017     }
1018 
1019     /**
1020      * Set the GDI graphics mode.
1021      * The {@code mode} should
1022      * be one of the following Windows constants:
1023      * {@code GM_COMPATIBLE} or {@code GM_ADVANCED}.
1024      */
setGraphicsMode(int mode)1025     private void setGraphicsMode(int mode) {
1026         setGraphicsMode(getPrintDC(), mode);
1027     }
1028 
1029     /**
1030      * Scale the GDI World Transform.
1031      */
scale(double scaleX, double scaleY)1032     private void scale(double scaleX, double scaleY) {
1033         scale(getPrintDC(), scaleX, scaleY);
1034     }
1035 
1036     /**
1037      * Get the GDI World Transform.
1038      */
getWorldTransform(double[] transform)1039     private void getWorldTransform(double[] transform) {
1040         getWorldTransform(getPrintDC(), transform);
1041     }
1042 
1043     /**
1044      * Set the GDI World Transform.
1045      */
setWorldTransform(double[] transform)1046     private void setWorldTransform(double[] transform) {
1047         setWorldTransform(getPrintDC(), transform);
1048     }
1049 
1050     /*
1051      * Create a Window's solid brush for the color specified
1052      * by {@code (red, green, blue)}. Once the brush
1053      * is created, select it in the current printing device
1054      * context and free the old brush.
1055      */
selectSolidBrush(Color color)1056     protected void selectSolidBrush(Color color) {
1057 
1058         /* We only need to select a brush if the color has changed.
1059         */
1060         if (color.equals(mLastColor) == false) {
1061             mLastColor = color;
1062             float[] rgb = color.getRGBColorComponents(null);
1063 
1064             selectSolidBrush(getPrintDC(),
1065                              (int) (rgb[0] * MAX_WCOLOR),
1066                              (int) (rgb[1] * MAX_WCOLOR),
1067                              (int) (rgb[2] * MAX_WCOLOR));
1068         }
1069     }
1070 
1071     /**
1072      * Return the x coordinate of the current pen
1073      * position in the print device context.
1074      */
getPenX()1075     protected int getPenX() {
1076 
1077         return getPenX(getPrintDC());
1078     }
1079 
1080 
1081     /**
1082      * Return the y coordinate of the current pen
1083      * position in the print device context.
1084      */
getPenY()1085     protected int getPenY() {
1086 
1087         return getPenY(getPrintDC());
1088     }
1089 
1090     /**
1091      * Set the current path in the printer device's
1092      * context to be clipping path.
1093      */
selectClipPath()1094     protected void selectClipPath() {
1095         selectClipPath(getPrintDC());
1096     }
1097 
1098 
frameRect(float x, float y, float width, float height)1099     protected void frameRect(float x, float y, float width, float height) {
1100         frameRect(getPrintDC(), x, y, width, height);
1101     }
1102 
fillRect(float x, float y, float width, float height, Color color)1103     protected void fillRect(float x, float y, float width, float height,
1104                             Color color) {
1105         float[] rgb = color.getRGBColorComponents(null);
1106 
1107         fillRect(getPrintDC(), x, y, width, height,
1108                  (int) (rgb[0] * MAX_WCOLOR),
1109                  (int) (rgb[1] * MAX_WCOLOR),
1110                  (int) (rgb[2] * MAX_WCOLOR));
1111     }
1112 
1113 
selectPen(float width, Color color)1114     protected void selectPen(float width, Color color) {
1115 
1116         float[] rgb = color.getRGBColorComponents(null);
1117 
1118         selectPen(getPrintDC(), width,
1119                   (int) (rgb[0] * MAX_WCOLOR),
1120                   (int) (rgb[1] * MAX_WCOLOR),
1121                   (int) (rgb[2] * MAX_WCOLOR));
1122     }
1123 
1124 
selectStylePen(int cap, int join, float width, Color color)1125     protected boolean selectStylePen(int cap, int join, float width,
1126                                      Color color) {
1127 
1128         long endCap;
1129         long lineJoin;
1130 
1131         float[] rgb = color.getRGBColorComponents(null);
1132 
1133         switch(cap) {
1134         case BasicStroke.CAP_BUTT: endCap = PS_ENDCAP_FLAT; break;
1135         case BasicStroke.CAP_ROUND: endCap = PS_ENDCAP_ROUND; break;
1136         default:
1137         case BasicStroke.CAP_SQUARE: endCap = PS_ENDCAP_SQUARE; break;
1138         }
1139 
1140         switch(join) {
1141         case BasicStroke.JOIN_BEVEL:lineJoin = PS_JOIN_BEVEL; break;
1142         default:
1143         case BasicStroke.JOIN_MITER:lineJoin = PS_JOIN_MITER; break;
1144         case BasicStroke.JOIN_ROUND:lineJoin = PS_JOIN_ROUND; break;
1145         }
1146 
1147         return (selectStylePen(getPrintDC(), endCap, lineJoin, width,
1148                                (int) (rgb[0] * MAX_WCOLOR),
1149                                (int) (rgb[1] * MAX_WCOLOR),
1150                                (int) (rgb[2] * MAX_WCOLOR)));
1151     }
1152 
1153     /**
1154      * Set a GDI font capable of drawing the java Font
1155      * passed in.
1156      */
setFont(String family, float size, int style, int rotation, float awScale)1157     protected boolean setFont(String family, float size, int style,
1158                               int rotation, float awScale) {
1159 
1160         if (family.isEmpty()) {
1161             return false;
1162         }
1163 
1164         boolean didSetFont = true;
1165 
1166         if (!family.equals(mLastFontFamily) ||
1167             size     != mLastFontSize       ||
1168             style    != mLastFontStyle      ||
1169             rotation != mLastRotation       ||
1170             awScale  != mLastAwScale) {
1171 
1172             didSetFont = setFont(getPrintDC(),
1173                                  family,
1174                                  size,
1175                                  (style & Font.BOLD) != 0,
1176                                  (style & Font.ITALIC) != 0,
1177                                  rotation, awScale);
1178             if (didSetFont) {
1179                 mLastFontFamily   = family;
1180                 mLastFontSize     = size;
1181                 mLastFontStyle    = style;
1182                 mLastRotation     = rotation;
1183                 mLastAwScale      = awScale;
1184             }
1185         }
1186         return didSetFont;
1187     }
1188 
1189     /**
1190      * Set the GDI color for text drawing.
1191      */
setTextColor(Color color)1192     protected void setTextColor(Color color) {
1193 
1194         /* We only need to select a brush if the color has changed.
1195         */
1196         if (color.equals(mLastTextColor) == false) {
1197             mLastTextColor = color;
1198             float[] rgb = color.getRGBColorComponents(null);
1199 
1200             setTextColor(getPrintDC(),
1201                          (int) (rgb[0] * MAX_WCOLOR),
1202                          (int) (rgb[1] * MAX_WCOLOR),
1203                          (int) (rgb[2] * MAX_WCOLOR));
1204         }
1205     }
1206 
1207     /**
1208      * Remove control characters.
1209      */
1210     @Override
removeControlChars(String str)1211     protected String removeControlChars(String str) {
1212         return super.removeControlChars(str);
1213     }
1214 
1215     /**
1216      * Draw the string {@code text} to the printer's
1217      * device context at the specified position.
1218      */
textOut(String str, float x, float y, float[] positions)1219     protected void textOut(String str, float x, float y,
1220                            float[] positions) {
1221         /* Don't leave handling of control chars to GDI.
1222          * If control chars are removed,  'positions' isn't valid.
1223          * This means the caller needs to be aware of this and remove
1224          * control chars up front if supplying positions. Since the
1225          * caller is tightly integrated here, that's acceptable.
1226          */
1227         String text = removeControlChars(str);
1228         assert (positions == null) || (text.length() == str.length());
1229         if (text.length() == 0) {
1230             return;
1231         }
1232         textOut(getPrintDC(), text, text.length(), false, x, y, positions);
1233     }
1234 
1235    /**
1236      * Draw the glyphs {@code glyphs} to the printer's
1237      * device context at the specified position.
1238      */
glyphsOut(int []glyphs, float x, float y, float[] positions)1239     protected void glyphsOut(int []glyphs, float x, float y,
1240                              float[] positions) {
1241 
1242         /* TrueType glyph codes are 16 bit values, so can be packed
1243          * in a unicode string, and that's how GDI expects them.
1244          * A flag bit is set to indicate to GDI that these are glyphs,
1245          * not characters. The positions array must always be non-null
1246          * here for our purposes, although if not supplied, GDI should
1247          * just use the default advances for the glyphs.
1248          * Mask out upper 16 bits to remove any slot from a composite.
1249          */
1250         char[] glyphCharArray = new char[glyphs.length];
1251         for (int i=0;i<glyphs.length;i++) {
1252             glyphCharArray[i] = (char)(glyphs[i] & 0xffff);
1253         }
1254         String glyphStr = new String(glyphCharArray);
1255         textOut(getPrintDC(), glyphStr, glyphs.length, true, x, y, positions);
1256     }
1257 
1258 
1259     /**
1260      * Get the advance of this text that GDI returns for the
1261      * font currently selected into the GDI device context for
1262      * this job. Note that the removed control characters are
1263      * interpreted as zero-width by JDK and we remove them for
1264      * rendering so also remove them for measurement so that
1265      * this measurement can be properly compared with JDK measurement.
1266      */
getGDIAdvance(String text)1267     protected int getGDIAdvance(String text) {
1268         /* Don't leave handling of control chars to GDI. */
1269         text = removeControlChars(text);
1270         if (text.length() == 0) {
1271             return 0;
1272         }
1273         return getGDIAdvance(getPrintDC(), text);
1274     }
1275 
1276      /**
1277      * Draw the 24 bit BGR image buffer represented by
1278      * {@code image} to the GDI device context
1279      * {@code printDC}. The image is drawn at
1280      * {@code (destX, destY)} in device coordinates.
1281      * The image is scaled into a square of size
1282      * specified by {@code destWidth} and
1283      * {@code destHeight}. The portion of the
1284      * source image copied into that square is specified
1285      * by {@code srcX}, {@code srcY},
1286      * {@code srcWidth}, and srcHeight.
1287      */
drawImage3ByteBGR(byte[] image, float destX, float destY, float destWidth, float destHeight, float srcX, float srcY, float srcWidth, float srcHeight)1288     protected void drawImage3ByteBGR(byte[] image,
1289                                      float destX, float destY,
1290                                      float destWidth, float destHeight,
1291                                      float srcX, float srcY,
1292                                      float srcWidth, float srcHeight) {
1293 
1294 
1295         drawDIBImage(getPrintDC(), image,
1296                      destX, destY,
1297                      destWidth, destHeight,
1298                      srcX, srcY,
1299                      srcWidth, srcHeight,
1300                      24, null);
1301 
1302     }
1303 
1304     /* If 'icm' is null we expect its 24 bit (ie 3BYTE_BGR).
1305      * If 'icm' is non-null we expect its no more than 8 bpp and
1306      * specifically must be a valid DIB sizes : 1, 4 or 8 bpp.
1307      * Then we need to extract the colours into a byte array of the
1308      * format required by GDI which is an array of 'RGBQUAD'
1309      * RGBQUAD looks like :
1310      * typedef struct tagRGBQUAD {
1311      *    BYTE    rgbBlue;
1312      *    BYTE    rgbGreen;
1313      *    BYTE    rgbRed;
1314      *    BYTE    rgbReserved; // must be zero.
1315      * } RGBQUAD;
1316      * There's no alignment problem as GDI expects this to be packed
1317      * and each struct will start on a 4 byte boundary anyway.
1318      */
drawDIBImage(byte[] image, float destX, float destY, float destWidth, float destHeight, float srcX, float srcY, float srcWidth, float srcHeight, int sampleBitsPerPixel, IndexColorModel icm)1319     protected void drawDIBImage(byte[] image,
1320                                 float destX, float destY,
1321                                 float destWidth, float destHeight,
1322                                 float srcX, float srcY,
1323                                 float srcWidth, float srcHeight,
1324                                 int sampleBitsPerPixel,
1325                                 IndexColorModel icm) {
1326         int bitCount = 24;
1327         byte[] bmiColors = null;
1328 
1329         if (icm != null) {
1330             bitCount = sampleBitsPerPixel;
1331             bmiColors = new byte[(1<<icm.getPixelSize())*4];
1332             for (int i=0;i<icm.getMapSize(); i++) {
1333                 bmiColors[i*4+0]=(byte)(icm.getBlue(i)&0xff);
1334                 bmiColors[i*4+1]=(byte)(icm.getGreen(i)&0xff);
1335                 bmiColors[i*4+2]=(byte)(icm.getRed(i)&0xff);
1336             }
1337         }
1338 
1339         drawDIBImage(getPrintDC(), image,
1340                      destX, destY,
1341                      destWidth, destHeight,
1342                      srcX, srcY,
1343                      srcWidth, srcHeight,
1344                      bitCount, bmiColors);
1345     }
1346 
1347     /**
1348      * Begin a new page.
1349      */
1350     @Override
startPage(PageFormat format, Printable painter, int index, boolean paperChanged)1351     protected void startPage(PageFormat format, Printable painter,
1352                              int index, boolean paperChanged) {
1353 
1354         /* Invalidate any device state caches we are
1355          * maintaining. Win95/98 resets the device
1356          * context attributes to default values at
1357          * the start of each page.
1358          */
1359         invalidateCachedState();
1360 
1361         deviceStartPage(format, painter, index, paperChanged);
1362     }
1363 
1364     /**
1365      * End a page.
1366      */
1367     @Override
endPage(PageFormat format, Printable painter, int index)1368     protected void endPage(PageFormat format, Printable painter,
1369                            int index) {
1370 
1371         deviceEndPage(format, painter, index);
1372     }
1373 
1374     /**
1375      * Forget any device state we may have cached.
1376      */
invalidateCachedState()1377     private void invalidateCachedState() {
1378         mLastColor = null;
1379         mLastTextColor = null;
1380         mLastFontFamily = null;
1381     }
1382 
1383     private boolean defaultCopies = true;
1384     /**
1385      * Set the number of copies to be printed.
1386      */
1387     @Override
setCopies(int copies)1388     public void setCopies(int copies) {
1389         super.setCopies(copies);
1390         defaultCopies = false;
1391         mAttCopies = copies;
1392         setNativeCopies(copies);
1393     }
1394 
1395 
1396  /* Native Methods */
1397 
1398     /**
1399      * Set copies in device.
1400      */
setNativeCopies(int copies)1401     private native void setNativeCopies(int copies);
1402 
1403     /**
1404      * Displays the print dialog and records the user's settings
1405      * into this object. Return false if the user cancels the
1406      * dialog.
1407      * If the dialog is to use a set of attributes, useAttributes is true.
1408      */
jobSetup(Pageable doc, boolean allowPrintToFile)1409     private native boolean jobSetup(Pageable doc, boolean allowPrintToFile);
1410 
1411     /* Make sure printer DC is intialised and that info about the printer
1412      * is reflected back up to Java code
1413      */
1414     @Override
initPrinter()1415     protected native void initPrinter();
1416 
1417     /**
1418      * Call Window's StartDoc routine to begin a
1419      * print job. The DC from the print dialog is
1420      * used. If the print dialog was not displayed
1421      * then a DC for the default printer is created.
1422      * The native StartDoc returns false if the end-user cancelled
1423      * printing. This is possible if the printer is connected to FILE:
1424      * in which case windows queries the user for a destination and the
1425      * user may cancel out of it. Note that the implementation of
1426      * cancel() throws PrinterAbortException to indicate the user cancelled.
1427      */
_startDoc(String dest, String jobName)1428     private native boolean _startDoc(String dest, String jobName)
1429                                      throws PrinterException;
1430     @Override
startDoc()1431     protected void startDoc() throws PrinterException {
1432         if (!_startDoc(mDestination, getJobName())) {
1433             cancel();
1434         }
1435     }
1436 
1437     /**
1438      * Call Window's EndDoc routine to end a
1439      * print job.
1440      */
1441     @Override
endDoc()1442     protected native void endDoc();
1443 
1444     /**
1445      * Call Window's AbortDoc routine to abort a
1446      * print job.
1447      */
1448     @Override
abortDoc()1449     protected native void abortDoc();
1450 
1451     /**
1452      * Call Windows native resource freeing APIs
1453      */
deleteDC(long dc, long devmode, long devnames)1454     private static native void deleteDC(long dc, long devmode, long devnames);
1455 
1456     /**
1457      * Begin a new page. This call's Window's
1458      * StartPage routine.
1459      */
deviceStartPage(PageFormat format, Printable painter, int index, boolean paperChanged)1460     protected native void deviceStartPage(PageFormat format, Printable painter,
1461                                           int index, boolean paperChanged);
1462     /**
1463      * End a page. This call's Window's EndPage
1464      * routine.
1465      */
deviceEndPage(PageFormat format, Printable painter, int index)1466     protected native void deviceEndPage(PageFormat format, Printable painter,
1467                                         int index);
1468 
1469     /**
1470      * Prints the contents of the array of ints, 'data'
1471      * to the current page. The band is placed at the
1472      * location (x, y) in device coordinates on the
1473      * page. The width and height of the band is
1474      * specified by the caller.
1475      */
1476     @Override
printBand(byte[] data, int x, int y, int width, int height)1477     protected native void printBand(byte[] data, int x, int y,
1478                                     int width, int height);
1479 
1480     /**
1481      * Begin a Window's rendering path in the device
1482      * context {@code printDC}.
1483      */
beginPath(long printDC)1484     protected native void beginPath(long printDC);
1485 
1486     /**
1487      * End a Window's rendering path in the device
1488      * context {@code printDC}.
1489      */
endPath(long printDC)1490     protected native void endPath(long printDC);
1491 
1492     /**
1493      * Close a subpath in a Window's rendering path in the device
1494      * context {@code printDC}.
1495      */
closeFigure(long printDC)1496     protected native void closeFigure(long printDC);
1497 
1498     /**
1499      * Fill a defined Window's rendering path in the device
1500      * context {@code printDC}.
1501      */
fillPath(long printDC)1502     protected native void fillPath(long printDC);
1503 
1504     /**
1505      * Move the Window's pen position to {@code (x,y)}
1506      * in the device context {@code printDC}.
1507      */
moveTo(long printDC, float x, float y)1508     protected native void moveTo(long printDC, float x, float y);
1509 
1510     /**
1511      * Draw a line from the current pen position to
1512      * {@code (x,y)} in the device context {@code printDC}.
1513      */
lineTo(long printDC, float x, float y)1514     protected native void lineTo(long printDC, float x, float y);
1515 
polyBezierTo(long printDC, float control1x, float control1y, float control2x, float control2y, float endX, float endY)1516     protected native void polyBezierTo(long printDC,
1517                                        float control1x, float control1y,
1518                                        float control2x, float control2y,
1519                                        float endX, float endY);
1520 
1521     /**
1522      * Set the current polgon fill rule into the device context
1523      * {@code printDC}. The {@code fillRule} should
1524      * be one of the following Windows constants:
1525      * {@code ALTERNATE} or {@code WINDING}.
1526      */
setPolyFillMode(long printDC, int fillRule)1527     protected native void setPolyFillMode(long printDC, int fillRule);
1528 
1529     /**
1530      * Set the GDI graphics mode to {@code GM_ADVANCED}
1531      * into the device context {@code printDC}.
1532      */
setAdvancedGraphicsMode(long printDC)1533     protected native int setAdvancedGraphicsMode(long printDC);
1534 
1535     /**
1536      * Set the GDI graphics {@code mode}
1537      * into the device context {@code printDC}.
1538      * The {@code mode} should
1539      * be one of the following Windows constants:
1540      * {@code GM_COMPATIBLE} or {@code GM_ADVANCED}.
1541      */
setGraphicsMode(long printDC, int mode)1542     protected native void setGraphicsMode(long printDC, int mode);
1543 
1544     /**
1545      * Scale the GDI World Transform
1546      * of the device context {@code printDC}.
1547      */
scale(long printDC, double scaleX, double scaleY)1548     protected native void scale(long printDC, double scaleX, double scaleY);
1549 
1550     /**
1551      * Get the GDI World Transform
1552      * from the device context {@code printDC}.
1553      */
getWorldTransform(long printDC, double[] transform)1554     protected native void getWorldTransform(long printDC, double[] transform);
1555 
1556     /**
1557      * Set the GDI World Transform
1558      * into the device context {@code printDC}.
1559      */
setWorldTransform(long printDC, double[] transform)1560     protected native void setWorldTransform(long printDC, double[] transform);
1561 
1562     /**
1563      * Create a Window's solid brush for the color specified
1564      * by {@code (red, green, blue)}. Once the brush
1565      * is created, select it in the device
1566      * context {@code printDC} and free the old brush.
1567      */
selectSolidBrush(long printDC, int red, int green, int blue)1568     protected native void selectSolidBrush(long printDC,
1569                                            int red, int green, int blue);
1570 
1571     /**
1572      * Return the x coordinate of the current pen
1573      * position in the device context
1574      * {@code printDC}.
1575      */
getPenX(long printDC)1576     protected native int getPenX(long printDC);
1577 
1578     /**
1579      * Return the y coordinate of the current pen
1580      * position in the device context
1581      * {@code printDC}.
1582      */
getPenY(long printDC)1583     protected native int getPenY(long printDC);
1584 
1585     /**
1586      * Select the device context's current path
1587      * to be the clipping path.
1588      */
selectClipPath(long printDC)1589     protected native void selectClipPath(long printDC);
1590 
1591     /**
1592      * Draw a rectangle using specified brush.
1593      */
frameRect(long printDC, float x, float y, float width, float height)1594     protected native void frameRect(long printDC, float x, float y,
1595                                     float width, float height);
1596 
1597     /**
1598      * Fill a rectangle specified by the coordinates using
1599      * specified brush.
1600      */
fillRect(long printDC, float x, float y, float width, float height, int red, int green, int blue)1601     protected native void fillRect(long printDC, float x, float y,
1602                                    float width, float height,
1603                                    int red, int green, int blue);
1604 
1605     /**
1606      * Create a solid brush using the RG & B colors and width.
1607      * Select this brush and delete the old one.
1608      */
selectPen(long printDC, float width, int red, int green, int blue)1609     protected native void selectPen(long printDC, float width,
1610                                     int red, int green, int blue);
1611 
1612     /**
1613      * Create a solid brush using the RG & B colors and specified
1614      * pen styles.  Select this created brush and delete the old one.
1615      */
selectStylePen(long printDC, long cap, long join, float width, int red, int green, int blue)1616     protected native boolean selectStylePen(long printDC, long cap,
1617                                             long join, float width,
1618                                             int red, int green, int blue);
1619 
1620     /**
1621      * Set a GDI font capable of drawing the java Font
1622      * passed in.
1623      */
setFont(long printDC, String familyName, float fontSize, boolean bold, boolean italic, int rotation, float awScale)1624     protected native boolean setFont(long printDC, String familyName,
1625                                      float fontSize,
1626                                      boolean bold,
1627                                      boolean italic,
1628                                      int rotation,
1629                                      float awScale);
1630 
1631 
1632     /**
1633      * Set the GDI color for text drawing.
1634      */
setTextColor(long printDC, int red, int green, int blue)1635     protected native void setTextColor(long printDC,
1636                                        int red, int green, int blue);
1637 
1638 
1639     /**
1640      * Draw the string {@code text} into the device
1641      * context {@code printDC} at the specified
1642      * position.
1643      */
textOut(long printDC, String text, int strlen, boolean glyphs, float x, float y, float[] positions)1644     protected native void textOut(long printDC, String text,
1645                                   int strlen, boolean glyphs,
1646                                   float x, float y, float[] positions);
1647 
1648 
getGDIAdvance(long printDC, String text)1649     private native int getGDIAdvance(long printDC, String text);
1650 
1651      /**
1652      * Draw the DIB compatible image buffer represented by
1653      * {@code image} to the GDI device context
1654      * {@code printDC}. The image is drawn at
1655      * {@code (destX, destY)} in device coordinates.
1656      * The image is scaled into a square of size
1657      * specified by {@code destWidth} and
1658      * {@code destHeight}. The portion of the
1659      * source image copied into that square is specified
1660      * by {@code srcX}, {@code srcY},
1661      * {@code srcWidth}, and srcHeight.
1662      * Note that the image isn't completely compatible with DIB format.
1663      * At the very least it needs to be padded so each scanline is
1664      * DWORD aligned. Also we "flip" the image to make it a bottom-up DIB.
1665      */
drawDIBImage(long printDC, byte[] image, float destX, float destY, float destWidth, float destHeight, float srcX, float srcY, float srcWidth, float srcHeight, int bitCount, byte[] bmiColors)1666     private native void drawDIBImage(long printDC, byte[] image,
1667                                      float destX, float destY,
1668                                      float destWidth, float destHeight,
1669                                      float srcX, float srcY,
1670                                      float srcWidth, float srcHeight,
1671                                      int bitCount, byte[] bmiColors);
1672 
1673 
1674     //** BEGIN Functions called by native code for querying/updating attributes
1675 
getPrinterAttrib()1676     private String getPrinterAttrib() {
1677         // getPrintService will get current print service or default if none
1678         PrintService service = this.getPrintService();
1679         String name = (service != null) ? service.getName() : null;
1680         return name;
1681     }
1682 
1683     /* SheetCollate */
getCollateAttrib()1684     private int getCollateAttrib() {
1685         // -1 means unset, 0 uncollated, 1 collated.
1686         return mAttCollate;
1687     }
1688 
setCollateAttrib(Attribute attr)1689     private void setCollateAttrib(Attribute attr) {
1690         if (attr == SheetCollate.COLLATED) {
1691             mAttCollate = 1; // DMCOLLATE_TRUE
1692         } else {
1693             mAttCollate = 0; // DMCOLLATE_FALSE
1694         }
1695     }
1696 
setCollateAttrib(Attribute attr, PrintRequestAttributeSet set)1697     private void setCollateAttrib(Attribute attr,
1698                                   PrintRequestAttributeSet set) {
1699         setCollateAttrib(attr);
1700         set.add(attr);
1701     }
1702 
1703     /* Orientation */
1704 
getOrientAttrib()1705     private int getOrientAttrib() {
1706         int orient = PageFormat.PORTRAIT;
1707         OrientationRequested orientReq = (attributes == null) ? null :
1708             (OrientationRequested)attributes.get(OrientationRequested.class);
1709         if (orientReq == null) {
1710             orientReq = (OrientationRequested)
1711                myService.getDefaultAttributeValue(OrientationRequested.class);
1712         }
1713         if (orientReq != null) {
1714             if (orientReq == OrientationRequested.REVERSE_LANDSCAPE) {
1715                 orient = PageFormat.REVERSE_LANDSCAPE;
1716             } else if (orientReq == OrientationRequested.LANDSCAPE) {
1717                 orient = PageFormat.LANDSCAPE;
1718             }
1719         }
1720 
1721         return orient;
1722     }
1723 
setOrientAttrib(Attribute attr, PrintRequestAttributeSet set)1724     private void setOrientAttrib(Attribute attr,
1725                                  PrintRequestAttributeSet set) {
1726         if (set != null) {
1727             set.add(attr);
1728         }
1729     }
1730 
1731     /* Copies and Page Range. */
getCopiesAttrib()1732     private int getCopiesAttrib() {
1733         if (defaultCopies) {
1734             return 0;
1735         } else {
1736             return getCopiesInt();
1737         }
1738      }
1739 
setRangeCopiesAttribute(int from, int to, boolean isRangeSet, int copies)1740     private void setRangeCopiesAttribute(int from, int to, boolean isRangeSet,
1741                                          int copies) {
1742         if (attributes != null) {
1743             if (isRangeSet) {
1744                 attributes.add(new PageRanges(from, to));
1745                 setPageRange(from, to);
1746             }
1747             defaultCopies = false;
1748             attributes.add(new Copies(copies));
1749             /* Since this is called from native to tell Java to sync
1750              * up with native, we don't call this class's own setCopies()
1751              * method which is mainly to send the value down to native
1752              */
1753             super.setCopies(copies);
1754             mAttCopies = copies;
1755         }
1756     }
1757 
1758 
1759 
getDestAttrib()1760     private boolean getDestAttrib() {
1761         return (mDestination != null);
1762     }
1763 
1764     /* Quality */
getQualityAttrib()1765     private int getQualityAttrib() {
1766         return mAttQuality;
1767     }
1768 
setQualityAttrib(Attribute attr)1769     private void setQualityAttrib(Attribute attr) {
1770         if (attr == PrintQuality.HIGH) {
1771             mAttQuality = -4; // DMRES_HIGH
1772         } else if (attr == PrintQuality.NORMAL) {
1773             mAttQuality = -3; // DMRES_MEDIUM
1774         } else {
1775             mAttQuality = -2; // DMRES_LOW
1776         }
1777     }
1778 
setQualityAttrib(Attribute attr, PrintRequestAttributeSet set)1779     private void setQualityAttrib(Attribute attr,
1780                                   PrintRequestAttributeSet set) {
1781         setQualityAttrib(attr);
1782         set.add(attr);
1783     }
1784 
1785     /* Color/Chromaticity */
getColorAttrib()1786     private int getColorAttrib() {
1787         return mAttChromaticity;
1788     }
1789 
setColorAttrib(Attribute attr)1790     private void setColorAttrib(Attribute attr) {
1791         if (attr == Chromaticity.COLOR) {
1792             mAttChromaticity = 2; // DMCOLOR_COLOR
1793         } else {
1794             mAttChromaticity = 1; // DMCOLOR_MONOCHROME
1795         }
1796     }
1797 
setColorAttrib(Attribute attr, PrintRequestAttributeSet set)1798     private void setColorAttrib(Attribute attr,
1799                                   PrintRequestAttributeSet set) {
1800         setColorAttrib(attr);
1801         set.add(attr);
1802     }
1803 
1804     /* Sides */
getSidesAttrib()1805     private int getSidesAttrib() {
1806         return mAttSides;
1807     }
1808 
setSidesAttrib(Attribute attr)1809     private void setSidesAttrib(Attribute attr) {
1810         if (attr == Sides.TWO_SIDED_LONG_EDGE) {
1811             mAttSides = 2; // DMDUP_VERTICAL
1812         } else if (attr == Sides.TWO_SIDED_SHORT_EDGE) {
1813             mAttSides = 3; // DMDUP_HORIZONTAL
1814         } else { // Sides.ONE_SIDED
1815             mAttSides = 1;
1816         }
1817     }
1818 
setSidesAttrib(Attribute attr, PrintRequestAttributeSet set)1819     private void setSidesAttrib(Attribute attr,
1820                                 PrintRequestAttributeSet set) {
1821         setSidesAttrib(attr);
1822         set.add(attr);
1823     }
1824 
1825     /** MediaSizeName / dmPaper */
getWin32MediaAttrib()1826     private int[] getWin32MediaAttrib() {
1827         int[] wid_ht = {0, 0};
1828         if (attributes != null) {
1829             Media media = (Media)attributes.get(Media.class);
1830             if (media instanceof MediaSizeName) {
1831                 MediaSizeName msn = (MediaSizeName)media;
1832                 MediaSize ms = MediaSize.getMediaSizeForName(msn);
1833                 if (ms != null) {
1834                     wid_ht[0] = (int)(ms.getX(MediaSize.INCH) * 72.0);
1835                     wid_ht[1] = (int)(ms.getY(MediaSize.INCH) * 72.0);
1836                 }
1837             }
1838         }
1839         return wid_ht;
1840     }
1841 
setWin32MediaAttrib(Attribute attr)1842     private void setWin32MediaAttrib(Attribute attr) {
1843         if (!(attr instanceof MediaSizeName)) {
1844             return;
1845         }
1846         MediaSizeName msn = (MediaSizeName)attr;
1847         mAttMediaSizeName = ((Win32PrintService)myService).findPaperID(msn);
1848     }
1849 
addPaperSize(PrintRequestAttributeSet aset, int dmIndex, int width, int length)1850     private void addPaperSize(PrintRequestAttributeSet aset,
1851                               int dmIndex, int width, int length) {
1852 
1853         if (aset == null) {
1854             return;
1855         }
1856         MediaSizeName msn =
1857            ((Win32PrintService)myService).findWin32Media(dmIndex);
1858         if (msn == null) {
1859             msn = ((Win32PrintService)myService).
1860                 findMatchingMediaSizeNameMM((float)width, (float)length);
1861         }
1862 
1863         if (msn != null) {
1864             aset.add(msn);
1865         }
1866     }
1867 
setWin32MediaAttrib(int dmIndex, int width, int length)1868     private void setWin32MediaAttrib(int dmIndex, int width, int length) {
1869         addPaperSize(attributes, dmIndex, width, length);
1870         mAttMediaSizeName = dmIndex;
1871     }
1872 
1873     /* MediaTray / dmTray */
setMediaTrayAttrib(Attribute attr)1874     private void setMediaTrayAttrib(Attribute attr) {
1875         if (attr == MediaTray.BOTTOM) {
1876             mAttMediaTray = 2;    // DMBIN_LOWER
1877         } else if (attr == MediaTray.ENVELOPE) {
1878             mAttMediaTray = 5;    // DMBIN_ENVELOPE
1879         } else if (attr == MediaTray.LARGE_CAPACITY) {
1880             mAttMediaTray = 11;      // DMBIN_LARGECAPACITY
1881         } else if (attr == MediaTray.MAIN) {
1882             mAttMediaTray =1;               // DMBIN_UPPER
1883         } else if (attr == MediaTray.MANUAL) {
1884             mAttMediaTray = 4;              // DMBIN_MANUAL
1885         } else if (attr == MediaTray.MIDDLE) {
1886             mAttMediaTray = 3;              // DMBIN_MIDDLE
1887         } else if (attr == MediaTray.SIDE) {
1888             // no equivalent predefined value
1889             mAttMediaTray = 7;              // DMBIN_AUTO
1890         } else if (attr == MediaTray.TOP) {
1891             mAttMediaTray = 1;              // DMBIN_UPPER
1892         } else {
1893             if (attr instanceof Win32MediaTray) {
1894                 mAttMediaTray = ((Win32MediaTray)attr).winID;
1895             } else {
1896                 mAttMediaTray = 1;  // default
1897             }
1898         }
1899     }
1900 
setMediaTrayAttrib(int dmBinID)1901     private void setMediaTrayAttrib(int dmBinID) {
1902         mAttMediaTray = dmBinID;
1903         MediaTray tray = ((Win32PrintService)myService).findMediaTray(dmBinID);
1904     }
1905 
getMediaTrayAttrib()1906     private int getMediaTrayAttrib() {
1907         return mAttMediaTray;
1908     }
1909 
1910 
1911 
getPrintToFileEnabled()1912     private boolean getPrintToFileEnabled() {
1913         @SuppressWarnings("removal")
1914         SecurityManager security = System.getSecurityManager();
1915         if (security != null) {
1916             FilePermission printToFilePermission =
1917                 new FilePermission("<<ALL FILES>>", "read,write");
1918             try {
1919                 security.checkPermission(printToFilePermission);
1920             } catch (SecurityException e) {
1921                 return false;
1922             }
1923         }
1924         return true;
1925     }
1926 
setNativeAttributes(int flags, int fields, int values)1927     private void setNativeAttributes(int flags, int fields, int values) {
1928         if (attributes == null) {
1929             return;
1930         }
1931         if ((flags & PD_PRINTTOFILE) != 0) {
1932             Destination destPrn = (Destination)attributes.get(
1933                                                  Destination.class);
1934             if (destPrn == null) {
1935                 try {
1936                     attributes.add(new Destination(
1937                                                new File("./out.prn").toURI()));
1938                 } catch (SecurityException se) {
1939                     try {
1940                         attributes.add(new Destination(
1941                                                 new URI("file:out.prn")));
1942                     } catch (URISyntaxException e) {
1943                     }
1944                 }
1945             }
1946         } else {
1947             attributes.remove(Destination.class);
1948         }
1949 
1950         if ((flags & PD_COLLATE) != 0) {
1951             setCollateAttrib(SheetCollate.COLLATED, attributes);
1952         } else {
1953             setCollateAttrib(SheetCollate.UNCOLLATED, attributes);
1954         }
1955 
1956         if ((flags & PD_NOSELECTION) != PD_NOSELECTION) {
1957             if ((flags & PD_PAGENUMS) != 0) {
1958                 attributes.add(SunPageSelection.RANGE);
1959             } else if ((flags & PD_SELECTION) != 0) {
1960                 attributes.add(SunPageSelection.SELECTION);
1961             } else {
1962                 attributes.add(SunPageSelection.ALL);
1963             }
1964         }
1965 
1966         if ((fields & DM_ORIENTATION) != 0) {
1967             if ((values & SET_ORIENTATION) != 0) {
1968                 setOrientAttrib(OrientationRequested.LANDSCAPE, attributes);
1969             } else {
1970                 setOrientAttrib(OrientationRequested.PORTRAIT, attributes);
1971             }
1972         }
1973 
1974         if ((fields & DM_COLOR) != 0) {
1975             if ((values & SET_COLOR) != 0) {
1976                 setColorAttrib(Chromaticity.COLOR, attributes);
1977             } else {
1978                 setColorAttrib(Chromaticity.MONOCHROME, attributes);
1979             }
1980         }
1981 
1982         if ((fields & DM_PRINTQUALITY) != 0) {
1983             PrintQuality quality;
1984             if ((values & SET_RES_LOW) != 0) {
1985                 quality = PrintQuality.DRAFT;
1986             } else if ((fields & SET_RES_HIGH) != 0) {
1987                 quality = PrintQuality.HIGH;
1988             } else {
1989                 quality = PrintQuality.NORMAL;
1990             }
1991             setQualityAttrib(quality, attributes);
1992         }
1993 
1994         if ((fields & DM_DUPLEX) != 0) {
1995             Sides sides;
1996             if ((values & SET_DUP_VERTICAL) != 0) {
1997                 sides = Sides.TWO_SIDED_LONG_EDGE;
1998             } else if ((values & SET_DUP_HORIZONTAL) != 0) {
1999                 sides = Sides.TWO_SIDED_SHORT_EDGE;
2000             } else {
2001                 sides = Sides.ONE_SIDED;
2002             }
2003             setSidesAttrib(sides, attributes);
2004         }
2005     }
2006 
2007     private static final class DevModeValues {
2008         int dmFields;
2009         short copies;
2010         short collate;
2011         short color;
2012         short duplex;
2013         short orient;
2014         short paper;
2015         short bin;
2016         short xres_quality;
2017         short yres;
2018     }
2019 
getDevModeValues(PrintRequestAttributeSet aset, DevModeValues info)2020     private void getDevModeValues(PrintRequestAttributeSet aset,
2021                                   DevModeValues info) {
2022 
2023         Copies c = (Copies)aset.get(Copies.class);
2024         if (c != null) {
2025             info.dmFields |= DM_COPIES;
2026             info.copies = (short)c.getValue();
2027         }
2028 
2029         SheetCollate sc = (SheetCollate)aset.get(SheetCollate.class);
2030         if (sc != null) {
2031             info.dmFields |= DM_COLLATE;
2032             info.collate = (sc == SheetCollate.COLLATED) ?
2033                 DMCOLLATE_TRUE : DMCOLLATE_FALSE;
2034         }
2035 
2036         Chromaticity ch = (Chromaticity)aset.get(Chromaticity.class);
2037         if (ch != null) {
2038             info.dmFields |= DM_COLOR;
2039             if (ch == Chromaticity.COLOR) {
2040                 info.color = DMCOLOR_COLOR;
2041             } else {
2042                 info.color = DMCOLOR_MONOCHROME;
2043             }
2044         }
2045 
2046         Sides s = (Sides)aset.get(Sides.class);
2047         if (s != null) {
2048             info.dmFields |= DM_DUPLEX;
2049             if (s == Sides.TWO_SIDED_LONG_EDGE) {
2050                 info.duplex = DMDUP_VERTICAL;
2051             } else if (s == Sides.TWO_SIDED_SHORT_EDGE) {
2052                 info.duplex = DMDUP_HORIZONTAL;
2053             } else { // Sides.ONE_SIDED
2054                 info.duplex = DMDUP_SIMPLEX;
2055             }
2056         }
2057 
2058         OrientationRequested or =
2059             (OrientationRequested)aset.get(OrientationRequested.class);
2060         if (or != null) {
2061             info.dmFields |= DM_ORIENTATION;
2062             info.orient = (or == OrientationRequested.LANDSCAPE)
2063                 ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
2064         }
2065 
2066         Media m = (Media)aset.get(Media.class);
2067         if (m instanceof MediaSizeName) {
2068             info.dmFields |= DM_PAPERSIZE;
2069             MediaSizeName msn = (MediaSizeName)m;
2070             info.paper =
2071                 (short)((Win32PrintService)myService).findPaperID(msn);
2072         }
2073 
2074         MediaTray mt = null;
2075         if (m instanceof MediaTray) {
2076             mt = (MediaTray)m;
2077         }
2078         if (mt == null) {
2079             SunAlternateMedia sam =
2080                 (SunAlternateMedia)aset.get(SunAlternateMedia.class);
2081             if (sam != null && (sam.getMedia() instanceof MediaTray)) {
2082                 mt = (MediaTray)sam.getMedia();
2083             }
2084         }
2085 
2086         if (mt != null) {
2087             info.dmFields |= DM_DEFAULTSOURCE;
2088             info.bin = (short)(((Win32PrintService)myService).findTrayID(mt));
2089         }
2090 
2091         PrintQuality q = (PrintQuality)aset.get(PrintQuality.class);
2092         if (q != null) {
2093             info.dmFields |= DM_PRINTQUALITY;
2094             if (q == PrintQuality.DRAFT) {
2095                 info.xres_quality = DMRES_DRAFT;
2096             } else if (q == PrintQuality.HIGH) {
2097                 info.xres_quality = DMRES_HIGH;
2098             } else {
2099                 info.xres_quality = DMRES_MEDIUM;
2100             }
2101         }
2102 
2103         PrinterResolution r =
2104             (PrinterResolution)aset.get(PrinterResolution.class);
2105         if (r != null) {
2106             info.dmFields |= DM_PRINTQUALITY | DM_YRESOLUTION;
2107             info.xres_quality =
2108                 (short)r.getCrossFeedResolution(PrinterResolution.DPI);
2109             info.yres = (short)r.getFeedResolution(PrinterResolution.DPI);
2110         }
2111     }
2112 
2113     /* This method is called from native to update the values in the
2114      * attribute set which originates from the cross-platform dialog,
2115      * but updated by the native DocumentPropertiesUI which updates the
2116      * devmode. This syncs the devmode back in to the attributes so that
2117      * we can update the cross-platform dialog.
2118      * The attribute set here is a temporary one installed whilst this
2119      * happens,
2120      */
setJobAttributes(PrintRequestAttributeSet attributes, int fields, int values, short copies, short dmPaperSize, short dmPaperWidth, short dmPaperLength, short dmDefaultSource, short xRes, short yRes)2121     private void setJobAttributes(PrintRequestAttributeSet attributes,
2122                                         int fields, int values,
2123                                         short copies,
2124                                         short dmPaperSize,
2125                                         short dmPaperWidth,
2126                                         short dmPaperLength,
2127                                         short dmDefaultSource,
2128                                         short xRes,
2129                                         short yRes) {
2130 
2131         if (attributes == null) {
2132             return;
2133         }
2134 
2135         if ((fields & DM_COPIES) != 0) {
2136             attributes.add(new Copies(copies));
2137         }
2138 
2139         if ((fields & DM_COLLATE) != 0) {
2140             if ((values & SET_COLLATED) != 0) {
2141                 attributes.add(SheetCollate.COLLATED);
2142             } else {
2143                 attributes.add(SheetCollate.UNCOLLATED);
2144             }
2145         }
2146 
2147         if ((fields & DM_ORIENTATION) != 0) {
2148             if ((values & SET_ORIENTATION) != 0) {
2149                 attributes.add(OrientationRequested.LANDSCAPE);
2150             } else {
2151                 attributes.add(OrientationRequested.PORTRAIT);
2152             }
2153         }
2154 
2155         if ((fields & DM_COLOR) != 0) {
2156             if ((values & SET_COLOR) != 0) {
2157                 attributes.add(Chromaticity.COLOR);
2158             } else {
2159                 attributes.add(Chromaticity.MONOCHROME);
2160             }
2161         }
2162 
2163         if ((fields & DM_PRINTQUALITY) != 0) {
2164             /* value < 0 indicates quality setting.
2165              * value > 0 indicates X resolution. In that case
2166              * hopefully we will also find y-resolution specified.
2167              * If its not, assume its the same as x-res.
2168              * Maybe Java code should try to reconcile this against
2169              * the printers claimed set of supported resolutions.
2170              */
2171             if (xRes < 0) {
2172                 PrintQuality quality;
2173                 if ((values & SET_RES_LOW) != 0) {
2174                     quality = PrintQuality.DRAFT;
2175                 } else if ((fields & SET_RES_HIGH) != 0) {
2176                     quality = PrintQuality.HIGH;
2177                 } else {
2178                     quality = PrintQuality.NORMAL;
2179                 }
2180                 attributes.add(quality);
2181             } else if (xRes > 0 && yRes > 0) {
2182                 attributes.add(
2183                     new PrinterResolution(xRes, yRes, PrinterResolution.DPI));
2184             }
2185         }
2186 
2187         if ((fields & DM_DUPLEX) != 0) {
2188             Sides sides;
2189             if ((values & SET_DUP_VERTICAL) != 0) {
2190                 sides = Sides.TWO_SIDED_LONG_EDGE;
2191             } else if ((values & SET_DUP_HORIZONTAL) != 0) {
2192                 sides = Sides.TWO_SIDED_SHORT_EDGE;
2193             } else {
2194                 sides = Sides.ONE_SIDED;
2195             }
2196             attributes.add(sides);
2197         }
2198 
2199         if ((fields & DM_PAPERSIZE) != 0) {
2200             addPaperSize(attributes, dmPaperSize, dmPaperWidth, dmPaperLength);
2201         }
2202 
2203         if ((fields & DM_DEFAULTSOURCE) != 0) {
2204             MediaTray tray =
2205                 ((Win32PrintService)myService).findMediaTray(dmDefaultSource);
2206             attributes.add(new SunAlternateMedia(tray));
2207         }
2208     }
2209 
showDocProperties(long hWnd, PrintRequestAttributeSet aset, int dmFields, short copies, short collate, short color, short duplex, short orient, short paper, short bin, short xres_quality, short yres)2210     private native boolean showDocProperties(long hWnd,
2211                                              PrintRequestAttributeSet aset,
2212                                              int dmFields,
2213                                              short copies,
2214                                              short collate,
2215                                              short color,
2216                                              short duplex,
2217                                              short orient,
2218                                              short paper,
2219                                              short bin,
2220                                              short xres_quality,
2221                                              short yres);
2222 
2223     public PrintRequestAttributeSet
showDocumentProperties(Window owner, PrintService service, PrintRequestAttributeSet aset)2224         showDocumentProperties(Window owner,
2225                                PrintService service,
2226                                PrintRequestAttributeSet aset)
2227     {
2228         try {
2229             setNativePrintServiceIfNeeded(service.getName());
2230         } catch (PrinterException e) {
2231         }
2232         final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
2233         long hWnd = acc.<WComponentPeer>getPeer(owner).getHWnd();
2234         DevModeValues info = new DevModeValues();
2235         getDevModeValues(aset, info);
2236         boolean ok =
2237             showDocProperties(hWnd, aset,
2238                               info.dmFields,
2239                               info.copies,
2240                               info.collate,
2241                               info.color,
2242                               info.duplex,
2243                               info.orient,
2244                               info.paper,
2245                               info.bin,
2246                               info.xres_quality,
2247                               info.yres);
2248 
2249         if (ok) {
2250             return aset;
2251         } else {
2252             return null;
2253         }
2254     }
2255 
2256     /* Printer Resolution. See also getXRes() and getYRes() */
setResolutionDPI(int xres, int yres)2257     private void setResolutionDPI(int xres, int yres) {
2258         if (attributes != null) {
2259             PrinterResolution res =
2260                 new PrinterResolution(xres, yres, PrinterResolution.DPI);
2261             attributes.add(res);
2262         }
2263         mAttXRes = xres;
2264         mAttYRes = yres;
2265     }
2266 
setResolutionAttrib(Attribute attr)2267     private void setResolutionAttrib(Attribute attr) {
2268         PrinterResolution pr = (PrinterResolution)attr;
2269         mAttXRes = pr.getCrossFeedResolution(PrinterResolution.DPI);
2270         mAttYRes = pr.getFeedResolution(PrinterResolution.DPI);
2271     }
2272 
setPrinterNameAttrib(String printerName)2273     private void setPrinterNameAttrib(String printerName) {
2274         PrintService service = this.getPrintService();
2275 
2276         if (printerName == null) {
2277             return;
2278         }
2279 
2280         if (service != null && printerName.equals(service.getName())) {
2281             return;
2282         } else {
2283             PrintService []services = PrinterJob.lookupPrintServices();
2284             for (int i=0; i<services.length; i++) {
2285                 if (printerName.equals(services[i].getName())) {
2286 
2287                     try {
2288                         this.setPrintService(services[i]);
2289                     } catch (PrinterException e) {
2290                     }
2291                     return;
2292                 }
2293             }
2294         }
2295     //** END Functions called by native code for querying/updating attributes
2296 
2297     }
2298 
2299 @SuppressWarnings("serial") // JDK-implementation class
2300 class PrintToFileErrorDialog extends Dialog implements ActionListener{
PrintToFileErrorDialog(Frame parent, String title, String message, String buttonText)2301     public PrintToFileErrorDialog(Frame parent, String title, String message,
2302                            String buttonText) {
2303         super(parent, title, true);
2304         init (parent, title, message, buttonText);
2305     }
2306 
PrintToFileErrorDialog(Dialog parent, String title, String message, String buttonText)2307     public PrintToFileErrorDialog(Dialog parent, String title, String message,
2308                            String buttonText) {
2309         super(parent, title, true);
2310         init (parent, title, message, buttonText);
2311     }
2312 
init(Component parent, String title, String message, String buttonText)2313     private void init(Component parent, String  title, String message,
2314                       String buttonText) {
2315         Panel p = new Panel();
2316         add("Center", new Label(message));
2317         Button btn = new Button(buttonText);
2318         btn.addActionListener(this);
2319         p.add(btn);
2320         add("South", p);
2321         pack();
2322 
2323         Dimension dDim = getSize();
2324         if (parent != null) {
2325             Rectangle fRect = parent.getBounds();
2326             setLocation(fRect.x + ((fRect.width - dDim.width) / 2),
2327                         fRect.y + ((fRect.height - dDim.height) / 2));
2328         }
2329     }
2330 
2331     @Override
actionPerformed(ActionEvent event)2332     public void actionPerformed(ActionEvent event) {
2333         setVisible(false);
2334         dispose();
2335         return;
2336     }
2337 }
2338 
2339 
2340 
2341 
2342     /**
2343      * Initialize JNI field and method ids
2344      */
initIDs()2345     private static native void initIDs();
2346 
2347 }
2348