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
26package sun.print;
27
28import java.io.FilePermission;
29
30import java.awt.Color;
31import java.awt.Dialog;
32import java.awt.Frame;
33import java.awt.Graphics;
34import java.awt.Graphics2D;
35import java.awt.GraphicsConfiguration;
36import java.awt.GraphicsEnvironment;
37import java.awt.HeadlessException;
38import java.awt.KeyboardFocusManager;
39import java.awt.Rectangle;
40import java.awt.Shape;
41import java.awt.geom.AffineTransform;
42import java.awt.geom.Area;
43import java.awt.geom.Point2D;
44import java.awt.geom.Rectangle2D;
45import java.awt.image.BufferedImage;
46import java.awt.print.Book;
47import java.awt.print.Pageable;
48import java.awt.print.PageFormat;
49import java.awt.print.Paper;
50import java.awt.print.Printable;
51import java.awt.print.PrinterAbortException;
52import java.awt.print.PrinterException;
53import java.awt.print.PrinterJob;
54import java.awt.Window;
55import java.io.File;
56import java.io.IOException;
57import java.util.ArrayList;
58import java.util.Enumeration;
59import java.util.Locale;
60import sun.awt.image.ByteInterleavedRaster;
61
62import javax.print.Doc;
63import javax.print.DocFlavor;
64import javax.print.DocPrintJob;
65import javax.print.PrintException;
66import javax.print.PrintService;
67import javax.print.PrintServiceLookup;
68import javax.print.ServiceUI;
69import javax.print.StreamPrintService;
70import javax.print.StreamPrintServiceFactory;
71import javax.print.attribute.Attribute;
72import javax.print.attribute.AttributeSet;
73import javax.print.attribute.HashPrintRequestAttributeSet;
74import javax.print.attribute.PrintRequestAttributeSet;
75import javax.print.attribute.Size2DSyntax;
76import javax.print.attribute.standard.Chromaticity;
77import javax.print.attribute.standard.Copies;
78import javax.print.attribute.standard.Destination;
79import javax.print.attribute.standard.DialogTypeSelection;
80import javax.print.attribute.standard.Fidelity;
81import javax.print.attribute.standard.JobName;
82import javax.print.attribute.standard.JobSheets;
83import javax.print.attribute.standard.Media;
84import javax.print.attribute.standard.MediaPrintableArea;
85import javax.print.attribute.standard.MediaSize;
86import javax.print.attribute.standard.MediaSizeName;
87import javax.print.attribute.standard.OrientationRequested;
88import javax.print.attribute.standard.PageRanges;
89import javax.print.attribute.standard.PrinterState;
90import javax.print.attribute.standard.PrinterStateReason;
91import javax.print.attribute.standard.PrinterStateReasons;
92import javax.print.attribute.standard.PrinterIsAcceptingJobs;
93import javax.print.attribute.standard.RequestingUserName;
94import javax.print.attribute.standard.SheetCollate;
95import javax.print.attribute.standard.Sides;
96
97import sun.print.PageableDoc;
98import sun.print.ServiceDialog;
99import sun.print.SunPrinterJobService;
100import sun.print.SunPageSelection;
101
102/**
103 * A class which rasterizes a printer job.
104 *
105 * @author Richard Blanchard
106 */
107public 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}