History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/print/RasterPrinterJob.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/print/RasterPrinterJob.java+2283−0
@@ -0,0 +1,2283 @@
1+/*
2+ * Copyright (c) 1998, 2008, 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.print;
27+
28+import java.io.FilePermission;
29+
30+import java.awt.Color;
31+import java.awt.Dialog;
32+import java.awt.Frame;
33+import java.awt.Graphics;
34+import java.awt.Graphics2D;
35+import java.awt.GraphicsConfiguration;
36+import java.awt.GraphicsEnvironment;
37+import java.awt.HeadlessException;
38+import java.awt.KeyboardFocusManager;
39+import java.awt.Rectangle;
40+import java.awt.Shape;
41+import java.awt.geom.AffineTransform;
42+import java.awt.geom.Area;
43+import java.awt.geom.Point2D;
44+import java.awt.geom.Rectangle2D;
45+import java.awt.image.BufferedImage;
46+import java.awt.print.Book;
47+import java.awt.print.Pageable;
48+import java.awt.print.PageFormat;
49+import java.awt.print.Paper;
50+import java.awt.print.Printable;
51+import java.awt.print.PrinterAbortException;
52+import java.awt.print.PrinterException;
53+import java.awt.print.PrinterJob;
54+import java.awt.Window;
55+import java.io.File;
56+import java.io.IOException;
57+import java.util.ArrayList;
58+import java.util.Enumeration;
59+import java.util.Locale;
60+import sun.awt.image.ByteInterleavedRaster;
61+
62+import javax.print.Doc;
63+import javax.print.DocFlavor;
64+import javax.print.DocPrintJob;
65+import javax.print.PrintException;
66+import javax.print.PrintService;
67+import javax.print.PrintServiceLookup;
68+import javax.print.ServiceUI;
69+import javax.print.StreamPrintService;
70+import javax.print.StreamPrintServiceFactory;
71+import javax.print.attribute.Attribute;
72+import javax.print.attribute.AttributeSet;
73+import javax.print.attribute.HashPrintRequestAttributeSet;
74+import javax.print.attribute.PrintRequestAttributeSet;
75+import javax.print.attribute.Size2DSyntax;
76+import javax.print.attribute.standard.Chromaticity;
77+import javax.print.attribute.standard.Copies;
78+import javax.print.attribute.standard.Destination;
79+import javax.print.attribute.standard.DialogTypeSelection;
80+import javax.print.attribute.standard.Fidelity;
81+import javax.print.attribute.standard.JobName;
82+import javax.print.attribute.standard.JobSheets;
83+import javax.print.attribute.standard.Media;
84+import javax.print.attribute.standard.MediaPrintableArea;
85+import javax.print.attribute.standard.MediaSize;
86+import javax.print.attribute.standard.MediaSizeName;
87+import javax.print.attribute.standard.OrientationRequested;
88+import javax.print.attribute.standard.PageRanges;
89+import javax.print.attribute.standard.PrinterState;
90+import javax.print.attribute.standard.PrinterStateReason;
91+import javax.print.attribute.standard.PrinterStateReasons;
92+import javax.print.attribute.standard.PrinterIsAcceptingJobs;
93+import javax.print.attribute.standard.RequestingUserName;
94+import javax.print.attribute.standard.SheetCollate;
95+import javax.print.attribute.standard.Sides;
96+
97+import sun.print.PageableDoc;
98+import sun.print.ServiceDialog;
99+import sun.print.SunPrinterJobService;
100+import sun.print.SunPageSelection;
101+
102+/**
103+ * A class which rasterizes a printer job.
104+ *
105+ * @author Richard Blanchard
106+ */
107+public abstract class RasterPrinterJob extends PrinterJob {
108+
109+ /* Class Constants */
110+
111+ /* Printer destination type. */
112+ protected static final int PRINTER = 0;
113+
114+ /* File destination type. */
115+ protected static final int FILE = 1;
116+
117+ /* Stream destination type. */
118+ protected static final int STREAM = 2;
119+
120+ /**
121+ * Maximum amount of memory in bytes to use for the
122+ * buffered image "band". 4Mb is a compromise between
123+ * limiting the number of bands on hi-res printers and
124+ * not using too much of the Java heap or causing paging
125+ * on systems with little RAM.
126+ */
127+ private static final int MAX_BAND_SIZE = (1024 * 1024 * 4);
128+
129+ /* Dots Per Inch */
130+ private static final float DPI = 72.0f;
131+
132+ /**
133+ * Useful mainly for debugging, this system property
134+ * can be used to force the printing code to print
135+ * using a particular pipeline. The two currently
136+ * supported values are FORCE_RASTER and FORCE_PDL.
137+ */
138+ private static final String FORCE_PIPE_PROP = "sun.java2d.print.pipeline";
139+
140+ /**
141+ * When the system property FORCE_PIPE_PROP has this value
142+ * then each page of a print job will be rendered through
143+ * the raster pipeline.
144+ */
145+ private static final String FORCE_RASTER = "raster";
146+
147+ /**
148+ * When the system property FORCE_PIPE_PROP has this value
149+ * then each page of a print job will be rendered through
150+ * the PDL pipeline.
151+ */
152+ private static final String FORCE_PDL = "pdl";
153+
154+ /**
155+ * When the system property SHAPE_TEXT_PROP has this value
156+ * then text is always rendered as a shape, and no attempt is made
157+ * to match the font through GDI
158+ */
159+ private static final String SHAPE_TEXT_PROP = "sun.java2d.print.shapetext";
160+
161+ /**
162+ * values obtained from System properties in static initialiser block
163+ */
164+ public static boolean forcePDL = false;
165+ public static boolean forceRaster = false;
166+ public static boolean shapeTextProp = false;
167+
168+ static {
169+ /* The system property FORCE_PIPE_PROP
170+ * can be used to force the printing code to
171+ * use a particular pipeline. Either the raster
172+ * pipeline or the pdl pipeline can be forced.
173+ */
174+ String forceStr =
175+ (String)java.security.AccessController.doPrivileged(
176+ new sun.security.action.GetPropertyAction(FORCE_PIPE_PROP));
177+
178+ if (forceStr != null) {
179+ if (forceStr.equalsIgnoreCase(FORCE_PDL)) {
180+ forcePDL = true;
181+ } else if (forceStr.equalsIgnoreCase(FORCE_RASTER)) {
182+ forceRaster = true;
183+ }
184+ }
185+
186+ String shapeTextStr =
187+ (String)java.security.AccessController.doPrivileged(
188+ new sun.security.action.GetPropertyAction(SHAPE_TEXT_PROP));
189+
190+ if (shapeTextStr != null) {
191+ shapeTextProp = true;
192+ }
193+ }
194+
195+ /* Instance Variables */
196+
197+ /**
198+ * Used to minimise GC & reallocation of band when printing
199+ */
200+ private int cachedBandWidth = 0;
201+ private int cachedBandHeight = 0;
202+ private BufferedImage cachedBand = null;
203+
204+ /**
205+ * The number of book copies to be printed.
206+ */
207+ private int mNumCopies = 1;
208+
209+ /**
210+ * Collation effects the order of the pages printed
211+ * when multiple copies are requested. For two copies
212+ * of a three page document the page order is:
213+ * mCollate true: 1, 2, 3, 1, 2, 3
214+ * mCollate false: 1, 1, 2, 2, 3, 3
215+ */
216+ private boolean mCollate = false;
217+
218+ /**
219+ * The zero based indices of the first and last
220+ * pages to be printed. If 'mFirstPage' is
221+ * UNDEFINED_PAGE_NUM then the first page to
222+ * be printed is page 0. If 'mLastPage' is
223+ * UNDEFINED_PAGE_NUM then the last page to
224+ * be printed is the last one in the book.
225+ */
226+ private int mFirstPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;
227+ private int mLastPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;
228+
229+ /**
230+ * The previous print stream Paper
231+ * Used to check if the paper size has changed such that the
232+ * implementation needs to emit the new paper size information
233+ * into the print stream.
234+ * Since we do our own rotation, and the margins aren't relevant,
235+ * Its strictly the dimensions of the paper that we will check.
236+ */
237+ private Paper previousPaper;
238+
239+ /**
240+ * The document to be printed. It is initialized to an
241+ * empty (zero pages) book.
242+ */
243+ private Pageable mDocument = new Book();
244+
245+ /**
246+ * The name of the job being printed.
247+ */
248+ private String mDocName = "Java Printing";
249+
250+
251+ /**
252+ * Printing cancellation flags
253+ */
254+ private boolean performingPrinting = false;
255+ private boolean userCancelled = false;
256+
257+ /**
258+ * Print to file permission variables.
259+ */
260+ private FilePermission printToFilePermission;
261+
262+ /**
263+ * List of areas & the graphics state for redrawing
264+ */
265+ private ArrayList redrawList = new ArrayList();
266+
267+
268+ /* variables representing values extracted from an attribute set.
269+ * These take precedence over values set on a printer job
270+ */
271+ private int copiesAttr;
272+ private String jobNameAttr;
273+ private String userNameAttr;
274+ private PageRanges pageRangesAttr;
275+ protected Sides sidesAttr;
276+ protected String destinationAttr;
277+ protected boolean noJobSheet = false;
278+ protected int mDestType = RasterPrinterJob.FILE;
279+ protected String mDestination = "";
280+ protected boolean collateAttReq = false;
281+
282+ /**
283+ * Device rotation flag, if it support 270, this is set to true;
284+ */
285+ protected boolean landscapeRotates270 = false;
286+
287+ /**
288+ * attributes used by no-args page and print dialog and print method to
289+ * communicate state
290+ */
291+ protected PrintRequestAttributeSet attributes = null;
292+
293+ /**
294+ * Class to keep state information for redrawing areas
295+ * "region" is an area at as a high a resolution as possible.
296+ * The redrawing code needs to look at sx, sy to calculate the scale
297+ * to device resolution.
298+ */
299+ private class GraphicsState {
300+ Rectangle2D region; // Area of page to repaint
301+ Shape theClip; // image drawing clip.
302+ AffineTransform theTransform; // to transform clip to dev coords.
303+ double sx; // X scale from region to device resolution
304+ double sy; // Y scale from region to device resolution
305+ }
306+
307+ /**
308+ * Service for this job
309+ */
310+ protected PrintService myService;
311+
312+ /* Constructors */
313+
314+ public RasterPrinterJob()
315+ {
316+ }
317+
318+/* Abstract Methods */
319+
320+ /**
321+ * Returns the resolution in dots per inch across the width
322+ * of the page.
323+ */
324+ abstract protected double getXRes();
325+
326+ /**
327+ * Returns the resolution in dots per inch down the height
328+ * of the page.
329+ */
330+ abstract protected double getYRes();
331+
332+ /**
333+ * Must be obtained from the current printer.
334+ * Value is in device pixels.
335+ * Not adjusted for orientation of the paper.
336+ */
337+ abstract protected double getPhysicalPrintableX(Paper p);
338+
339+ /**
340+ * Must be obtained from the current printer.
341+ * Value is in device pixels.
342+ * Not adjusted for orientation of the paper.
343+ */
344+ abstract protected double getPhysicalPrintableY(Paper p);
345+
346+ /**
347+ * Must be obtained from the current printer.
348+ * Value is in device pixels.
349+ * Not adjusted for orientation of the paper.
350+ */
351+ abstract protected double getPhysicalPrintableWidth(Paper p);
352+
353+ /**
354+ * Must be obtained from the current printer.
355+ * Value is in device pixels.
356+ * Not adjusted for orientation of the paper.
357+ */
358+ abstract protected double getPhysicalPrintableHeight(Paper p);
359+
360+ /**
361+ * Must be obtained from the current printer.
362+ * Value is in device pixels.
363+ * Not adjusted for orientation of the paper.
364+ */
365+ abstract protected double getPhysicalPageWidth(Paper p);
366+
367+ /**
368+ * Must be obtained from the current printer.
369+ * Value is in device pixels.
370+ * Not adjusted for orientation of the paper.
371+ */
372+ abstract protected double getPhysicalPageHeight(Paper p);
373+
374+ /**
375+ * Begin a new page.
376+ */
377+ abstract protected void startPage(PageFormat format, Printable painter,
378+ int index, boolean paperChanged)
379+ throws PrinterException;
380+
381+ /**
382+ * End a page.
383+ */
384+ abstract protected void endPage(PageFormat format, Printable painter,
385+ int index)
386+ throws PrinterException;
387+
388+ /**
389+ * Prints the contents of the array of ints, 'data'
390+ * to the current page. The band is placed at the
391+ * location (x, y) in device coordinates on the
392+ * page. The width and height of the band is
393+ * specified by the caller.
394+ */
395+ abstract protected void printBand(byte[] data, int x, int y,
396+ int width, int height)
397+ throws PrinterException;
398+
399+/* Instance Methods */
400+
401+ /**
402+ * save graphics state of a PathGraphics for later redrawing
403+ * of part of page represented by the region in that state
404+ */
405+
406+ public void saveState(AffineTransform at, Shape clip,
407+ Rectangle2D region, double sx, double sy) {
408+ GraphicsState gstate = new GraphicsState();
409+ gstate.theTransform = at;
410+ gstate.theClip = clip;
411+ gstate.region = region;
412+ gstate.sx = sx;
413+ gstate.sy = sy;
414+ redrawList.add(gstate);
415+ }
416+
417+
418+ /*
419+ * A convenience method which returns the default service
420+ * for 2D <code>PrinterJob</code>s.
421+ * May return null if there is no suitable default (although there
422+ * may still be 2D services available).
423+ * @return default 2D print service, or null.
424+ * @since 1.4
425+ */
426+ protected static PrintService lookupDefaultPrintService() {
427+ PrintService service = PrintServiceLookup.lookupDefaultPrintService();
428+
429+ /* Pageable implies Printable so checking both isn't strictly needed */
430+ if (service != null &&
431+ service.isDocFlavorSupported(
432+ DocFlavor.SERVICE_FORMATTED.PAGEABLE) &&
433+ service.isDocFlavorSupported(
434+ DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
435+ return service;
436+ } else {
437+ PrintService []services =
438+ PrintServiceLookup.lookupPrintServices(
439+ DocFlavor.SERVICE_FORMATTED.PAGEABLE, null);
440+ if (services.length > 0) {
441+ return services[0];
442+ }
443+ }
444+ return null;
445+ }
446+
447+ /**
448+ * Returns the service (printer) for this printer job.
449+ * Implementations of this class which do not support print services
450+ * may return null;
451+ * @return the service for this printer job.
452+ *
453+ */
454+ public PrintService getPrintService() {
455+ if (myService == null) {
456+ PrintService svc = PrintServiceLookup.lookupDefaultPrintService();
457+ if (svc != null &&
458+ svc.isDocFlavorSupported(
459+ DocFlavor.SERVICE_FORMATTED.PAGEABLE)) {
460+ try {
461+ setPrintService(svc);
462+ myService = svc;
463+ } catch (PrinterException e) {
464+ }
465+ }
466+ if (myService == null) {
467+ PrintService[] svcs = PrintServiceLookup.lookupPrintServices(
468+ DocFlavor.SERVICE_FORMATTED.PAGEABLE, null);
469+ if (svcs.length > 0) {
470+ try {
471+ setPrintService(svcs[0]);
472+ myService = svcs[0];
473+ } catch (PrinterException e) {
474+ }
475+ }
476+ }
477+ }
478+ return myService;
479+ }
480+
481+ /**
482+ * Associate this PrinterJob with a new PrintService.
483+ *
484+ * Throws <code>PrinterException</code> if the specified service
485+ * cannot support the <code>Pageable</code> and
486+ * <code>Printable</code> interfaces necessary to support 2D printing.
487+ * @param a print service which supports 2D printing.
488+ *
489+ * @throws PrinterException if the specified service does not support
490+ * 2D printing or no longer available.
491+ */
492+ public void setPrintService(PrintService service)
493+ throws PrinterException {
494+ if (service == null) {
495+ throw new PrinterException("Service cannot be null");
496+ } else if (!(service instanceof StreamPrintService) &&
497+ service.getName() == null) {
498+ throw new PrinterException("Null PrintService name.");
499+ } else {
500+ // Check the list of services. This service may have been
501+ // deleted already
502+ PrinterState prnState = (PrinterState)service.getAttribute(
503+ PrinterState.class);
504+ if (prnState == PrinterState.STOPPED) {
505+ PrinterStateReasons prnStateReasons =
506+ (PrinterStateReasons)service.getAttribute(
507+ PrinterStateReasons.class);
508+ if ((prnStateReasons != null) &&
509+ (prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN)))
510+ {
511+ throw new PrinterException("PrintService is no longer available.");
512+ }
513+ }
514+
515+
516+ if (service.isDocFlavorSupported(
517+ DocFlavor.SERVICE_FORMATTED.PAGEABLE) &&
518+ service.isDocFlavorSupported(
519+ DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {
520+ myService = service;
521+ } else {
522+ throw new PrinterException("Not a 2D print service: " + service);
523+ }
524+ }
525+ }
526+
527+
528+ protected void updatePageAttributes(PrintService service,
529+ PageFormat page) {
530+ if (service == null || page == null) {
531+ return;
532+ }
533+
534+ float x = (float)Math.rint(
535+ (page.getPaper().getWidth()*Size2DSyntax.INCH)/
536+ (72.0))/(float)Size2DSyntax.INCH;
537+ float y = (float)Math.rint(
538+ (page.getPaper().getHeight()*Size2DSyntax.INCH)/
539+ (72.0))/(float)Size2DSyntax.INCH;
540+
541+ // We should limit the list where we search the matching
542+ // media, this will prevent mapping to wrong media ex. Ledger
543+ // can be mapped to B. Especially useful when creating
544+ // custom MediaSize.
545+ Media[] mediaList = (Media[])service.getSupportedAttributeValues(
546+ Media.class, null, null);
547+ Media media = null;
548+ try {
549+ media = CustomMediaSizeName.findMedia(mediaList, x, y,
550+ Size2DSyntax.INCH);
551+ } catch (IllegalArgumentException iae) {
552+ }
553+ if ((media == null) ||
554+ !(service.isAttributeValueSupported(media, null, null))) {
555+ media = (Media)service.getDefaultAttributeValue(Media.class);
556+ }
557+
558+ OrientationRequested orient;
559+ switch (page.getOrientation()) {
560+ case PageFormat.LANDSCAPE :
561+ orient = OrientationRequested.LANDSCAPE;
562+ break;
563+ case PageFormat.REVERSE_LANDSCAPE:
564+ orient = OrientationRequested.REVERSE_LANDSCAPE;
565+ break;
566+ default:
567+ orient = OrientationRequested.PORTRAIT;
568+ }
569+
570+ if (attributes == null) {
571+ attributes = new HashPrintRequestAttributeSet();
572+ }
573+ if (media != null) {
574+ attributes.add(media);
575+ }
576+ attributes.add(orient);
577+
578+ float ix = (float)(page.getPaper().getImageableX()/DPI);
579+ float iw = (float)(page.getPaper().getImageableWidth()/DPI);
580+ float iy = (float)(page.getPaper().getImageableY()/DPI);
581+ float ih = (float)(page.getPaper().getImageableHeight()/DPI);
582+ if (ix < 0) ix = 0f; if (iy < 0) iy = 0f;
583+ try {
584+ attributes.add(new MediaPrintableArea(ix, iy, iw, ih,
585+ MediaPrintableArea.INCH));
586+ } catch (IllegalArgumentException iae) {
587+ }
588+ }
589+
590+ /**
591+ * Display a dialog to the user allowing the modification of a
592+ * PageFormat instance.
593+ * The <code>page</code> argument is used to initialize controls
594+ * in the page setup dialog.
595+ * If the user cancels the dialog, then the method returns the
596+ * original <code>page</code> object unmodified.
597+ * If the user okays the dialog then the method returns a new
598+ * PageFormat object with the indicated changes.
599+ * In either case the original <code>page</code> object will
600+ * not be modified.
601+ * @param page the default PageFormat presented to the user
602+ * for modification
603+ * @return the original <code>page</code> object if the dialog
604+ * is cancelled, or a new PageFormat object containing
605+ * the format indicated by the user if the dialog is
606+ * acknowledged
607+ * @exception HeadlessException if GraphicsEnvironment.isHeadless()
608+ * returns true.
609+ * @see java.awt.GraphicsEnvironment#isHeadless
610+ * @since 1.2
611+ */
612+ public PageFormat pageDialog(PageFormat page)
613+ throws HeadlessException {
614+ if (GraphicsEnvironment.isHeadless()) {
615+ throw new HeadlessException();
616+ }
617+
618+ final GraphicsConfiguration gc =
619+ GraphicsEnvironment.getLocalGraphicsEnvironment().
620+ getDefaultScreenDevice().getDefaultConfiguration();
621+
622+ PrintService service =
623+ (PrintService)java.security.AccessController.doPrivileged(
624+ new java.security.PrivilegedAction() {
625+ public Object run() {
626+ PrintService service = getPrintService();
627+ if (service == null) {
628+ ServiceDialog.showNoPrintService(gc);
629+ return null;
630+ }
631+ return service;
632+ }
633+ });
634+
635+ if (service == null) {
636+ return page;
637+ }
638+ updatePageAttributes(service, page);
639+
640+ PageFormat newPage = pageDialog(attributes);
641+
642+ if (newPage == null) {
643+ return page;
644+ } else {
645+ return newPage;
646+ }
647+ }
648+
649+ /**
650+ * return a PageFormat corresponding to the updated attributes,
651+ * or null if the user cancelled the dialog.
652+ */
653+ public PageFormat pageDialog(final PrintRequestAttributeSet attributes)
654+ throws HeadlessException {
655+ if (GraphicsEnvironment.isHeadless()) {
656+ throw new HeadlessException();
657+ }
658+
659+ final GraphicsConfiguration gc =
660+ GraphicsEnvironment.getLocalGraphicsEnvironment().
661+ getDefaultScreenDevice().getDefaultConfiguration();
662+ Rectangle bounds = gc.getBounds();
663+ int x = bounds.x+bounds.width/3;
664+ int y = bounds.y+bounds.height/3;
665+
666+ PrintService service =
667+ (PrintService)java.security.AccessController.doPrivileged(
668+ new java.security.PrivilegedAction() {
669+ public Object run() {
670+ PrintService service = getPrintService();
671+ if (service == null) {
672+ ServiceDialog.showNoPrintService(gc);
673+ return null;
674+ }
675+ return service;
676+ }
677+ });
678+
679+ if (service == null) {
680+ return null;
681+ }
682+
683+ ServiceDialog pageDialog = new ServiceDialog(gc, x, y, service,
684+ DocFlavor.SERVICE_FORMATTED.PAGEABLE,
685+ attributes, (Frame)null);
686+ pageDialog.show();
687+
688+ if (pageDialog.getStatus() == ServiceDialog.APPROVE) {
689+ PrintRequestAttributeSet newas =
690+ pageDialog.getAttributes();
691+ Class amCategory = SunAlternateMedia.class;
692+
693+ if (attributes.containsKey(amCategory) &&
694+ !newas.containsKey(amCategory)) {
695+ attributes.remove(amCategory);
696+ }
697+ attributes.addAll(newas);
698+
699+ PageFormat page = defaultPage();
700+
701+ OrientationRequested orient =
702+ (OrientationRequested)
703+ attributes.get(OrientationRequested.class);
704+ int pfOrient = PageFormat.PORTRAIT;
705+ if (orient != null) {
706+ if (orient == OrientationRequested.REVERSE_LANDSCAPE) {
707+ pfOrient = PageFormat.REVERSE_LANDSCAPE;
708+ } else if (orient == OrientationRequested.LANDSCAPE) {
709+ pfOrient = PageFormat.LANDSCAPE;
710+ }
711+ }
712+ page.setOrientation(pfOrient);
713+
714+ Media media = (Media)attributes.get(Media.class);
715+ if (media == null) {
716+ media =
717+ (Media)service.getDefaultAttributeValue(Media.class);
718+ }
719+ if (!(media instanceof MediaSizeName)) {
720+ media = MediaSizeName.NA_LETTER;
721+ }
722+ MediaSize size =
723+ MediaSize.getMediaSizeForName((MediaSizeName)media);
724+ if (size == null) {
725+ size = MediaSize.NA.LETTER;
726+ }
727+ Paper paper = new Paper();
728+ float dim[] = size.getSize(1); //units == 1 to avoid FP error
729+ double w = Math.rint((dim[0]*72.0)/Size2DSyntax.INCH);
730+ double h = Math.rint((dim[1]*72.0)/Size2DSyntax.INCH);
731+ paper.setSize(w, h);
732+ MediaPrintableArea area =
733+ (MediaPrintableArea)
734+ attributes.get(MediaPrintableArea.class);
735+ double ix, iw, iy, ih;
736+
737+ if (area != null) {
738+ // Should pass in same unit as updatePageAttributes
739+ // to avoid rounding off errors.
740+ ix = Math.rint(
741+ area.getX(MediaPrintableArea.INCH) * DPI);
742+ iy = Math.rint(
743+ area.getY(MediaPrintableArea.INCH) * DPI);
744+ iw = Math.rint(
745+ area.getWidth(MediaPrintableArea.INCH) * DPI);
746+ ih = Math.rint(
747+ area.getHeight(MediaPrintableArea.INCH) * DPI);
748+ }
749+ else {
750+ if (w >= 72.0 * 6.0) {
751+ ix = 72.0;
752+ iw = w - 2 * 72.0;
753+ } else {
754+ ix = w / 6.0;
755+ iw = w * 0.75;
756+ }
757+ if (h >= 72.0 * 6.0) {
758+ iy = 72.0;
759+ ih = h - 2 * 72.0;
760+ } else {
761+ iy = h / 6.0;
762+ ih = h * 0.75;
763+ }
764+ }
765+ paper.setImageableArea(ix, iy, iw, ih);
766+ page.setPaper(paper);
767+
768+ return page;
769+ } else {
770+ return null;
771+ }
772+ }
773+
774+ /**
775+ * Presents the user a dialog for changing properties of the
776+ * print job interactively.
777+ * The services browsable here are determined by the type of
778+ * service currently installed.
779+ * If the application installed a StreamPrintService on this
780+ * PrinterJob, only the available StreamPrintService (factories) are
781+ * browsable.
782+ *
783+ * @param attributes to store changed properties.
784+ * @return false if the user cancels the dialog and true otherwise.
785+ * @exception HeadlessException if GraphicsEnvironment.isHeadless()
786+ * returns true.
787+ * @see java.awt.GraphicsEnvironment#isHeadless
788+ */
789+ public boolean printDialog(final PrintRequestAttributeSet attributes)
790+ throws HeadlessException {
791+ if (GraphicsEnvironment.isHeadless()) {
792+ throw new HeadlessException();
793+ }
794+
795+
796+ DialogTypeSelection dlg =
797+ (DialogTypeSelection)attributes.get(DialogTypeSelection.class);
798+
799+ // Check for native, note that default dialog is COMMON.
800+ if (dlg == DialogTypeSelection.NATIVE) {
801+ this.attributes = attributes;
802+ try {
803+ debug_println("calling setAttributes in printDialog");
804+ setAttributes(attributes);
805+
806+ } catch (PrinterException e) {
807+
808+ }
809+
810+ boolean ret = printDialog();
811+ this.attributes = attributes;
812+ return ret;
813+
814+ }
815+
816+
817+ /* A security check has already been performed in the
818+ * java.awt.print.printerJob.getPrinterJob method.
819+ * So by the time we get here, it is OK for the current thread
820+ * to print either to a file (from a Dialog we control!) or
821+ * to a chosen printer.
822+ *
823+ * We raise privilege when we put up the dialog, to avoid
824+ * the "warning applet window" banner.
825+ */
826+ final GraphicsConfiguration gc =
827+ GraphicsEnvironment.getLocalGraphicsEnvironment().
828+ getDefaultScreenDevice().getDefaultConfiguration();
829+
830+ PrintService service =
831+ (PrintService)java.security.AccessController.doPrivileged(
832+ new java.security.PrivilegedAction() {
833+ public Object run() {
834+ PrintService service = getPrintService();
835+ if (service == null) {
836+ ServiceDialog.showNoPrintService(gc);
837+ return null;
838+ }
839+ return service;
840+ }
841+ });
842+
843+ if (service == null) {
844+ return false;
845+ }
846+
847+ PrintService[] services;
848+ StreamPrintServiceFactory[] spsFactories = null;
849+ if (service instanceof StreamPrintService) {
850+ spsFactories = lookupStreamPrintServices(null);
851+ services = new StreamPrintService[spsFactories.length];
852+ for (int i=0; i<spsFactories.length; i++) {
853+ services[i] = spsFactories[i].getPrintService(null);
854+ }
855+ } else {
856+ services =
857+ (PrintService[])java.security.AccessController.doPrivileged(
858+ new java.security.PrivilegedAction() {
859+ public Object run() {
860+ PrintService[] services = PrinterJob.lookupPrintServices();
861+ return services;
862+ }
863+ });
864+
865+ if ((services == null) || (services.length == 0)) {
866+ /*
867+ * No services but default PrintService exists?
868+ * Create services using defaultService.
869+ */
870+ services = new PrintService[1];
871+ services[0] = service;
872+ }
873+ }
874+
875+ Rectangle bounds = gc.getBounds();
876+ int x = bounds.x+bounds.width/3;
877+ int y = bounds.y+bounds.height/3;
878+ PrintService newService;
879+ try {
880+ newService =
881+ ServiceUI.printDialog(gc, x, y,
882+ services, service,
883+ DocFlavor.SERVICE_FORMATTED.PAGEABLE,
884+ attributes);
885+ } catch (IllegalArgumentException iae) {
886+ newService = ServiceUI.printDialog(gc, x, y,
887+ services, services[0],
888+ DocFlavor.SERVICE_FORMATTED.PAGEABLE,
889+ attributes);
890+ }
891+
892+ if (newService == null) {
893+ return false;
894+ }
895+
896+ if (!service.equals(newService)) {
897+ try {
898+ setPrintService(newService);
899+ } catch (PrinterException e) {
900+ /*
901+ * The only time it would throw an exception is when
902+ * newService is no longer available but we should still
903+ * select this printer.
904+ */
905+ myService = newService;
906+ }
907+ }
908+ return true;
909+ }
910+
911+ /**
912+ * Presents the user a dialog for changing properties of the
913+ * print job interactively.
914+ * @returns false if the user cancels the dialog and
915+ * true otherwise.
916+ * @exception HeadlessException if GraphicsEnvironment.isHeadless()
917+ * returns true.
918+ * @see java.awt.GraphicsEnvironment#isHeadless
919+ */
920+ public boolean printDialog() throws HeadlessException {
921+
922+ if (GraphicsEnvironment.isHeadless()) {
923+ throw new HeadlessException();
924+ }
925+
926+ PrintRequestAttributeSet attributes =
927+ new HashPrintRequestAttributeSet();
928+ attributes.add(new Copies(getCopies()));
929+ attributes.add(new JobName(getJobName(), null));
930+ boolean doPrint = printDialog(attributes);
931+ if (doPrint) {
932+ JobName jobName = (JobName)attributes.get(JobName.class);
933+ if (jobName != null) {
934+ setJobName(jobName.getValue());
935+ }
936+ Copies copies = (Copies)attributes.get(Copies.class);
937+ if (copies != null) {
938+ setCopies(copies.getValue());
939+ }
940+
941+ Destination dest = (Destination)attributes.get(Destination.class);
942+
943+ if (dest != null) {
944+ try {
945+ mDestType = RasterPrinterJob.FILE;
946+ mDestination = (new File(dest.getURI())).getPath();
947+ } catch (Exception e) {
948+ mDestination = "out.prn";
949+ PrintService ps = getPrintService();
950+ if (ps != null) {
951+ Destination defaultDest = (Destination)ps.
952+ getDefaultAttributeValue(Destination.class);
953+ if (defaultDest != null) {
954+ mDestination = (new File(defaultDest.getURI())).getPath();
955+ }
956+ }
957+ }
958+ } else {
959+ mDestType = RasterPrinterJob.PRINTER;
960+ PrintService ps = getPrintService();
961+ if (ps != null) {
962+ mDestination = ps.getName();
963+ }
964+ }
965+ }
966+
967+ return doPrint;
968+ }
969+
970+ /**
971+ * The pages in the document to be printed by this PrinterJob
972+ * are drawn by the Printable object 'painter'. The PageFormat
973+ * for each page is the default page format.
974+ * @param Printable Called to render each page of the document.
975+ */
976+ public void setPrintable(Printable painter) {
977+ setPageable(new OpenBook(defaultPage(new PageFormat()), painter));
978+ }
979+
980+ /**
981+ * The pages in the document to be printed by this PrinterJob
982+ * are drawn by the Printable object 'painter'. The PageFormat
983+ * of each page is 'format'.
984+ * @param Printable Called to render each page of the document.
985+ * @param PageFormat The size and orientation of each page to
986+ * be printed.
987+ */
988+ public void setPrintable(Printable painter, PageFormat format) {
989+ setPageable(new OpenBook(format, painter));
990+ updatePageAttributes(getPrintService(), format);
991+ }
992+
993+ /**
994+ * The pages in the document to be printed are held by the
995+ * Pageable instance 'document'. 'document' will be queried
996+ * for the number of pages as well as the PageFormat and
997+ * Printable for each page.
998+ * @param Pageable The document to be printed. It may not be null.
999+ * @exception NullPointerException the Pageable passed in was null.
1000+ * @see PageFormat
1001+ * @see Printable
1002+ */
1003+ public void setPageable(Pageable document) throws NullPointerException {
1004+ if (document != null) {
1005+ mDocument = document;
1006+
1007+ } else {
1008+ throw new NullPointerException();
1009+ }
1010+ }
1011+
1012+ protected void initPrinter() {
1013+ return;
1014+ }
1015+
1016+ protected boolean isSupportedValue(Attribute attrval,
1017+ PrintRequestAttributeSet attrset) {
1018+ PrintService ps = getPrintService();
1019+ return
1020+ (attrval != null && ps != null &&
1021+ ps.isAttributeValueSupported(attrval,
1022+ DocFlavor.SERVICE_FORMATTED.PAGEABLE,
1023+ attrset));
1024+ }
1025+
1026+ /* subclasses may need to pull extra information out of the attribute set
1027+ * They can override this method & call super.setAttributes()
1028+ */
1029+ protected void setAttributes(PrintRequestAttributeSet attributes)
1030+ throws PrinterException {
1031+ /* reset all values to defaults */
1032+ setCollated(false);
1033+ sidesAttr = null;
1034+ pageRangesAttr = null;
1035+ copiesAttr = 0;
1036+ jobNameAttr = null;
1037+ userNameAttr = null;
1038+ destinationAttr = null;
1039+ collateAttReq = false;
1040+
1041+ PrintService service = getPrintService();
1042+ if (attributes == null || service == null) {
1043+ return;
1044+ }
1045+
1046+ boolean fidelity = false;
1047+ Fidelity attrFidelity = (Fidelity)attributes.get(Fidelity.class);
1048+ if (attrFidelity != null && attrFidelity == Fidelity.FIDELITY_TRUE) {
1049+ fidelity = true;
1050+ }
1051+
1052+ if (fidelity == true) {
1053+ AttributeSet unsupported =
1054+ service.getUnsupportedAttributes(
1055+ DocFlavor.SERVICE_FORMATTED.PAGEABLE,
1056+ attributes);
1057+ if (unsupported != null) {
1058+ throw new PrinterException("Fidelity cannot be satisfied");
1059+ }
1060+ }
1061+
1062+ /*
1063+ * Since we have verified supported values if fidelity is true,
1064+ * we can either ignore unsupported values, or substitute a
1065+ * reasonable alternative
1066+ */
1067+
1068+ SheetCollate collateAttr =
1069+ (SheetCollate)attributes.get(SheetCollate.class);
1070+ if (isSupportedValue(collateAttr, attributes)) {
1071+ setCollated(collateAttr == SheetCollate.COLLATED);
1072+ }
1073+
1074+ sidesAttr = (Sides)attributes.get(Sides.class);
1075+ if (!isSupportedValue(sidesAttr, attributes)) {
1076+ sidesAttr = Sides.ONE_SIDED;
1077+ }
1078+
1079+ pageRangesAttr = (PageRanges)attributes.get(PageRanges.class);
1080+ if (!isSupportedValue(pageRangesAttr, attributes)) {
1081+ pageRangesAttr = null;
1082+ } else {
1083+ if ((SunPageSelection)attributes.get(SunPageSelection.class)
1084+ == SunPageSelection.RANGE) {
1085+ // get to, from, min, max page ranges
1086+ int[][] range = pageRangesAttr.getMembers();
1087+ // setPageRanges uses 0-based indexing so we subtract 1
1088+ setPageRange(range[0][0] - 1, range[0][1] - 1);
1089+ } else {
1090+ setPageRange(-1, - 1);
1091+ }
1092+ }
1093+
1094+ Copies copies = (Copies)attributes.get(Copies.class);
1095+ if (isSupportedValue(copies, attributes) ||
1096+ (!fidelity && copies != null)) {
1097+ copiesAttr = copies.getValue();
1098+ setCopies(copiesAttr);
1099+ } else {
1100+ copiesAttr = getCopies();
1101+ }
1102+
1103+ Destination destination =
1104+ (Destination)attributes.get(Destination.class);
1105+
1106+ if (isSupportedValue(destination, attributes)) {
1107+ try {
1108+ // Old code (new File(destination.getURI())).getPath()
1109+ // would generate a "URI is not hierarchical" IAE
1110+ // for "file:out.prn" so we use getSchemeSpecificPart instead
1111+ destinationAttr = "" + new File(destination.getURI().
1112+ getSchemeSpecificPart());
1113+ } catch (Exception e) { // paranoid exception
1114+ Destination defaultDest = (Destination)service.
1115+ getDefaultAttributeValue(Destination.class);
1116+ if (defaultDest != null) {
1117+ destinationAttr = "" + new File(defaultDest.getURI().
1118+ getSchemeSpecificPart());
1119+ }
1120+ }
1121+ }
1122+
1123+ JobSheets jobSheets = (JobSheets)attributes.get(JobSheets.class);
1124+ if (jobSheets != null) {
1125+ noJobSheet = jobSheets == JobSheets.NONE;
1126+ }
1127+
1128+ JobName jobName = (JobName)attributes.get(JobName.class);
1129+ if (isSupportedValue(jobName, attributes) ||
1130+ (!fidelity && jobName != null)) {
1131+ jobNameAttr = jobName.getValue();
1132+ setJobName(jobNameAttr);
1133+ } else {
1134+ jobNameAttr = getJobName();
1135+ }
1136+
1137+ RequestingUserName userName =
1138+ (RequestingUserName)attributes.get(RequestingUserName.class);
1139+ if (isSupportedValue(userName, attributes) ||
1140+ (!fidelity && userName != null)) {
1141+ userNameAttr = userName.getValue();
1142+ } else {
1143+ try {
1144+ userNameAttr = getUserName();
1145+ } catch (SecurityException e) {
1146+ userNameAttr = "";
1147+ }
1148+ }
1149+
1150+ /* OpenBook is used internally only when app uses Printable.
1151+ * This is the case when we use the values from the attribute set.
1152+ */
1153+ Media media = (Media)attributes.get(Media.class);
1154+ OrientationRequested orientReq =
1155+ (OrientationRequested)attributes.get(OrientationRequested.class);
1156+ MediaPrintableArea mpa =
1157+ (MediaPrintableArea)attributes.get(MediaPrintableArea.class);
1158+
1159+ if ((orientReq != null || media != null || mpa != null) &&
1160+ getPageable() instanceof OpenBook) {
1161+
1162+ /* We could almost(!) use PrinterJob.getPageFormat() except
1163+ * here we need to start with the PageFormat from the OpenBook :
1164+ */
1165+ Pageable pageable = getPageable();
1166+ Printable printable = pageable.getPrintable(0);
1167+ PageFormat pf = (PageFormat)pageable.getPageFormat(0).clone();
1168+ Paper paper = pf.getPaper();
1169+
1170+ /* If there's a media but no media printable area, we can try
1171+ * to retrieve the default value for mpa and use that.
1172+ */
1173+ if (mpa == null && media != null &&
1174+ service.
1175+ isAttributeCategorySupported(MediaPrintableArea.class)) {
1176+ Object mpaVals = service.
1177+ getSupportedAttributeValues(MediaPrintableArea.class,
1178+ null, attributes);
1179+ if (mpaVals instanceof MediaPrintableArea[] &&
1180+ ((MediaPrintableArea[])mpaVals).length > 0) {
1181+ mpa = ((MediaPrintableArea[])mpaVals)[0];
1182+ }
1183+ }
1184+
1185+ if (isSupportedValue(orientReq, attributes) ||
1186+ (!fidelity && orientReq != null)) {
1187+ int orient;
1188+ if (orientReq.equals(OrientationRequested.REVERSE_LANDSCAPE)) {
1189+ orient = PageFormat.REVERSE_LANDSCAPE;
1190+ } else if (orientReq.equals(OrientationRequested.LANDSCAPE)) {
1191+ orient = PageFormat.LANDSCAPE;
1192+ } else {
1193+ orient = PageFormat.PORTRAIT;
1194+ }
1195+ pf.setOrientation(orient);
1196+ }
1197+
1198+ if (isSupportedValue(media, attributes) ||
1199+ (!fidelity && media != null)) {
1200+ if (media instanceof MediaSizeName) {
1201+ MediaSizeName msn = (MediaSizeName)media;
1202+ MediaSize msz = MediaSize.getMediaSizeForName(msn);
1203+ if (msz != null) {
1204+ float paperWid = msz.getX(MediaSize.INCH) * 72.0f;
1205+ float paperHgt = msz.getY(MediaSize.INCH) * 72.0f;
1206+ paper.setSize(paperWid, paperHgt);
1207+ if (mpa == null) {
1208+ paper.setImageableArea(72.0, 72.0,
1209+ paperWid-144.0,
1210+ paperHgt-144.0);
1211+ }
1212+ }
1213+ }
1214+ }
1215+
1216+ if (isSupportedValue(mpa, attributes) ||
1217+ (!fidelity && mpa != null)) {
1218+ float [] printableArea =
1219+ mpa.getPrintableArea(MediaPrintableArea.INCH);
1220+ for (int i=0; i < printableArea.length; i++) {
1221+ printableArea[i] = printableArea[i]*72.0f;
1222+ }
1223+ paper.setImageableArea(printableArea[0], printableArea[1],
1224+ printableArea[2], printableArea[3]);
1225+ }
1226+
1227+ pf.setPaper(paper);
1228+ pf = validatePage(pf);
1229+ setPrintable(printable, pf);
1230+ } else {
1231+ // for AWT where pageable is not an instance of OpenBook,
1232+ // we need to save paper info
1233+ this.attributes = attributes;
1234+ }
1235+
1236+ }
1237+
1238+ /*
1239+ * Services we don't recognize as built-in services can't be
1240+ * implemented as subclasses of PrinterJob, therefore we create
1241+ * a DocPrintJob from their service and pass a Doc representing
1242+ * the application's printjob
1243+ */
1244+ private void spoolToService(PrintService psvc,
1245+ PrintRequestAttributeSet attributes)
1246+ throws PrinterException {
1247+
1248+ if (psvc == null) {
1249+ throw new PrinterException("No print service found.");
1250+ }
1251+
1252+ DocPrintJob job = psvc.createPrintJob();
1253+ Doc doc = new PageableDoc(getPageable());
1254+ if (attributes == null) {
1255+ attributes = new HashPrintRequestAttributeSet();
1256+ }
1257+ try {
1258+ job.print(doc, attributes);
1259+ } catch (PrintException e) {
1260+ throw new PrinterException(e.toString());
1261+ }
1262+ }
1263+
1264+ /**
1265+ * Prints a set of pages.
1266+ * @exception java.awt.print.PrinterException an error in the print system
1267+ * caused the job to be aborted
1268+ * @see java.awt.print.Book
1269+ * @see java.awt.print.Pageable
1270+ * @see java.awt.print.Printable
1271+ */
1272+ public void print() throws PrinterException {
1273+ print(attributes);
1274+ }
1275+
1276+ public static boolean debugPrint = false;
1277+ protected void debug_println(String str) {
1278+ if (debugPrint) {
1279+ System.out.println("RasterPrinterJob "+str+" "+this);
1280+ }
1281+ }
1282+
1283+ public void print(PrintRequestAttributeSet attributes)
1284+ throws PrinterException {
1285+
1286+ /*
1287+ * In the future PrinterJob will probably always dispatch
1288+ * the print job to the PrintService.
1289+ * This is how third party 2D Print Services will be invoked
1290+ * when applications use the PrinterJob API.
1291+ * However the JRE's concrete PrinterJob implementations have
1292+ * not yet been re-worked to be implemented as standalone
1293+ * services, and are implemented only as subclasses of PrinterJob.
1294+ * So here we dispatch only those services we do not recognize
1295+ * as implemented through platform subclasses of PrinterJob
1296+ * (and this class).
1297+ */
1298+ PrintService psvc = getPrintService();
1299+ debug_println("psvc = "+psvc);
1300+ if (psvc == null) {
1301+ throw new PrinterException("No print service found.");
1302+ }
1303+
1304+ // Check the list of services. This service may have been
1305+ // deleted already
1306+ PrinterState prnState = (PrinterState)psvc.getAttribute(
1307+ PrinterState.class);
1308+ if (prnState == PrinterState.STOPPED) {
1309+ PrinterStateReasons prnStateReasons =
1310+ (PrinterStateReasons)psvc.getAttribute(
1311+ PrinterStateReasons.class);
1312+ if ((prnStateReasons != null) &&
1313+ (prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN)))
1314+ {
1315+ throw new PrinterException("PrintService is no longer available.");
1316+ }
1317+ }
1318+
1319+ if ((PrinterIsAcceptingJobs)(psvc.getAttribute(
1320+ PrinterIsAcceptingJobs.class)) ==
1321+ PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS) {
1322+ throw new PrinterException("Printer is not accepting job.");
1323+ }
1324+
1325+ if ((psvc instanceof SunPrinterJobService) &&
1326+ ((SunPrinterJobService)psvc).usesClass(getClass())) {
1327+ setAttributes(attributes);
1328+ // throw exception for invalid destination
1329+ if (destinationAttr != null) {
1330+ // destinationAttr is null for Destination(new URI(""))
1331+ // because isAttributeValueSupported returns false in setAttributes
1332+
1333+ // Destination(new URI(" ")) throws URISyntaxException
1334+ File f = new File(destinationAttr);
1335+ try {
1336+ // check if this is a new file and if filename chars are valid
1337+ if (f.createNewFile()) {
1338+ f.delete();
1339+ }
1340+ } catch (IOException ioe) {
1341+ throw new PrinterException("Cannot write to file:"+
1342+ destinationAttr);
1343+ } catch (SecurityException se) {
1344+ //There is already file read/write access so at this point
1345+ // only delete access is denied. Just ignore it because in
1346+ // most cases the file created in createNewFile gets overwritten
1347+ // anyway.
1348+ }
1349+
1350+ File pFile = f.getParentFile();
1351+ if ((f.exists() &&
1352+ (!f.isFile() || !f.canWrite())) ||
1353+ ((pFile != null) &&
1354+ (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {
1355+ throw new PrinterException("Cannot write to file:"+
1356+ destinationAttr);
1357+ }
1358+ }
1359+ } else {
1360+ spoolToService(psvc, attributes);
1361+ return;
1362+ }
1363+ /* We need to make sure that the collation and copies
1364+ * settings are initialised */
1365+ initPrinter();
1366+
1367+ int numCollatedCopies = getCollatedCopies();
1368+ int numNonCollatedCopies = getNoncollatedCopies();
1369+ debug_println("getCollatedCopies() "+numCollatedCopies
1370+ + " getNoncollatedCopies() "+ numNonCollatedCopies);
1371+
1372+ /* Get the range of pages we are to print. If the
1373+ * last page to print is unknown, then we print to
1374+ * the end of the document. Note that firstPage
1375+ * and lastPage are 0 based page indices.
1376+ */
1377+ int numPages = mDocument.getNumberOfPages();
1378+ if (numPages == 0) {
1379+ return;
1380+ }
1381+
1382+ int firstPage = getFirstPage();
1383+ int lastPage = getLastPage();
1384+ if(lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES){
1385+ int totalPages = mDocument.getNumberOfPages();
1386+ if (totalPages != Pageable.UNKNOWN_NUMBER_OF_PAGES) {
1387+ lastPage = mDocument.getNumberOfPages() - 1;
1388+ }
1389+ }
1390+
1391+ try {
1392+ synchronized (this) {
1393+ performingPrinting = true;
1394+ userCancelled = false;
1395+ }
1396+
1397+ startDoc();
1398+ if (isCancelled()) {
1399+ cancelDoc();
1400+ }
1401+
1402+ // PageRanges can be set even if RANGE is not selected
1403+ // so we need to check if it is selected.
1404+ boolean rangeIsSelected = true;
1405+ if (attributes != null) {
1406+ SunPageSelection pages =
1407+ (SunPageSelection)attributes.get(SunPageSelection.class);
1408+ if ((pages != null) && (pages != SunPageSelection.RANGE)) {
1409+ rangeIsSelected = false;
1410+ }
1411+ }
1412+
1413+
1414+ debug_println("after startDoc rangeSelected? "+rangeIsSelected
1415+ + " numNonCollatedCopies "+ numNonCollatedCopies);
1416+
1417+
1418+ /* Three nested loops iterate over the document. The outer loop
1419+ * counts the number of collated copies while the inner loop
1420+ * counts the number of nonCollated copies. Normally, one of
1421+ * these two loops will only execute once; that is we will
1422+ * either print collated copies or noncollated copies. The
1423+ * middle loop iterates over the pages.
1424+ * If a PageRanges attribute is used, it constrains the pages
1425+ * that are imaged. If a platform subclass (though a user dialog)
1426+ * requests a page range via setPageRange(). it too can
1427+ * constrain the page ranges that are imaged.
1428+ * It is expected that only one of these will be used in a
1429+ * job but both should be able to co-exist.
1430+ */
1431+ for(int collated = 0; collated < numCollatedCopies; collated++) {
1432+ for(int i = firstPage, pageResult = Printable.PAGE_EXISTS;
1433+ (i <= lastPage ||
1434+ lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES)
1435+ && pageResult == Printable.PAGE_EXISTS;
1436+ i++)
1437+ {
1438+
1439+ if ((pageRangesAttr != null) && rangeIsSelected ){
1440+ int nexti = pageRangesAttr.next(i);
1441+ if (nexti == -1) {
1442+ break;
1443+ } else if (nexti != i+1) {
1444+ continue;
1445+ }
1446+ }
1447+
1448+ for(int nonCollated = 0;
1449+ nonCollated < numNonCollatedCopies
1450+ && pageResult == Printable.PAGE_EXISTS;
1451+ nonCollated++)
1452+ {
1453+ if (isCancelled()) {
1454+ cancelDoc();
1455+ }
1456+ debug_println("printPage "+i);
1457+ pageResult = printPage(mDocument, i);
1458+
1459+ }
1460+ }
1461+ }
1462+
1463+ if (isCancelled()) {
1464+ cancelDoc();
1465+ }
1466+
1467+ } finally {
1468+ // reset previousPaper in case this job is invoked again.
1469+ previousPaper = null;
1470+ synchronized (this) {
1471+ if (performingPrinting) {
1472+ endDoc();
1473+ }
1474+ performingPrinting = false;
1475+ notify();
1476+ }
1477+ }
1478+ }
1479+
1480+ /**
1481+ * updates a Paper object to reflect the current printer's selected
1482+ * paper size and imageable area for that paper size.
1483+ * Default implementation copies settings from the original, applies
1484+ * applies some validity checks, changes them only if they are
1485+ * clearly unreasonable, then sets them into the new Paper.
1486+ * Subclasses are expected to override this method to make more
1487+ * informed decisons.
1488+ */
1489+ protected void validatePaper(Paper origPaper, Paper newPaper) {
1490+ if (origPaper == null || newPaper == null) {
1491+ return;
1492+ } else {
1493+ double wid = origPaper.getWidth();
1494+ double hgt = origPaper.getHeight();
1495+ double ix = origPaper.getImageableX();
1496+ double iy = origPaper.getImageableY();
1497+ double iw = origPaper.getImageableWidth();
1498+ double ih = origPaper.getImageableHeight();
1499+
1500+ /* Assume any +ve values are legal. Overall paper dimensions
1501+ * take precedence. Make sure imageable area fits on the paper.
1502+ */
1503+ Paper defaultPaper = new Paper();
1504+ wid = ((wid > 0.0) ? wid : defaultPaper.getWidth());
1505+ hgt = ((hgt > 0.0) ? hgt : defaultPaper.getHeight());
1506+ ix = ((ix > 0.0) ? ix : defaultPaper.getImageableX());
1507+ iy = ((iy > 0.0) ? iy : defaultPaper.getImageableY());
1508+ iw = ((iw > 0.0) ? iw : defaultPaper.getImageableWidth());
1509+ ih = ((ih > 0.0) ? ih : defaultPaper.getImageableHeight());
1510+ /* full width/height is not likely to be imageable, but since we
1511+ * don't know the limits we have to allow it
1512+ */
1513+ if (iw > wid) {
1514+ iw = wid;
1515+ }
1516+ if (ih > hgt) {
1517+ ih = hgt;
1518+ }
1519+ if ((ix + iw) > wid) {
1520+ ix = wid - iw;
1521+ }
1522+ if ((iy + ih) > hgt) {
1523+ iy = hgt - ih;
1524+ }
1525+ newPaper.setSize(wid, hgt);
1526+ newPaper.setImageableArea(ix, iy, iw, ih);
1527+ }
1528+ }
1529+
1530+ /**
1531+ * The passed in PageFormat will be copied and altered to describe
1532+ * the default page size and orientation of the PrinterJob's
1533+ * current printer.
1534+ * Platform subclasses which can access the actual default paper size
1535+ * for a printer may override this method.
1536+ */
1537+ public PageFormat defaultPage(PageFormat page) {
1538+ PageFormat newPage = (PageFormat)page.clone();
1539+ newPage.setOrientation(PageFormat.PORTRAIT);
1540+ Paper newPaper = new Paper();
1541+ double ptsPerInch = 72.0;
1542+ double w, h;
1543+ Media media = null;
1544+
1545+ PrintService service = getPrintService();
1546+ if (service != null) {
1547+ MediaSize size;
1548+ media =
1549+ (Media)service.getDefaultAttributeValue(Media.class);
1550+
1551+ if (media instanceof MediaSizeName &&
1552+ ((size = MediaSize.getMediaSizeForName((MediaSizeName)media)) !=
1553+ null)) {
1554+ w = size.getX(MediaSize.INCH) * ptsPerInch;
1555+ h = size.getY(MediaSize.INCH) * ptsPerInch;
1556+ newPaper.setSize(w, h);
1557+ newPaper.setImageableArea(ptsPerInch, ptsPerInch,
1558+ w - 2.0*ptsPerInch,
1559+ h - 2.0*ptsPerInch);
1560+ newPage.setPaper(newPaper);
1561+ return newPage;
1562+
1563+ }
1564+ }
1565+
1566+ /* Default to A4 paper outside North America.
1567+ */
1568+ String defaultCountry = Locale.getDefault().getCountry();
1569+ if (!Locale.getDefault().equals(Locale.ENGLISH) && // ie "C"
1570+ defaultCountry != null &&
1571+ !defaultCountry.equals(Locale.US.getCountry()) &&
1572+ !defaultCountry.equals(Locale.CANADA.getCountry())) {
1573+
1574+ double mmPerInch = 25.4;
1575+ w = Math.rint((210.0*ptsPerInch)/mmPerInch);
1576+ h = Math.rint((297.0*ptsPerInch)/mmPerInch);
1577+ newPaper.setSize(w, h);
1578+ newPaper.setImageableArea(ptsPerInch, ptsPerInch,
1579+ w - 2.0*ptsPerInch,
1580+ h - 2.0*ptsPerInch);
1581+ }
1582+
1583+ newPage.setPaper(newPaper);
1584+
1585+ return newPage;
1586+ }
1587+
1588+ /**
1589+ * The passed in PageFormat is cloned and altered to be usable on
1590+ * the PrinterJob's current printer.
1591+ */
1592+ public PageFormat validatePage(PageFormat page) {
1593+ PageFormat newPage = (PageFormat)page.clone();
1594+ Paper newPaper = new Paper();
1595+ validatePaper(newPage.getPaper(), newPaper);
1596+ newPage.setPaper(newPaper);
1597+
1598+ return newPage;
1599+ }
1600+
1601+ /**
1602+ * Set the number of copies to be printed.
1603+ */
1604+ public void setCopies(int copies) {
1605+ mNumCopies = copies;
1606+ }
1607+
1608+ /**
1609+ * Get the number of copies to be printed.
1610+ */
1611+ public int getCopies() {
1612+ return mNumCopies;
1613+ }
1614+
1615+ /* Used when executing a print job where an attribute set may
1616+ * over ride API values.
1617+ */
1618+ protected int getCopiesInt() {
1619+ return (copiesAttr > 0) ? copiesAttr : getCopies();
1620+ }
1621+
1622+ /**
1623+ * Get the name of the printing user.
1624+ * The caller must have security permission to read system properties.
1625+ */
1626+ public String getUserName() {
1627+ return System.getProperty("user.name");
1628+ }
1629+
1630+ /* Used when executing a print job where an attribute set may
1631+ * over ride API values.
1632+ */
1633+ protected String getUserNameInt() {
1634+ if (userNameAttr != null) {
1635+ return userNameAttr;
1636+ } else {
1637+ try {
1638+ return getUserName();
1639+ } catch (SecurityException e) {
1640+ return "";
1641+ }
1642+ }
1643+ }
1644+
1645+ /**
1646+ * Set the name of the document to be printed.
1647+ * The document name can not be null.
1648+ */
1649+ public void setJobName(String jobName) {
1650+ if (jobName != null) {
1651+ mDocName = jobName;
1652+ } else {
1653+ throw new NullPointerException();
1654+ }
1655+ }
1656+
1657+ /**
1658+ * Get the name of the document to be printed.
1659+ */
1660+ public String getJobName() {
1661+ return mDocName;
1662+ }
1663+
1664+ /* Used when executing a print job where an attribute set may
1665+ * over ride API values.
1666+ */
1667+ protected String getJobNameInt() {
1668+ return (jobNameAttr != null) ? jobNameAttr : getJobName();
1669+ }
1670+
1671+ /**
1672+ * Set the range of pages from a Book to be printed.
1673+ * Both 'firstPage' and 'lastPage' are zero based
1674+ * page indices. If either parameter is less than
1675+ * zero then the page range is set to be from the
1676+ * first page to the last.
1677+ */
1678+ protected void setPageRange(int firstPage, int lastPage) {
1679+ if(firstPage >= 0 && lastPage >= 0) {
1680+ mFirstPage = firstPage;
1681+ mLastPage = lastPage;
1682+ if(mLastPage < mFirstPage) mLastPage = mFirstPage;
1683+ } else {
1684+ mFirstPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;
1685+ mLastPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;
1686+ }
1687+ }
1688+
1689+ /**
1690+ * Return the zero based index of the first page to
1691+ * be printed in this job.
1692+ */
1693+ protected int getFirstPage() {
1694+ return mFirstPage == Book.UNKNOWN_NUMBER_OF_PAGES ? 0 : mFirstPage;
1695+ }
1696+
1697+ /**
1698+ * Return the zero based index of the last page to
1699+ * be printed in this job.
1700+ */
1701+ protected int getLastPage() {
1702+ return mLastPage;
1703+ }
1704+
1705+ /**
1706+ * Set whether copies should be collated or not.
1707+ * Two collated copies of a three page document
1708+ * print in this order: 1, 2, 3, 1, 2, 3 while
1709+ * uncollated copies print in this order:
1710+ * 1, 1, 2, 2, 3, 3.
1711+ * This is set when request is using an attribute set.
1712+ */
1713+ protected void setCollated(boolean collate) {
1714+ mCollate = collate;
1715+ collateAttReq = true;
1716+ }
1717+
1718+ /**
1719+ * Return true if collated copies will be printed as determined
1720+ * in an attribute set.
1721+ */
1722+ protected boolean isCollated() {
1723+ return mCollate;
1724+ }
1725+
1726+ /**
1727+ * Called by the print() method at the start of
1728+ * a print job.
1729+ */
1730+ protected abstract void startDoc() throws PrinterException;
1731+
1732+ /**
1733+ * Called by the print() method at the end of
1734+ * a print job.
1735+ */
1736+ protected abstract void endDoc() throws PrinterException;
1737+
1738+ /* Called by cancelDoc */
1739+ protected abstract void abortDoc();
1740+
1741+ private void cancelDoc() throws PrinterAbortException {
1742+ abortDoc();
1743+ synchronized (this) {
1744+ userCancelled = false;
1745+ performingPrinting = false;
1746+ notify();
1747+ }
1748+ throw new PrinterAbortException();
1749+ }
1750+
1751+ /**
1752+ * Returns how many times the entire book should
1753+ * be printed by the PrintJob. If the printer
1754+ * itself supports collation then this method
1755+ * should return 1 indicating that the entire
1756+ * book need only be printed once and the copies
1757+ * will be collated and made in the printer.
1758+ */
1759+ protected int getCollatedCopies() {
1760+ return isCollated() ? getCopiesInt() : 1;
1761+ }
1762+
1763+ /**
1764+ * Returns how many times each page in the book
1765+ * should be consecutively printed by PrintJob.
1766+ * If the printer makes copies itself then this
1767+ * method should return 1.
1768+ */
1769+ protected int getNoncollatedCopies() {
1770+ return isCollated() ? 1 : getCopiesInt();
1771+ }
1772+
1773+
1774+ /* The printer graphics config is cached on the job, so that it can
1775+ * be created once, and updated only as needed (for now only to change
1776+ * the bounds if when using a Pageable the page sizes changes).
1777+ */
1778+
1779+ private int deviceWidth, deviceHeight;
1780+ private AffineTransform defaultDeviceTransform;
1781+ private PrinterGraphicsConfig pgConfig;
1782+
1783+ synchronized void setGraphicsConfigInfo(AffineTransform at,
1784+ double pw, double ph) {
1785+ Point2D.Double pt = new Point2D.Double(pw, ph);
1786+ at.transform(pt, pt);
1787+
1788+ if (pgConfig == null ||
1789+ defaultDeviceTransform == null ||
1790+ !at.equals(defaultDeviceTransform) ||
1791+ deviceWidth != (int)pt.getX() ||
1792+ deviceHeight != (int)pt.getY()) {
1793+
1794+ deviceWidth = (int)pt.getX();
1795+ deviceHeight = (int)pt.getY();
1796+ defaultDeviceTransform = at;
1797+ pgConfig = null;
1798+ }
1799+ }
1800+
1801+ synchronized PrinterGraphicsConfig getPrinterGraphicsConfig() {
1802+ if (pgConfig != null) {
1803+ return pgConfig;
1804+ }
1805+ String deviceID = "Printer Device";
1806+ PrintService service = getPrintService();
1807+ if (service != null) {
1808+ deviceID = service.toString();
1809+ }
1810+ pgConfig = new PrinterGraphicsConfig(deviceID,
1811+ defaultDeviceTransform,
1812+ deviceWidth, deviceHeight);
1813+ return pgConfig;
1814+ }
1815+
1816+ /**
1817+ * Print a page from the provided document.
1818+ * @return int Printable.PAGE_EXISTS if the page existed and was drawn and
1819+ * Printable.NO_SUCH_PAGE if the page did not exist.
1820+ * @see java.awt.print.Printable
1821+ */
1822+ protected int printPage(Pageable document, int pageIndex)
1823+ throws PrinterException
1824+ {
1825+ PageFormat page;
1826+ PageFormat origPage;
1827+ Printable painter;
1828+ try {
1829+ origPage = document.getPageFormat(pageIndex);
1830+ page = (PageFormat)origPage.clone();
1831+ painter = document.getPrintable(pageIndex);
1832+ } catch (Exception e) {
1833+ PrinterException pe =
1834+ new PrinterException("Error getting page or printable.[ " +
1835+ e +" ]");
1836+ pe.initCause(e);
1837+ throw pe;
1838+ }
1839+
1840+ /* Get the imageable area from Paper instead of PageFormat
1841+ * because we do not want it adjusted by the page orientation.
1842+ */
1843+ Paper paper = page.getPaper();
1844+ // if non-portrait and 270 degree landscape rotation
1845+ if (page.getOrientation() != PageFormat.PORTRAIT &&
1846+ landscapeRotates270) {
1847+
1848+ double left = paper.getImageableX();
1849+ double top = paper.getImageableY();
1850+ double width = paper.getImageableWidth();
1851+ double height = paper.getImageableHeight();
1852+ paper.setImageableArea(paper.getWidth()-left-width,
1853+ paper.getHeight()-top-height,
1854+ width, height);
1855+ page.setPaper(paper);
1856+ if (page.getOrientation() == PageFormat.LANDSCAPE) {
1857+ page.setOrientation(PageFormat.REVERSE_LANDSCAPE);
1858+ } else {
1859+ page.setOrientation(PageFormat.LANDSCAPE);
1860+ }
1861+ }
1862+
1863+ double xScale = getXRes() / 72.0;
1864+ double yScale = getYRes() / 72.0;
1865+
1866+ /* The deviceArea is the imageable area in the printer's
1867+ * resolution.
1868+ */
1869+ Rectangle2D deviceArea =
1870+ new Rectangle2D.Double(paper.getImageableX() * xScale,
1871+ paper.getImageableY() * yScale,
1872+ paper.getImageableWidth() * xScale,
1873+ paper.getImageableHeight() * yScale);
1874+
1875+ /* Build and hold on to a uniform transform so that
1876+ * we can get back to device space at the beginning
1877+ * of each band.
1878+ */
1879+ AffineTransform uniformTransform = new AffineTransform();
1880+
1881+ /* The scale transform is used to switch from the
1882+ * device space to the user's 72 dpi space.
1883+ */
1884+ AffineTransform scaleTransform = new AffineTransform();
1885+ scaleTransform.scale(xScale, yScale);
1886+
1887+ /* bandwidth is multiple of 4 as the data is used in a win32 DIB and
1888+ * some drivers behave badly if scanlines aren't multiples of 4 bytes.
1889+ */
1890+ int bandWidth = (int) deviceArea.getWidth();
1891+ if (bandWidth % 4 != 0) {
1892+ bandWidth += (4 - (bandWidth % 4));
1893+ }
1894+ if (bandWidth <= 0) {
1895+ throw new PrinterException("Paper's imageable width is too small.");
1896+ }
1897+
1898+ int deviceAreaHeight = (int)deviceArea.getHeight();
1899+ if (deviceAreaHeight <= 0) {
1900+ throw new PrinterException("Paper's imageable height is too small.");
1901+ }
1902+
1903+ /* Figure out the number of lines that will fit into
1904+ * our maximum band size. The hard coded 3 reflects the
1905+ * fact that we can only create 24 bit per pixel 3 byte BGR
1906+ * BufferedImages. FIX.
1907+ */
1908+ int bandHeight = (int)(MAX_BAND_SIZE / bandWidth / 3);
1909+
1910+ int deviceLeft = (int)Math.rint(paper.getImageableX() * xScale);
1911+ int deviceTop = (int)Math.rint(paper.getImageableY() * yScale);
1912+
1913+ /* The device transform is used to move the band down
1914+ * the page using translates. Normally this is all it
1915+ * would do, but since, when printing, the Window's
1916+ * DIB format wants the last line to be first (lowest) in
1917+ * memory, the deviceTransform moves the origin to the
1918+ * bottom of the band and flips the origin. This way the
1919+ * app prints upside down into the band which is the DIB
1920+ * format.
1921+ */
1922+ AffineTransform deviceTransform = new AffineTransform();
1923+ deviceTransform.translate(-deviceLeft, deviceTop);
1924+ deviceTransform.translate(0, bandHeight);
1925+ deviceTransform.scale(1, -1);
1926+
1927+ /* Create a BufferedImage to hold the band. We set the clip
1928+ * of the band to be tight around the bits so that the
1929+ * application can use it to figure what part of the
1930+ * page needs to be drawn. The clip is never altered in
1931+ * this method, but we do translate the band's coordinate
1932+ * system so that the app will see the clip moving down the
1933+ * page though it s always around the same set of pixels.
1934+ */
1935+ BufferedImage pBand = new BufferedImage(1, 1,
1936+ BufferedImage.TYPE_3BYTE_BGR);
1937+
1938+ /* Have the app draw into a PeekGraphics object so we can
1939+ * learn something about the needs of the print job.
1940+ */
1941+
1942+ PeekGraphics peekGraphics = createPeekGraphics(pBand.createGraphics(),
1943+ this);
1944+
1945+ Rectangle2D.Double pageFormatArea =
1946+ new Rectangle2D.Double(page.getImageableX(),
1947+ page.getImageableY(),
1948+ page.getImageableWidth(),
1949+ page.getImageableHeight());
1950+ peekGraphics.transform(scaleTransform);
1951+ peekGraphics.translate(-getPhysicalPrintableX(paper) / xScale,
1952+ -getPhysicalPrintableY(paper) / yScale);
1953+ peekGraphics.transform(new AffineTransform(page.getMatrix()));
1954+ initPrinterGraphics(peekGraphics, pageFormatArea);
1955+ AffineTransform pgAt = peekGraphics.getTransform();
1956+
1957+ /* Update the information used to return a GraphicsConfiguration
1958+ * for this printer device. It needs to be updated per page as
1959+ * not all pages in a job may be the same size (different bounds)
1960+ * The transform is the scaling transform as this corresponds to
1961+ * the default transform for the device. The width and height are
1962+ * those of the paper, not the page format, as we want to describe
1963+ * the bounds of the device in its natural coordinate system of
1964+ * device coordinate whereas a page format may be in a rotated context.
1965+ */
1966+ setGraphicsConfigInfo(scaleTransform,
1967+ paper.getWidth(), paper.getHeight());
1968+ int pageResult = painter.print(peekGraphics, origPage, pageIndex);
1969+ debug_println("pageResult "+pageResult);
1970+ if (pageResult == Printable.PAGE_EXISTS) {
1971+ debug_println("startPage "+pageIndex);
1972+
1973+ /* We need to check if the paper size is changed.
1974+ * Note that it is not sufficient to ask for the pageformat
1975+ * of "pageIndex-1", since PageRanges mean that pages can be
1976+ * skipped. So we have to look at the actual last paper size used.
1977+ */
1978+ Paper thisPaper = page.getPaper();
1979+ boolean paperChanged =
1980+ previousPaper == null ||
1981+ thisPaper.getWidth() != previousPaper.getWidth() ||
1982+ thisPaper.getHeight() != previousPaper.getHeight();
1983+ previousPaper = thisPaper;
1984+
1985+ startPage(page, painter, pageIndex, paperChanged);
1986+ Graphics2D pathGraphics = createPathGraphics(peekGraphics, this,
1987+ painter, page,
1988+ pageIndex);
1989+
1990+ /* If we can convert the page directly to the
1991+ * underlying graphics system then we do not
1992+ * need to rasterize. We also may not need to
1993+ * create the 'band' if all the pages can take
1994+ * this path.
1995+ */
1996+ if (pathGraphics != null) {
1997+ pathGraphics.transform(scaleTransform);
1998+ // user (0,0) should be origin of page, not imageable area
1999+ pathGraphics.translate(-getPhysicalPrintableX(paper) / xScale,
2000+ -getPhysicalPrintableY(paper) / yScale);
2001+ pathGraphics.transform(new AffineTransform(page.getMatrix()));
2002+ initPrinterGraphics(pathGraphics, pageFormatArea);
2003+
2004+ redrawList.clear();
2005+
2006+ AffineTransform initialTx = pathGraphics.getTransform();
2007+
2008+ painter.print(pathGraphics, origPage, pageIndex);
2009+
2010+ for (int i=0;i<redrawList.size();i++) {
2011+ GraphicsState gstate = (GraphicsState)redrawList.get(i);
2012+ pathGraphics.setTransform(initialTx);
2013+ ((PathGraphics)pathGraphics).redrawRegion(
2014+ gstate.region,
2015+ gstate.sx,
2016+ gstate.sy,
2017+ gstate.theClip,
2018+ gstate.theTransform);
2019+ }
2020+
2021+ /* This is the banded-raster printing loop.
2022+ * It should be moved into its own method.
2023+ */
2024+ } else {
2025+ BufferedImage band = cachedBand;
2026+ if (cachedBand == null ||
2027+ bandWidth != cachedBandWidth ||
2028+ bandHeight != cachedBandHeight) {
2029+ band = new BufferedImage(bandWidth, bandHeight,
2030+ BufferedImage.TYPE_3BYTE_BGR);
2031+ cachedBand = band;
2032+ cachedBandWidth = bandWidth;
2033+ cachedBandHeight = bandHeight;
2034+ }
2035+ Graphics2D bandGraphics = band.createGraphics();
2036+
2037+ Rectangle2D.Double clipArea =
2038+ new Rectangle2D.Double(0, 0, bandWidth, bandHeight);
2039+
2040+ initPrinterGraphics(bandGraphics, clipArea);
2041+
2042+ ProxyGraphics2D painterGraphics =
2043+ new ProxyGraphics2D(bandGraphics, this);
2044+
2045+ Graphics2D clearGraphics = band.createGraphics();
2046+ clearGraphics.setColor(Color.white);
2047+
2048+ /* We need the actual bits of the BufferedImage to send to
2049+ * the native Window's code. 'data' points to the actual
2050+ * pixels. Right now these are in ARGB format with 8 bits
2051+ * per component. We need to use a monochrome BufferedImage
2052+ * for monochrome printers when this is supported by
2053+ * BufferedImage. FIX
2054+ */
2055+ ByteInterleavedRaster tile = (ByteInterleavedRaster)band.getRaster();
2056+ byte[] data = tile.getDataStorage();
2057+
2058+ /* Loop over the page moving our band down the page,
2059+ * calling the app to render the band, and then send the band
2060+ * to the printer.
2061+ */
2062+ int deviceBottom = deviceTop + deviceAreaHeight;
2063+
2064+ /* device's printable x,y is really addressable origin
2065+ * we address relative to media origin so when we print a
2066+ * band we need to adjust for the different methods of
2067+ * addressing it.
2068+ */
2069+ int deviceAddressableX = (int)getPhysicalPrintableX(paper);
2070+ int deviceAddressableY = (int)getPhysicalPrintableY(paper);
2071+
2072+ for (int bandTop = 0; bandTop <= deviceAreaHeight;
2073+ bandTop += bandHeight)
2074+ {
2075+
2076+ /* Put the band back into device space and
2077+ * erase the contents of the band.
2078+ */
2079+ clearGraphics.fillRect(0, 0, bandWidth, bandHeight);
2080+
2081+ /* Put the band into the correct location on the
2082+ * page. Once the band is moved we translate the
2083+ * device transform so that the band will move down
2084+ * the page on the next iteration of the loop.
2085+ */
2086+ bandGraphics.setTransform(uniformTransform);
2087+ bandGraphics.transform(deviceTransform);
2088+ deviceTransform.translate(0, -bandHeight);
2089+
2090+ /* Switch the band from device space to user,
2091+ * 72 dpi, space.
2092+ */
2093+ bandGraphics.transform(scaleTransform);
2094+ bandGraphics.transform(new AffineTransform(page.getMatrix()));
2095+
2096+ Rectangle clip = bandGraphics.getClipBounds();
2097+ clip = pgAt.createTransformedShape(clip).getBounds();
2098+
2099+ if ((clip == null) || peekGraphics.hitsDrawingArea(clip) &&
2100+ (bandWidth > 0 && bandHeight > 0)) {
2101+
2102+ /* if the client has specified an imageable X or Y
2103+ * which is off than the physically addressable
2104+ * area of the page, then we need to adjust for that
2105+ * here so that we pass only non -ve band coordinates
2106+ * We also need to translate by the adjusted amount
2107+ * so that printing appears in the correct place.
2108+ */
2109+ int bandX = deviceLeft - deviceAddressableX;
2110+ if (bandX < 0) {
2111+ bandGraphics.translate(bandX/xScale,0);
2112+ bandX = 0;
2113+ }
2114+ int bandY = deviceTop + bandTop - deviceAddressableY;
2115+ if (bandY < 0) {
2116+ bandGraphics.translate(0,bandY/yScale);
2117+ bandY = 0;
2118+ }
2119+ /* Have the app's painter image into the band
2120+ * and then send the band to the printer.
2121+ */
2122+ painterGraphics.setDelegate((Graphics2D) bandGraphics.create());
2123+ painter.print(painterGraphics, origPage, pageIndex);
2124+ painterGraphics.dispose();
2125+ printBand(data, bandX, bandY, bandWidth, bandHeight);
2126+ }
2127+ }
2128+
2129+ clearGraphics.dispose();
2130+ bandGraphics.dispose();
2131+
2132+ }
2133+ debug_println("calling endPage "+pageIndex);
2134+ endPage(page, painter, pageIndex);
2135+ }
2136+
2137+ return pageResult;
2138+ }
2139+
2140+ /**
2141+ * If a print job is in progress, print() has been
2142+ * called but has not returned, then this signals
2143+ * that the job should be cancelled and the next
2144+ * chance. If there is no print job in progress then
2145+ * this call does nothing.
2146+ */
2147+ public void cancel() {
2148+ synchronized (this) {
2149+ if (performingPrinting) {
2150+ userCancelled = true;
2151+ }
2152+ notify();
2153+ }
2154+ }
2155+
2156+ /**
2157+ * Returns true is a print job is ongoing but will
2158+ * be cancelled and the next opportunity. false is
2159+ * returned otherwise.
2160+ */
2161+ public boolean isCancelled() {
2162+
2163+ boolean cancelled = false;
2164+
2165+ synchronized (this) {
2166+ cancelled = (performingPrinting && userCancelled);
2167+ notify();
2168+ }
2169+
2170+ return cancelled;
2171+ }
2172+
2173+ /**
2174+ * Return the Pageable describing the pages to be printed.
2175+ */
2176+ protected Pageable getPageable() {
2177+ return mDocument;
2178+ }
2179+
2180+ /**
2181+ * Examine the metrics captured by the
2182+ * <code>PeekGraphics</code> instance and
2183+ * if capable of directly converting this
2184+ * print job to the printer's control language
2185+ * or the native OS's graphics primitives, then
2186+ * return a <code>PathGraphics</code> to perform
2187+ * that conversion. If there is not an object
2188+ * capable of the conversion then return
2189+ * <code>null</code>. Returning <code>null</code>
2190+ * causes the print job to be rasterized.
2191+ */
2192+ protected Graphics2D createPathGraphics(PeekGraphics graphics,
2193+ PrinterJob printerJob,
2194+ Printable painter,
2195+ PageFormat pageFormat,
2196+ int pageIndex) {
2197+
2198+ return null;
2199+ }
2200+
2201+ /**
2202+ * Create and return an object that will
2203+ * gather and hold metrics about the print
2204+ * job. This method is passed a <code>Graphics2D</code>
2205+ * object that can be used as a proxy for the
2206+ * object gathering the print job matrics. The
2207+ * method is also supplied with the instance
2208+ * controlling the print job, <code>printerJob</code>.
2209+ */
2210+ protected PeekGraphics createPeekGraphics(Graphics2D graphics,
2211+ PrinterJob printerJob) {
2212+
2213+ return new PeekGraphics(graphics, printerJob);
2214+ }
2215+
2216+ /**
2217+ * Configure the passed in Graphics2D so that
2218+ * is contains the defined initial settings
2219+ * for a print job. These settings are:
2220+ * color: black.
2221+ * clip: <as passed in>
2222+ */
2223+ void initPrinterGraphics(Graphics2D g, Rectangle2D clip) {
2224+
2225+ g.setClip(clip);
2226+ g.setPaint(Color.black);
2227+ }
2228+
2229+
2230+ /**
2231+ * User dialogs should disable "File" buttons if this returns false.
2232+ *
2233+ */
2234+ public boolean checkAllowedToPrintToFile() {
2235+ try {
2236+ throwPrintToFile();
2237+ return true;
2238+ } catch (SecurityException e) {
2239+ return false;
2240+ }
2241+ }
2242+
2243+ /**
2244+ * Break this out as it may be useful when we allow API to
2245+ * specify printing to a file. In that case its probably right
2246+ * to throw a SecurityException if the permission is not granted
2247+ */
2248+ private void throwPrintToFile() {
2249+ SecurityManager security = System.getSecurityManager();
2250+ if (security != null) {
2251+ if (printToFilePermission == null) {
2252+ printToFilePermission =
2253+ new FilePermission("<<ALL FILES>>", "read,write");
2254+ }
2255+ security.checkPermission(printToFilePermission);
2256+ }
2257+ }
2258+
2259+ /* On-screen drawString renders most control chars as the missing glyph
2260+ * and have the non-zero advance of that glyph.
2261+ * Exceptions are \t, \n and \r which are considered zero-width.
2262+ * This is a utility method used by subclasses to remove them so we
2263+ * don't have to worry about platform or font specific handling of them.
2264+ */
2265+ protected String removeControlChars(String s) {
2266+ char[] in_chars = s.toCharArray();
2267+ int len = in_chars.length;
2268+ char[] out_chars = new char[len];
2269+ int pos = 0;
2270+
2271+ for (int i = 0; i < len; i++) {
2272+ char c = in_chars[i];
2273+ if (c > '\r' || c < '\t' || c == '\u000b' || c == '\u000c') {
2274+ out_chars[pos++] = c;
2275+ }
2276+ }
2277+ if (pos == len) {
2278+ return s; // no need to make a new String.
2279+ } else {
2280+ return new String(out_chars, 0, pos);
2281+ }
2282+ }
2283+}