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