Path: blob/master/src/java.desktop/share/classes/sun/print/RasterPrinterJob.java
41153 views
/*1* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.print;2627import java.io.FilePermission;2829import java.awt.Color;30import java.awt.Dialog;31import java.awt.Frame;32import java.awt.Graphics2D;33import java.awt.GraphicsConfiguration;34import java.awt.GraphicsEnvironment;35import java.awt.HeadlessException;36import java.awt.KeyboardFocusManager;37import java.awt.Rectangle;38import java.awt.Shape;39import java.awt.geom.AffineTransform;40import java.awt.geom.Point2D;41import java.awt.geom.Rectangle2D;42import java.awt.image.BufferedImage;43import java.awt.print.Book;44import java.awt.print.Pageable;45import java.awt.print.PageFormat;46import java.awt.print.Paper;47import java.awt.print.Printable;48import java.awt.print.PrinterAbortException;49import java.awt.print.PrinterException;50import java.awt.print.PrinterJob;51import java.awt.Window;52import java.io.File;53import java.io.IOException;54import java.util.ArrayList;55import java.util.Locale;56import sun.awt.image.ByteInterleavedRaster;5758import javax.print.Doc;59import javax.print.DocFlavor;60import javax.print.DocPrintJob;61import javax.print.PrintException;62import javax.print.PrintService;63import javax.print.PrintServiceLookup;64import javax.print.ServiceUI;65import javax.print.StreamPrintService;66import javax.print.StreamPrintServiceFactory;67import javax.print.attribute.Attribute;68import javax.print.attribute.AttributeSet;69import javax.print.attribute.HashPrintRequestAttributeSet;70import javax.print.attribute.PrintRequestAttributeSet;71import javax.print.attribute.ResolutionSyntax;72import javax.print.attribute.Size2DSyntax;73import javax.print.attribute.standard.Copies;74import javax.print.attribute.standard.Destination;75import javax.print.attribute.standard.DialogTypeSelection;76import javax.print.attribute.standard.DialogOwner;77import javax.print.attribute.standard.Fidelity;78import javax.print.attribute.standard.JobName;79import javax.print.attribute.standard.JobSheets;80import javax.print.attribute.standard.Media;81import javax.print.attribute.standard.MediaPrintableArea;82import javax.print.attribute.standard.MediaSize;83import javax.print.attribute.standard.MediaSizeName;84import javax.print.attribute.standard.OrientationRequested;85import javax.print.attribute.standard.PageRanges;86import javax.print.attribute.standard.PrinterResolution;87import javax.print.attribute.standard.PrinterState;88import javax.print.attribute.standard.PrinterStateReason;89import javax.print.attribute.standard.PrinterStateReasons;90import javax.print.attribute.standard.PrinterIsAcceptingJobs;91import javax.print.attribute.standard.RequestingUserName;92import javax.print.attribute.standard.SheetCollate;93import javax.print.attribute.standard.Sides;9495/**96* A class which rasterizes a printer job.97*98* @author Richard Blanchard99*/100public abstract class RasterPrinterJob extends PrinterJob {101102/* Class Constants */103104/* Printer destination type. */105protected static final int PRINTER = 0;106107/* File destination type. */108protected static final int FILE = 1;109110/* Stream destination type. */111protected static final int STREAM = 2;112113/**114* Pageable MAX pages115*/116protected static final int MAX_UNKNOWN_PAGES = 9999;117118protected static final int PD_ALLPAGES = 0x00000000;119protected static final int PD_SELECTION = 0x00000001;120protected static final int PD_PAGENUMS = 0x00000002;121protected static final int PD_NOSELECTION = 0x00000004;122123/**124* Maximum amount of memory in bytes to use for the125* buffered image "band". 4Mb is a compromise between126* limiting the number of bands on hi-res printers and127* not using too much of the Java heap or causing paging128* on systems with little RAM.129*/130private static final int MAX_BAND_SIZE = (1024 * 1024 * 4);131132/* Dots Per Inch */133private static final float DPI = 72.0f;134135/**136* Useful mainly for debugging, this system property137* can be used to force the printing code to print138* using a particular pipeline. The two currently139* supported values are FORCE_RASTER and FORCE_PDL.140*/141private static final String FORCE_PIPE_PROP = "sun.java2d.print.pipeline";142143/**144* When the system property FORCE_PIPE_PROP has this value145* then each page of a print job will be rendered through146* the raster pipeline.147*/148private static final String FORCE_RASTER = "raster";149150/**151* When the system property FORCE_PIPE_PROP has this value152* then each page of a print job will be rendered through153* the PDL pipeline.154*/155private static final String FORCE_PDL = "pdl";156157/**158* When the system property SHAPE_TEXT_PROP has this value159* then text is always rendered as a shape, and no attempt is made160* to match the font through GDI161*/162private static final String SHAPE_TEXT_PROP = "sun.java2d.print.shapetext";163164/**165* values obtained from System properties in static initialiser block166*/167public static boolean forcePDL = false;168public static boolean forceRaster = false;169public static boolean shapeTextProp = false;170171static {172/* The system property FORCE_PIPE_PROP173* can be used to force the printing code to174* use a particular pipeline. Either the raster175* pipeline or the pdl pipeline can be forced.176*/177@SuppressWarnings("removal")178String forceStr = java.security.AccessController.doPrivileged(179new sun.security.action.GetPropertyAction(FORCE_PIPE_PROP));180181if (forceStr != null) {182if (forceStr.equalsIgnoreCase(FORCE_PDL)) {183forcePDL = true;184} else if (forceStr.equalsIgnoreCase(FORCE_RASTER)) {185forceRaster = true;186}187}188189@SuppressWarnings("removal")190String shapeTextStr =java.security.AccessController.doPrivileged(191new sun.security.action.GetPropertyAction(SHAPE_TEXT_PROP));192193if (shapeTextStr != null) {194shapeTextProp = true;195}196}197198/* Instance Variables */199200/**201* Used to minimize GC & reallocation of band when printing202*/203private int cachedBandWidth = 0;204private int cachedBandHeight = 0;205private BufferedImage cachedBand = null;206207/**208* The number of book copies to be printed.209*/210private int mNumCopies = 1;211212/**213* Collation effects the order of the pages printed214* when multiple copies are requested. For two copies215* of a three page document the page order is:216* mCollate true: 1, 2, 3, 1, 2, 3217* mCollate false: 1, 1, 2, 2, 3, 3218*/219private boolean mCollate = false;220221/**222* The zero based indices of the first and last223* pages to be printed. If 'mFirstPage' is224* UNDEFINED_PAGE_NUM then the first page to225* be printed is page 0. If 'mLastPage' is226* UNDEFINED_PAGE_NUM then the last page to227* be printed is the last one in the book.228*/229private int mFirstPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;230private int mLastPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;231232/**233* The previous print stream Paper234* Used to check if the paper size has changed such that the235* implementation needs to emit the new paper size information236* into the print stream.237* Since we do our own rotation, and the margins aren't relevant,238* Its strictly the dimensions of the paper that we will check.239*/240private Paper previousPaper;241242/**243* The document to be printed. It is initialized to an244* empty (zero pages) book.245*/246// MacOSX - made protected so subclasses can reference it.247protected Pageable mDocument = new Book();248249/**250* The name of the job being printed.251*/252private String mDocName = "Java Printing";253254255/**256* Printing cancellation flags257*/258// MacOSX - made protected so subclasses can reference it.259protected boolean performingPrinting = false;260// MacOSX - made protected so subclasses can reference it.261protected boolean userCancelled = false;262263/**264* Print to file permission variables.265*/266private FilePermission printToFilePermission;267268/**269* List of areas & the graphics state for redrawing270*/271private ArrayList<GraphicsState> redrawList = new ArrayList<>();272273274/* variables representing values extracted from an attribute set.275* These take precedence over values set on a printer job276*/277private int copiesAttr;278private String jobNameAttr;279private String userNameAttr;280private PageRanges pageRangesAttr;281protected PrinterResolution printerResAttr;282protected Sides sidesAttr;283protected String destinationAttr;284protected boolean noJobSheet = false;285protected int mDestType = RasterPrinterJob.FILE;286protected String mDestination = "";287protected boolean collateAttReq = false;288289/**290* Device rotation flag, if it support 270, this is set to true;291*/292protected boolean landscapeRotates270 = false;293294/**295* attributes used by no-args page and print dialog and print method to296* communicate state297*/298protected PrintRequestAttributeSet attributes = null;299300/**301* Class to keep state information for redrawing areas302* "region" is an area at as a high a resolution as possible.303* The redrawing code needs to look at sx, sy to calculate the scale304* to device resolution.305*/306private class GraphicsState {307Rectangle2D region; // Area of page to repaint308Shape theClip; // image drawing clip.309AffineTransform theTransform; // to transform clip to dev coords.310double sx; // X scale from region to device resolution311double sy; // Y scale from region to device resolution312}313314/**315* Service for this job316*/317protected PrintService myService;318319/* Constructors */320321public RasterPrinterJob()322{323}324325/* Abstract Methods */326327/**328* Returns the resolution in dots per inch across the width329* of the page.330*/331protected abstract double getXRes();332333/**334* Returns the resolution in dots per inch down the height335* of the page.336*/337protected abstract double getYRes();338339/**340* Must be obtained from the current printer.341* Value is in device pixels.342* Not adjusted for orientation of the paper.343*/344protected abstract double getPhysicalPrintableX(Paper p);345346/**347* Must be obtained from the current printer.348* Value is in device pixels.349* Not adjusted for orientation of the paper.350*/351protected abstract double getPhysicalPrintableY(Paper p);352353/**354* Must be obtained from the current printer.355* Value is in device pixels.356* Not adjusted for orientation of the paper.357*/358protected abstract double getPhysicalPrintableWidth(Paper p);359360/**361* Must be obtained from the current printer.362* Value is in device pixels.363* Not adjusted for orientation of the paper.364*/365protected abstract double getPhysicalPrintableHeight(Paper p);366367/**368* Must be obtained from the current printer.369* Value is in device pixels.370* Not adjusted for orientation of the paper.371*/372protected abstract double getPhysicalPageWidth(Paper p);373374/**375* Must be obtained from the current printer.376* Value is in device pixels.377* Not adjusted for orientation of the paper.378*/379protected abstract double getPhysicalPageHeight(Paper p);380381/**382* Begin a new page.383*/384protected abstract void startPage(PageFormat format, Printable painter,385int index, boolean paperChanged)386throws PrinterException;387388/**389* End a page.390*/391protected abstract void endPage(PageFormat format, Printable painter,392int index)393throws PrinterException;394395/**396* Prints the contents of the array of ints, 'data'397* to the current page. The band is placed at the398* location (x, y) in device coordinates on the399* page. The width and height of the band is400* specified by the caller.401*/402protected abstract void printBand(byte[] data, int x, int y,403int width, int height)404throws PrinterException;405406/* Instance Methods */407408/**409* save graphics state of a PathGraphics for later redrawing410* of part of page represented by the region in that state411*/412413public void saveState(AffineTransform at, Shape clip,414Rectangle2D region, double sx, double sy) {415GraphicsState gstate = new GraphicsState();416gstate.theTransform = at;417gstate.theClip = clip;418gstate.region = region;419gstate.sx = sx;420gstate.sy = sy;421redrawList.add(gstate);422}423424425/*426* A convenience method which returns the default service427* for 2D {@code PrinterJob}s.428* May return null if there is no suitable default (although there429* may still be 2D services available).430* @return default 2D print service, or null.431* @since 1.4432*/433protected static PrintService lookupDefaultPrintService() {434PrintService service = PrintServiceLookup.lookupDefaultPrintService();435436/* Pageable implies Printable so checking both isn't strictly needed */437if (service != null &&438service.isDocFlavorSupported(439DocFlavor.SERVICE_FORMATTED.PAGEABLE) &&440service.isDocFlavorSupported(441DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {442return service;443} else {444PrintService []services =445PrintServiceLookup.lookupPrintServices(446DocFlavor.SERVICE_FORMATTED.PAGEABLE, null);447if (services.length > 0) {448return services[0];449}450}451return null;452}453454/**455* Returns the service (printer) for this printer job.456* Implementations of this class which do not support print services457* may return null;458* @return the service for this printer job.459*460*/461public PrintService getPrintService() {462if (myService == null) {463PrintService svc = PrintServiceLookup.lookupDefaultPrintService();464if (svc != null &&465svc.isDocFlavorSupported(466DocFlavor.SERVICE_FORMATTED.PAGEABLE)) {467try {468setPrintService(svc);469myService = svc;470} catch (PrinterException e) {471}472}473if (myService == null) {474PrintService[] svcs = PrintServiceLookup.lookupPrintServices(475DocFlavor.SERVICE_FORMATTED.PAGEABLE, null);476if (svcs.length > 0) {477try {478setPrintService(svcs[0]);479myService = svcs[0];480} catch (PrinterException e) {481}482}483}484}485return myService;486}487488/**489* Associate this PrinterJob with a new PrintService.490*491* Throws {@code PrinterException} if the specified service492* cannot support the {@code Pageable} and493* {@code Printable} interfaces necessary to support 2D printing.494* @param service print service which supports 2D printing.495*496* @throws PrinterException if the specified service does not support497* 2D printing or no longer available.498*/499public void setPrintService(PrintService service)500throws PrinterException {501if (service == null) {502throw new PrinterException("Service cannot be null");503} else if (!(service instanceof StreamPrintService) &&504service.getName() == null) {505throw new PrinterException("Null PrintService name.");506} else {507// Check the list of services. This service may have been508// deleted already509PrinterState prnState = service.getAttribute(PrinterState.class);510if (prnState == PrinterState.STOPPED) {511PrinterStateReasons prnStateReasons =512service.getAttribute(PrinterStateReasons.class);513if ((prnStateReasons != null) &&514(prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN)))515{516throw new PrinterException("PrintService is no longer available.");517}518}519520521if (service.isDocFlavorSupported(522DocFlavor.SERVICE_FORMATTED.PAGEABLE) &&523service.isDocFlavorSupported(524DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {525myService = service;526} else {527throw new PrinterException("Not a 2D print service: " + service);528}529}530}531532private PageFormat attributeToPageFormat(PrintService service,533PrintRequestAttributeSet attSet) {534PageFormat page = defaultPage();535536if (service == null) {537return page;538}539540OrientationRequested orient = (OrientationRequested)541attSet.get(OrientationRequested.class);542if (orient == null) {543orient = (OrientationRequested)544service.getDefaultAttributeValue(OrientationRequested.class);545}546if (orient == OrientationRequested.REVERSE_LANDSCAPE) {547page.setOrientation(PageFormat.REVERSE_LANDSCAPE);548} else if (orient == OrientationRequested.LANDSCAPE) {549page.setOrientation(PageFormat.LANDSCAPE);550} else {551page.setOrientation(PageFormat.PORTRAIT);552}553554Media media = (Media)attSet.get(Media.class);555MediaSize size = getMediaSize(media, service, page);556557Paper paper = new Paper();558float[] dim = size.getSize(1); //units == 1 to avoid FP error559double w = Math.rint((dim[0]*72.0)/Size2DSyntax.INCH);560double h = Math.rint((dim[1]*72.0)/Size2DSyntax.INCH);561paper.setSize(w, h);562MediaPrintableArea area =563(MediaPrintableArea)564attSet.get(MediaPrintableArea.class);565if (area == null) {566area = getDefaultPrintableArea(page, w, h);567}568569double ix, iw, iy, ih;570// Should pass in same unit as updatePageAttributes571// to avoid rounding off errors.572ix = Math.rint(573area.getX(MediaPrintableArea.INCH) * DPI);574iy = Math.rint(575area.getY(MediaPrintableArea.INCH) * DPI);576iw = Math.rint(577area.getWidth(MediaPrintableArea.INCH) * DPI);578ih = Math.rint(579area.getHeight(MediaPrintableArea.INCH) * DPI);580paper.setImageableArea(ix, iy, iw, ih);581page.setPaper(paper);582return page;583}584protected MediaSize getMediaSize(Media media, PrintService service,585PageFormat page) {586if (media == null) {587media = (Media)service.getDefaultAttributeValue(Media.class);588}589if (!(media instanceof MediaSizeName)) {590media = MediaSizeName.NA_LETTER;591}592MediaSize size = MediaSize.getMediaSizeForName((MediaSizeName) media);593return size != null ? size : MediaSize.NA.LETTER;594}595596protected MediaPrintableArea getDefaultPrintableArea(PageFormat page,597double w, double h) {598double ix, iw, iy, ih;599if (w >= 72.0 * 6.0) {600ix = 72.0;601iw = w - 2 * 72.0;602} else {603ix = w / 6.0;604iw = w * 0.75;605}606if (h >= 72.0 * 6.0) {607iy = 72.0;608ih = h - 2 * 72.0;609} else {610iy = h / 6.0;611ih = h * 0.75;612}613614return new MediaPrintableArea((float) (ix / DPI), (float) (iy / DPI),615(float) (iw / DPI), (float) (ih / DPI), MediaPrintableArea.INCH);616}617618protected void updatePageAttributes(PrintService service,619PageFormat page) {620if (this.attributes == null) {621this.attributes = new HashPrintRequestAttributeSet();622}623624updateAttributesWithPageFormat(service, page, this.attributes);625}626627protected void updateAttributesWithPageFormat(PrintService service,628PageFormat page,629PrintRequestAttributeSet pageAttributes) {630if (service == null || page == null || pageAttributes == null) {631return;632}633634float x = (float)Math.rint(635(page.getPaper().getWidth()*Size2DSyntax.INCH)/636(72.0))/(float)Size2DSyntax.INCH;637float y = (float)Math.rint(638(page.getPaper().getHeight()*Size2DSyntax.INCH)/639(72.0))/(float)Size2DSyntax.INCH;640641// We should limit the list where we search the matching642// media, this will prevent mapping to wrong media ex. Ledger643// can be mapped to B. Especially useful when creating644// custom MediaSize.645Media[] mediaList = (Media[])service.getSupportedAttributeValues(646Media.class, null, null);647Media media = null;648try {649media = CustomMediaSizeName.findMedia(mediaList, x, y,650Size2DSyntax.INCH);651} catch (IllegalArgumentException iae) {652}653if ((media == null) ||654!(service.isAttributeValueSupported(media, null, null))) {655media = (Media)service.getDefaultAttributeValue(Media.class);656}657658OrientationRequested orient;659switch (page.getOrientation()) {660case PageFormat.LANDSCAPE :661orient = OrientationRequested.LANDSCAPE;662break;663case PageFormat.REVERSE_LANDSCAPE:664orient = OrientationRequested.REVERSE_LANDSCAPE;665break;666default:667orient = OrientationRequested.PORTRAIT;668}669670if (media != null) {671pageAttributes.add(media);672}673pageAttributes.add(orient);674675float ix = (float)(page.getPaper().getImageableX()/DPI);676float iw = (float)(page.getPaper().getImageableWidth()/DPI);677float iy = (float)(page.getPaper().getImageableY()/DPI);678float ih = (float)(page.getPaper().getImageableHeight()/DPI);679680if (ix < 0) ix = 0; if (iy < 0) iy = 0;681if (iw <= 0) iw = (float)(page.getPaper().getWidth()/DPI) - (ix*2);682683// If iw is still negative, it means ix is too large to print684// anything inside printable area if we have to leave the same margin685// in the right side of paper so we go back to default mpa values686if (iw < 0) iw = 0;687688if (ih <= 0) ih = (float)(page.getPaper().getHeight()/DPI) - (iy*2);689690// If ih is still negative, it means iy is too large to print691// anything inside printable area if we have to leave the same margin692// in the bottom side of paper so we go back to default mpa values693if (ih < 0) ih = 0;694try {695pageAttributes.add(new MediaPrintableArea(ix, iy, iw, ih,696MediaPrintableArea.INCH));697} catch (IllegalArgumentException iae) {698}699}700701/**702* Display a dialog to the user allowing the modification of a703* PageFormat instance.704* The {@code page} argument is used to initialize controls705* in the page setup dialog.706* If the user cancels the dialog, then the method returns the707* original {@code page} object unmodified.708* If the user okays the dialog then the method returns a new709* PageFormat object with the indicated changes.710* In either case the original {@code page} object will711* not be modified.712* @param page the default PageFormat presented to the user713* for modification714* @return the original {@code page} object if the dialog715* is cancelled, or a new PageFormat object containing716* the format indicated by the user if the dialog is717* acknowledged718* @exception HeadlessException if GraphicsEnvironment.isHeadless()719* returns true.720* @see java.awt.GraphicsEnvironment#isHeadless721* @since 1.2722*/723public PageFormat pageDialog(PageFormat page)724throws HeadlessException {725if (GraphicsEnvironment.isHeadless()) {726throw new HeadlessException();727}728729final GraphicsConfiguration gc =730GraphicsEnvironment.getLocalGraphicsEnvironment().731getDefaultScreenDevice().getDefaultConfiguration();732733@SuppressWarnings("removal")734PrintService service = java.security.AccessController.doPrivileged(735new java.security.PrivilegedAction<PrintService>() {736public PrintService run() {737PrintService service = getPrintService();738if (service == null) {739ServiceDialog.showNoPrintService(gc);740return null;741}742return service;743}744});745746if (service == null) {747return page;748}749updatePageAttributes(service, page);750751PageFormat newPage = null;752DialogTypeSelection dts =753(DialogTypeSelection)attributes.get(DialogTypeSelection.class);754if (dts == DialogTypeSelection.NATIVE) {755// Remove DialogTypeSelection.NATIVE to prevent infinite loop in756// RasterPrinterJob.757attributes.remove(DialogTypeSelection.class);758newPage = pageDialog(attributes);759// restore attribute760attributes.add(DialogTypeSelection.NATIVE);761} else {762newPage = pageDialog(attributes);763}764765if (newPage == null) {766return page;767} else {768return newPage;769}770}771772/**773* return a PageFormat corresponding to the updated attributes,774* or null if the user cancelled the dialog.775*/776@SuppressWarnings("deprecation")777public PageFormat pageDialog(final PrintRequestAttributeSet attributes)778throws HeadlessException {779if (GraphicsEnvironment.isHeadless()) {780throw new HeadlessException();781}782783DialogTypeSelection dlg =784(DialogTypeSelection)attributes.get(DialogTypeSelection.class);785786// Check for native, note that default dialog is COMMON.787if (dlg == DialogTypeSelection.NATIVE) {788PrintService pservice = getPrintService();789PageFormat pageFrmAttrib = attributeToPageFormat(pservice,790attributes);791setParentWindowID(attributes);792PageFormat page = pageDialog(pageFrmAttrib);793clearParentWindowID();794795// If user cancels the dialog, pageDialog() will return the original796// page object and as per spec, we should return null in that case.797if (page == pageFrmAttrib) {798return null;799}800updateAttributesWithPageFormat(pservice, page, attributes);801return page;802}803804GraphicsConfiguration grCfg = null;805Window w = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();806if (w != null) {807grCfg = w.getGraphicsConfiguration();808} else {809grCfg = GraphicsEnvironment.getLocalGraphicsEnvironment().810getDefaultScreenDevice().getDefaultConfiguration();811}812final GraphicsConfiguration gc = grCfg;813814@SuppressWarnings("removal")815PrintService service = java.security.AccessController.doPrivileged(816new java.security.PrivilegedAction<PrintService>() {817public PrintService run() {818PrintService service = getPrintService();819if (service == null) {820ServiceDialog.showNoPrintService(gc);821return null;822}823return service;824}825});826827if (service == null) {828return null;829}830831// we position the dialog a little beyond the upper-left corner of the window832// which is consistent with the NATIVE page dialog833Rectangle gcBounds = gc.getBounds();834int x = gcBounds.x+50;835int y = gcBounds.y+50;836ServiceDialog pageDialog;837boolean setOnTop = false;838if (onTop != null) {839attributes.add(onTop);840Window owner = onTop.getOwner();841if (owner != null) {842w = owner; // use the one specifed by the app843} else if (DialogOwnerAccessor.getID(onTop) == 0) {844setOnTop = true;845}846}847pageDialog = new ServiceDialog(gc, x, y, service,848DocFlavor.SERVICE_FORMATTED.PAGEABLE,849attributes, w);850if (setOnTop) {851try {852pageDialog.setAlwaysOnTop(true);853} catch (SecurityException e) {854}855}856857Rectangle dlgBounds = pageDialog.getBounds();858859// if portion of dialog is not within the gc boundary860if (!gcBounds.contains(dlgBounds)) {861// check if dialog exceed window bounds at left or bottom862// Then position the dialog by moving it by the amount it exceeds863// the window bounds864// If it results in dialog moving beyond the window bounds at top/left865// then position it at window top/left866if (dlgBounds.x + dlgBounds.width > gcBounds.x + gcBounds.width) {867if ((gcBounds.x + gcBounds.width - dlgBounds.width) > gcBounds.x) {868x = (gcBounds.x + gcBounds.width) - dlgBounds.width;869} else {870x = gcBounds.x;871}872}873if (dlgBounds.y + dlgBounds.height > gcBounds.y + gcBounds.height) {874if ((gcBounds.y + gcBounds.height - dlgBounds.height) > gcBounds.y) {875y = (gcBounds.y + gcBounds.height) - dlgBounds.height;876} else {877y = gcBounds.y;878}879}880pageDialog.setBounds(x, y, dlgBounds.width, dlgBounds.height);881}882pageDialog.show();883884if (pageDialog.getStatus() == ServiceDialog.APPROVE) {885PrintRequestAttributeSet newas =886pageDialog.getAttributes();887Class<?> amCategory = SunAlternateMedia.class;888889if (attributes.containsKey(amCategory) &&890!newas.containsKey(amCategory)) {891attributes.remove(amCategory);892}893attributes.addAll(newas);894return attributeToPageFormat(service, attributes);895} else {896return null;897}898}899900protected PageFormat getPageFormatFromAttributes() {901Pageable pageable = null;902if (attributes == null || attributes.isEmpty() ||903!((pageable = getPageable()) instanceof OpenBook)) {904return null;905}906907PageFormat newPf = attributeToPageFormat(908getPrintService(), attributes);909PageFormat oldPf = null;910if ((oldPf = pageable.getPageFormat(0)) != null) {911// If orientation, media, imageable area attributes are not in912// "attributes" set, then use respective values of the existing913// page format "oldPf".914if (attributes.get(OrientationRequested.class) == null) {915newPf.setOrientation(oldPf.getOrientation());916}917918Paper newPaper = newPf.getPaper();919Paper oldPaper = oldPf.getPaper();920boolean oldPaperValWasSet = false;921if (attributes.get(MediaSizeName.class) == null) {922newPaper.setSize(oldPaper.getWidth(), oldPaper.getHeight());923oldPaperValWasSet = true;924}925if (attributes.get(MediaPrintableArea.class) == null) {926newPaper.setImageableArea(927oldPaper.getImageableX(), oldPaper.getImageableY(),928oldPaper.getImageableWidth(),929oldPaper.getImageableHeight());930oldPaperValWasSet = true;931}932if (oldPaperValWasSet) {933newPf.setPaper(newPaper);934}935}936return newPf;937}938939940/**941* Presents the user a dialog for changing properties of the942* print job interactively.943* The services browsable here are determined by the type of944* service currently installed.945* If the application installed a StreamPrintService on this946* PrinterJob, only the available StreamPrintService (factories) are947* browsable.948*949* @param attributes to store changed properties.950* @return false if the user cancels the dialog and true otherwise.951* @exception HeadlessException if GraphicsEnvironment.isHeadless()952* returns true.953* @see java.awt.GraphicsEnvironment#isHeadless954*/955@SuppressWarnings("removal")956public boolean printDialog(final PrintRequestAttributeSet attributes)957throws HeadlessException {958if (GraphicsEnvironment.isHeadless()) {959throw new HeadlessException();960}961962DialogTypeSelection dlg =963(DialogTypeSelection)attributes.get(DialogTypeSelection.class);964965// Check for native, note that default dialog is COMMON.966if (dlg == DialogTypeSelection.NATIVE) {967this.attributes = attributes;968try {969debug_println("calling setAttributes in printDialog");970setAttributes(attributes);971972} catch (PrinterException e) {973974}975976setParentWindowID(attributes);977boolean ret = printDialog();978clearParentWindowID();979this.attributes = attributes;980return ret;981982}983984/* A security check has already been performed in the985* java.awt.print.printerJob.getPrinterJob method.986* So by the time we get here, it is OK for the current thread987* to print either to a file (from a Dialog we control!) or988* to a chosen printer.989*990* We raise privilege when we put up the dialog, to avoid991* the "warning applet window" banner.992*/993GraphicsConfiguration grCfg = null;994Window w = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();995if (w != null) {996grCfg = w.getGraphicsConfiguration();997/* Add DialogOwner attribute to set the owner of this print dialog998* only if it is not set already999* (it might be set in java.awt.PrintJob.printDialog)1000*/1001if (attributes.get(DialogOwner.class) == null) {1002attributes.add(new DialogOwner(w));1003}1004} else {1005grCfg = GraphicsEnvironment.getLocalGraphicsEnvironment().1006getDefaultScreenDevice().getDefaultConfiguration();1007}1008final GraphicsConfiguration gc = grCfg;10091010PrintService service = java.security.AccessController.doPrivileged(1011new java.security.PrivilegedAction<PrintService>() {1012public PrintService run() {1013PrintService service = getPrintService();1014if (service == null) {1015ServiceDialog.showNoPrintService(gc);1016return null;1017}1018return service;1019}1020});10211022if (service == null) {1023return false;1024}10251026PrintService[] services;1027StreamPrintServiceFactory[] spsFactories = null;1028if (service instanceof StreamPrintService) {1029spsFactories = lookupStreamPrintServices(null);1030services = new StreamPrintService[spsFactories.length];1031for (int i=0; i<spsFactories.length; i++) {1032services[i] = spsFactories[i].getPrintService(null);1033}1034} else {1035services = java.security.AccessController.doPrivileged(1036new java.security.PrivilegedAction<PrintService[]>() {1037public PrintService[] run() {1038PrintService[] services = PrinterJob.lookupPrintServices();1039return services;1040}1041});10421043if ((services == null) || (services.length == 0)) {1044/*1045* No services but default PrintService exists?1046* Create services using defaultService.1047*/1048services = new PrintService[1];1049services[0] = service;1050}1051}10521053// we position the dialog a little beyond the upper-left corner of the window1054// which is consistent with the NATIVE print dialog1055int x = 50;1056int y = 50;1057PrintService newService;1058// temporarily add an attribute pointing back to this job.1059PrinterJobWrapper jobWrapper = new PrinterJobWrapper(this);1060attributes.add(jobWrapper);1061PageRanges pgRng = (PageRanges)attributes.get(PageRanges.class);1062if (pgRng == null && mDocument.getNumberOfPages() > 1) {1063attributes.add(new PageRanges(1, mDocument.getNumberOfPages()));1064}1065try {1066newService =1067ServiceUI.printDialog(gc, x, y,1068services, service,1069DocFlavor.SERVICE_FORMATTED.PAGEABLE,1070attributes);1071} catch (IllegalArgumentException iae) {1072newService = ServiceUI.printDialog(gc, x, y,1073services, services[0],1074DocFlavor.SERVICE_FORMATTED.PAGEABLE,1075attributes);1076}1077attributes.remove(PrinterJobWrapper.class);1078attributes.remove(DialogOwner.class);10791080if (newService == null) {1081return false;1082}10831084if (!service.equals(newService)) {1085try {1086setPrintService(newService);1087} catch (PrinterException e) {1088/*1089* The only time it would throw an exception is when1090* newService is no longer available but we should still1091* select this printer.1092*/1093myService = newService;1094}1095}1096return true;1097}10981099/**1100* Presents the user a dialog for changing properties of the1101* print job interactively.1102* @return false if the user cancels the dialog and1103* true otherwise.1104* @exception HeadlessException if GraphicsEnvironment.isHeadless()1105* returns true.1106* @see java.awt.GraphicsEnvironment#isHeadless1107*/1108public boolean printDialog() throws HeadlessException {11091110if (GraphicsEnvironment.isHeadless()) {1111throw new HeadlessException();1112}11131114PrintRequestAttributeSet attributes =1115new HashPrintRequestAttributeSet();1116attributes.add(new Copies(getCopies()));1117attributes.add(new JobName(getJobName(), null));1118boolean doPrint = printDialog(attributes);1119if (doPrint) {1120JobName jobName = (JobName)attributes.get(JobName.class);1121if (jobName != null) {1122setJobName(jobName.getValue());1123}1124Copies copies = (Copies)attributes.get(Copies.class);1125if (copies != null) {1126setCopies(copies.getValue());1127}11281129Destination dest = (Destination)attributes.get(Destination.class);11301131if (dest != null) {1132try {1133mDestType = RasterPrinterJob.FILE;1134mDestination = (new File(dest.getURI())).getPath();1135} catch (Exception e) {1136mDestination = "out.prn";1137PrintService ps = getPrintService();1138if (ps != null) {1139Destination defaultDest = (Destination)ps.1140getDefaultAttributeValue(Destination.class);1141if (defaultDest != null) {1142mDestination = (new File(defaultDest.getURI())).getPath();1143}1144}1145}1146} else {1147mDestType = RasterPrinterJob.PRINTER;1148PrintService ps = getPrintService();1149if (ps != null) {1150mDestination = ps.getName();1151}1152}1153}11541155return doPrint;1156}11571158/**1159* The pages in the document to be printed by this PrinterJob1160* are drawn by the Printable object 'painter'. The PageFormat1161* for each page is the default page format.1162* @param painter Called to render each page of the document.1163*/1164public void setPrintable(Printable painter) {1165setPageable(new OpenBook(defaultPage(new PageFormat()), painter));1166}11671168/**1169* The pages in the document to be printed by this PrinterJob1170* are drawn by the Printable object 'painter'. The PageFormat1171* of each page is 'format'.1172* @param painter Called to render each page of the document.1173* @param format The size and orientation of each page to1174* be printed.1175*/1176public void setPrintable(Printable painter, PageFormat format) {1177setPageable(new OpenBook(format, painter));1178updatePageAttributes(getPrintService(), format);1179}11801181/**1182* The pages in the document to be printed are held by the1183* Pageable instance 'document'. 'document' will be queried1184* for the number of pages as well as the PageFormat and1185* Printable for each page.1186* @param document The document to be printed. It may not be null.1187* @exception NullPointerException the Pageable passed in was null.1188* @see PageFormat1189* @see Printable1190*/1191public void setPageable(Pageable document) throws NullPointerException {1192if (document != null) {1193mDocument = document;11941195} else {1196throw new NullPointerException();1197}1198}11991200protected void initPrinter() {1201return;1202}12031204protected boolean isSupportedValue(Attribute attrval,1205PrintRequestAttributeSet attrset) {1206PrintService ps = getPrintService();1207return1208(attrval != null && ps != null &&1209ps.isAttributeValueSupported(attrval,1210DocFlavor.SERVICE_FORMATTED.PAGEABLE,1211attrset));1212}12131214/**1215* Set the device resolution.1216* Overridden and used only by the postscript code.1217* Windows code pulls the information from the attribute set itself.1218*/1219protected void setXYRes(double x, double y) {1220}12211222/* subclasses may need to pull extra information out of the attribute set1223* They can override this method & call super.setAttributes()1224*/1225protected void setAttributes(PrintRequestAttributeSet attributes)1226throws PrinterException {1227/* reset all values to defaults */1228setCollated(false);1229sidesAttr = null;1230printerResAttr = null;1231pageRangesAttr = null;1232copiesAttr = 0;1233jobNameAttr = null;1234userNameAttr = null;1235destinationAttr = null;1236collateAttReq = false;12371238PrintService service = getPrintService();1239if (attributes == null || service == null) {1240return;1241}12421243boolean fidelity = false;1244Fidelity attrFidelity = (Fidelity)attributes.get(Fidelity.class);1245if (attrFidelity != null && attrFidelity == Fidelity.FIDELITY_TRUE) {1246fidelity = true;1247}12481249if (fidelity == true) {1250AttributeSet unsupported =1251service.getUnsupportedAttributes(1252DocFlavor.SERVICE_FORMATTED.PAGEABLE,1253attributes);1254if (unsupported != null) {1255throw new PrinterException("Fidelity cannot be satisfied");1256}1257}12581259/*1260* Since we have verified supported values if fidelity is true,1261* we can either ignore unsupported values, or substitute a1262* reasonable alternative1263*/12641265SheetCollate collateAttr =1266(SheetCollate)attributes.get(SheetCollate.class);1267if (isSupportedValue(collateAttr, attributes)) {1268setCollated(collateAttr == SheetCollate.COLLATED);1269}12701271sidesAttr = (Sides)attributes.get(Sides.class);1272if (!isSupportedValue(sidesAttr, attributes)) {1273sidesAttr = Sides.ONE_SIDED;1274}12751276printerResAttr = (PrinterResolution)attributes.get(PrinterResolution.class);1277if (service.isAttributeCategorySupported(PrinterResolution.class)) {1278if (!isSupportedValue(printerResAttr, attributes)) {1279printerResAttr = (PrinterResolution)1280service.getDefaultAttributeValue(PrinterResolution.class);1281}1282if (printerResAttr != null) {1283double xr =1284printerResAttr.getCrossFeedResolution(ResolutionSyntax.DPI);1285double yr = printerResAttr.getFeedResolution(ResolutionSyntax.DPI);1286setXYRes(xr, yr);1287}1288}12891290pageRangesAttr = (PageRanges)attributes.get(PageRanges.class);1291if (!isSupportedValue(pageRangesAttr, attributes)) {1292pageRangesAttr = null;1293setPageRange(-1, -1);1294} else {1295if ((SunPageSelection)attributes.get(SunPageSelection.class)1296== SunPageSelection.RANGE) {1297// get to, from, min, max page ranges1298int[][] range = pageRangesAttr.getMembers();1299// setPageRanges uses 0-based indexing so we subtract 11300setPageRange(range[0][0] - 1, range[0][1] - 1);1301} else {1302setPageRange(-1, - 1);1303}1304}13051306Copies copies = (Copies)attributes.get(Copies.class);1307if (isSupportedValue(copies, attributes) ||1308(!fidelity && copies != null)) {1309copiesAttr = copies.getValue();1310setCopies(copiesAttr);1311} else {1312copiesAttr = getCopies();1313}13141315Destination destination =1316(Destination)attributes.get(Destination.class);13171318if (isSupportedValue(destination, attributes)) {1319try {1320// Old code (new File(destination.getURI())).getPath()1321// would generate a "URI is not hierarchical" IAE1322// for "file:out.prn" so we use getSchemeSpecificPart instead1323destinationAttr = "" + new File(destination.getURI().1324getSchemeSpecificPart());1325} catch (Exception e) { // paranoid exception1326Destination defaultDest = (Destination)service.1327getDefaultAttributeValue(Destination.class);1328if (defaultDest != null) {1329destinationAttr = "" + new File(defaultDest.getURI().1330getSchemeSpecificPart());1331}1332}1333}13341335JobSheets jobSheets = (JobSheets)attributes.get(JobSheets.class);1336if (jobSheets != null) {1337noJobSheet = jobSheets == JobSheets.NONE;1338}13391340JobName jobName = (JobName)attributes.get(JobName.class);1341if (isSupportedValue(jobName, attributes) ||1342(!fidelity && jobName != null)) {1343jobNameAttr = jobName.getValue();1344setJobName(jobNameAttr);1345} else {1346jobNameAttr = getJobName();1347}13481349RequestingUserName userName =1350(RequestingUserName)attributes.get(RequestingUserName.class);1351if (isSupportedValue(userName, attributes) ||1352(!fidelity && userName != null)) {1353userNameAttr = userName.getValue();1354} else {1355try {1356userNameAttr = getUserName();1357} catch (SecurityException e) {1358userNameAttr = "";1359}1360}13611362/* OpenBook is used internally only when app uses Printable.1363* This is the case when we use the values from the attribute set.1364*/1365Media media = (Media)attributes.get(Media.class);1366OrientationRequested orientReq =1367(OrientationRequested)attributes.get(OrientationRequested.class);1368MediaPrintableArea mpa =1369(MediaPrintableArea)attributes.get(MediaPrintableArea.class);13701371if ((orientReq != null || media != null || mpa != null) &&1372getPageable() instanceof OpenBook) {13731374/* We could almost(!) use PrinterJob.getPageFormat() except1375* here we need to start with the PageFormat from the OpenBook :1376*/1377Pageable pageable = getPageable();1378Printable printable = pageable.getPrintable(0);1379PageFormat pf = (PageFormat)pageable.getPageFormat(0).clone();1380Paper paper = pf.getPaper();13811382/* If there's a media but no media printable area, we can try1383* to retrieve the default value for mpa and use that.1384*/1385if (mpa == null && media != null &&1386service.1387isAttributeCategorySupported(MediaPrintableArea.class)) {1388Object mpaVals = service.1389getSupportedAttributeValues(MediaPrintableArea.class,1390null, attributes);1391if (mpaVals instanceof MediaPrintableArea[] &&1392((MediaPrintableArea[])mpaVals).length > 0) {1393mpa = ((MediaPrintableArea[])mpaVals)[0];1394}1395}13961397if (isSupportedValue(orientReq, attributes) ||1398(!fidelity && orientReq != null)) {1399int orient;1400if (orientReq.equals(OrientationRequested.REVERSE_LANDSCAPE)) {1401orient = PageFormat.REVERSE_LANDSCAPE;1402} else if (orientReq.equals(OrientationRequested.LANDSCAPE)) {1403orient = PageFormat.LANDSCAPE;1404} else {1405orient = PageFormat.PORTRAIT;1406}1407pf.setOrientation(orient);1408}14091410if (isSupportedValue(media, attributes) ||1411(!fidelity && media != null)) {1412if (media instanceof MediaSizeName) {1413MediaSizeName msn = (MediaSizeName)media;1414MediaSize msz = MediaSize.getMediaSizeForName(msn);1415if (msz != null) {1416float paperWid = msz.getX(MediaSize.INCH) * 72.0f;1417float paperHgt = msz.getY(MediaSize.INCH) * 72.0f;1418paper.setSize(paperWid, paperHgt);1419if (mpa == null) {1420paper.setImageableArea(72.0, 72.0,1421paperWid-144.0,1422paperHgt-144.0);1423}1424}1425}1426}14271428if (isSupportedValue(mpa, attributes) ||1429(!fidelity && mpa != null)) {1430float [] printableArea =1431mpa.getPrintableArea(MediaPrintableArea.INCH);1432for (int i=0; i < printableArea.length; i++) {1433printableArea[i] = printableArea[i]*72.0f;1434}1435paper.setImageableArea(printableArea[0], printableArea[1],1436printableArea[2], printableArea[3]);1437}14381439pf.setPaper(paper);1440pf = validatePage(pf);1441setPrintable(printable, pf);1442} else {1443// for AWT where pageable is not an instance of OpenBook,1444// we need to save paper info1445this.attributes = attributes;1446}14471448}14491450/*1451* Services we don't recognize as built-in services can't be1452* implemented as subclasses of PrinterJob, therefore we create1453* a DocPrintJob from their service and pass a Doc representing1454* the application's printjob1455*/1456// MacOSX - made protected so subclasses can reference it.1457protected void spoolToService(PrintService psvc,1458PrintRequestAttributeSet attributes)1459throws PrinterException {14601461if (psvc == null) {1462throw new PrinterException("No print service found.");1463}14641465DocPrintJob job = psvc.createPrintJob();1466Doc doc = new PageableDoc(getPageable());1467if (attributes == null) {1468attributes = new HashPrintRequestAttributeSet();1469attributes.add(new Copies(getCopies()));1470attributes.add(new JobName(getJobName(), null));1471}1472try {1473job.print(doc, attributes);1474} catch (PrintException e) {1475throw new PrinterException(e.toString());1476}1477}14781479/**1480* Prints a set of pages.1481* @exception java.awt.print.PrinterException an error in the print system1482* caused the job to be aborted1483* @see java.awt.print.Book1484* @see java.awt.print.Pageable1485* @see java.awt.print.Printable1486*/1487public void print() throws PrinterException {1488print(attributes);1489}14901491public static boolean debugPrint = false;1492protected void debug_println(String str) {1493if (debugPrint) {1494System.out.println("RasterPrinterJob "+str+" "+this);1495}1496}14971498public void print(PrintRequestAttributeSet attributes)1499throws PrinterException {15001501/*1502* In the future PrinterJob will probably always dispatch1503* the print job to the PrintService.1504* This is how third party 2D Print Services will be invoked1505* when applications use the PrinterJob API.1506* However the JRE's concrete PrinterJob implementations have1507* not yet been re-worked to be implemented as standalone1508* services, and are implemented only as subclasses of PrinterJob.1509* So here we dispatch only those services we do not recognize1510* as implemented through platform subclasses of PrinterJob1511* (and this class).1512*/1513PrintService psvc = getPrintService();1514debug_println("psvc = "+psvc);1515if (psvc == null) {1516throw new PrinterException("No print service found.");1517}15181519// Check the list of services. This service may have been1520// deleted already1521PrinterState prnState = psvc.getAttribute(PrinterState.class);1522if (prnState == PrinterState.STOPPED) {1523PrinterStateReasons prnStateReasons =1524psvc.getAttribute(PrinterStateReasons.class);1525if ((prnStateReasons != null) &&1526(prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN)))1527{1528throw new PrinterException("PrintService is no longer available.");1529}1530}15311532if ((psvc.getAttribute(PrinterIsAcceptingJobs.class)) ==1533PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS) {1534throw new PrinterException("Printer is not accepting job.");1535}15361537/*1538* Check the default job-sheet value on underlying platform. If IPP1539* reports job-sheets=none, then honour that and modify noJobSheet since1540* by default, noJobSheet is false which mean jdk will print banner page.1541* This is because if "attributes" is null (if user directly calls print()1542* without specifying any attributes and without showing printdialog) then1543* setAttribute will return without changing noJobSheet value.1544* Also, we do this before setAttributes() call so as to allow the user1545* to override this via explicitly adding JobSheets attributes to1546* PrintRequestAttributeSet while calling print(attributes)1547*/1548JobSheets js = (JobSheets)psvc.getDefaultAttributeValue(JobSheets.class);1549if (js != null && js.equals(JobSheets.NONE)) {1550noJobSheet = true;1551}15521553if ((psvc instanceof SunPrinterJobService) &&1554((SunPrinterJobService)psvc).usesClass(getClass())) {1555setAttributes(attributes);1556// throw exception for invalid destination1557if (destinationAttr != null) {1558validateDestination(destinationAttr);1559}1560} else {1561spoolToService(psvc, attributes);1562return;1563}1564/* We need to make sure that the collation and copies1565* settings are initialised */1566initPrinter();15671568int numCollatedCopies = getCollatedCopies();1569int numNonCollatedCopies = getNoncollatedCopies();1570debug_println("getCollatedCopies() "+numCollatedCopies1571+ " getNoncollatedCopies() "+ numNonCollatedCopies);15721573/* Get the range of pages we are to print. If the1574* last page to print is unknown, then we print to1575* the end of the document. Note that firstPage1576* and lastPage are 0 based page indices.1577*/1578int numPages = mDocument.getNumberOfPages();1579if (numPages == 0) {1580return;1581}15821583int firstPage = getFirstPage();1584int lastPage = getLastPage();1585if(lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES){1586int totalPages = mDocument.getNumberOfPages();1587if (totalPages != Pageable.UNKNOWN_NUMBER_OF_PAGES) {1588lastPage = mDocument.getNumberOfPages() - 1;1589}1590}15911592try {1593synchronized (this) {1594performingPrinting = true;1595userCancelled = false;1596}15971598startDoc();1599if (isCancelled()) {1600cancelDoc();1601}16021603// PageRanges can be set even if RANGE is not selected1604// so we need to check if it is selected.1605boolean rangeIsSelected = true;1606if (attributes != null) {1607SunPageSelection pages =1608(SunPageSelection)attributes.get(SunPageSelection.class);1609if ((pages != null) && (pages != SunPageSelection.RANGE)) {1610rangeIsSelected = false;1611}1612}161316141615debug_println("after startDoc rangeSelected? "+rangeIsSelected1616+ " numNonCollatedCopies "+ numNonCollatedCopies);161716181619/* Three nested loops iterate over the document. The outer loop1620* counts the number of collated copies while the inner loop1621* counts the number of nonCollated copies. Normally, one of1622* these two loops will only execute once; that is we will1623* either print collated copies or noncollated copies. The1624* middle loop iterates over the pages.1625* If a PageRanges attribute is used, it constrains the pages1626* that are imaged. If a platform subclass (though a user dialog)1627* requests a page range via setPageRange(). it too can1628* constrain the page ranges that are imaged.1629* It is expected that only one of these will be used in a1630* job but both should be able to co-exist.1631*/1632for(int collated = 0; collated < numCollatedCopies; collated++) {1633for(int i = firstPage, pageResult = Printable.PAGE_EXISTS;1634(i <= lastPage ||1635lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES)1636&& pageResult == Printable.PAGE_EXISTS;1637i++)1638{16391640if ((pageRangesAttr != null) && rangeIsSelected ){1641int nexti = pageRangesAttr.next(i);1642if (nexti == -1) {1643break;1644} else if (nexti != i+1) {1645continue;1646}1647}16481649for(int nonCollated = 0;1650nonCollated < numNonCollatedCopies1651&& pageResult == Printable.PAGE_EXISTS;1652nonCollated++)1653{1654if (isCancelled()) {1655cancelDoc();1656}1657debug_println("printPage "+i);1658pageResult = printPage(mDocument, i);16591660}1661}1662}16631664if (isCancelled()) {1665cancelDoc();1666}16671668} finally {1669// reset previousPaper in case this job is invoked again.1670previousPaper = null;1671synchronized (this) {1672if (performingPrinting) {1673endDoc();1674}1675performingPrinting = false;1676notify();1677}1678}1679}16801681protected void validateDestination(String dest) throws PrinterException {1682if (dest == null) {1683return;1684}1685// dest is null for Destination(new URI(""))1686// because isAttributeValueSupported returns false in setAttributes16871688// Destination(new URI(" ")) throws URISyntaxException1689File f = new File(dest);1690try {1691// check if this is a new file and if filename chars are valid1692if (f.createNewFile()) {1693f.delete();1694}1695} catch (IOException ioe) {1696throw new PrinterException("Cannot write to file:"+1697dest);1698} catch (SecurityException se) {1699//There is already file read/write access so at this point1700// only delete access is denied. Just ignore it because in1701// most cases the file created in createNewFile gets overwritten1702// anyway.1703}17041705File pFile = f.getParentFile();1706if ((f.exists() &&1707(!f.isFile() || !f.canWrite())) ||1708((pFile != null) &&1709(!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {1710if (f.exists()) {1711f.delete();1712}1713throw new PrinterException("Cannot write to file:"+1714dest);1715}1716}17171718/**1719* updates a Paper object to reflect the current printer's selected1720* paper size and imageable area for that paper size.1721* Default implementation copies settings from the original, applies1722* applies some validity checks, changes them only if they are1723* clearly unreasonable, then sets them into the new Paper.1724* Subclasses are expected to override this method to make more1725* informed decisons.1726*/1727protected void validatePaper(Paper origPaper, Paper newPaper) {1728if (origPaper == null || newPaper == null) {1729return;1730} else {1731double wid = origPaper.getWidth();1732double hgt = origPaper.getHeight();1733double ix = origPaper.getImageableX();1734double iy = origPaper.getImageableY();1735double iw = origPaper.getImageableWidth();1736double ih = origPaper.getImageableHeight();17371738/* Assume any +ve values are legal. Overall paper dimensions1739* take precedence. Make sure imageable area fits on the paper.1740*/1741Paper defaultPaper = new Paper();1742wid = ((wid > 0.0) ? wid : defaultPaper.getWidth());1743hgt = ((hgt > 0.0) ? hgt : defaultPaper.getHeight());1744ix = ((ix > 0.0) ? ix : defaultPaper.getImageableX());1745iy = ((iy > 0.0) ? iy : defaultPaper.getImageableY());1746iw = ((iw > 0.0) ? iw : defaultPaper.getImageableWidth());1747ih = ((ih > 0.0) ? ih : defaultPaper.getImageableHeight());1748/* full width/height is not likely to be imageable, but since we1749* don't know the limits we have to allow it1750*/1751if (iw > wid) {1752iw = wid;1753}1754if (ih > hgt) {1755ih = hgt;1756}1757if ((ix + iw) > wid) {1758ix = wid - iw;1759}1760if ((iy + ih) > hgt) {1761iy = hgt - ih;1762}1763newPaper.setSize(wid, hgt);1764newPaper.setImageableArea(ix, iy, iw, ih);1765}1766}17671768/**1769* The passed in PageFormat will be copied and altered to describe1770* the default page size and orientation of the PrinterJob's1771* current printer.1772* Platform subclasses which can access the actual default paper size1773* for a printer may override this method.1774*/1775public PageFormat defaultPage(PageFormat page) {1776PageFormat newPage = (PageFormat)page.clone();1777newPage.setOrientation(PageFormat.PORTRAIT);1778Paper newPaper = new Paper();1779double ptsPerInch = 72.0;1780double w, h;1781Media media = null;17821783PrintService service = getPrintService();1784if (service != null) {1785MediaSize size;1786media =1787(Media)service.getDefaultAttributeValue(Media.class);17881789if (media instanceof MediaSizeName &&1790((size = MediaSize.getMediaSizeForName((MediaSizeName)media)) !=1791null)) {1792w = size.getX(MediaSize.INCH) * ptsPerInch;1793h = size.getY(MediaSize.INCH) * ptsPerInch;1794newPaper.setSize(w, h);1795newPaper.setImageableArea(ptsPerInch, ptsPerInch,1796w - 2.0*ptsPerInch,1797h - 2.0*ptsPerInch);1798newPage.setPaper(newPaper);1799return newPage;18001801}1802}18031804/* Default to A4 paper outside North America.1805*/1806String defaultCountry = Locale.getDefault().getCountry();1807if (!Locale.getDefault().equals(Locale.ENGLISH) && // ie "C"1808defaultCountry != null &&1809!defaultCountry.equals(Locale.US.getCountry()) &&1810!defaultCountry.equals(Locale.CANADA.getCountry())) {18111812double mmPerInch = 25.4;1813w = Math.rint((210.0*ptsPerInch)/mmPerInch);1814h = Math.rint((297.0*ptsPerInch)/mmPerInch);1815newPaper.setSize(w, h);1816newPaper.setImageableArea(ptsPerInch, ptsPerInch,1817w - 2.0*ptsPerInch,1818h - 2.0*ptsPerInch);1819}18201821newPage.setPaper(newPaper);18221823return newPage;1824}18251826/**1827* The passed in PageFormat is cloned and altered to be usable on1828* the PrinterJob's current printer.1829*/1830public PageFormat validatePage(PageFormat page) {1831PageFormat newPage = (PageFormat)page.clone();1832Paper newPaper = new Paper();1833validatePaper(newPage.getPaper(), newPaper);1834newPage.setPaper(newPaper);18351836return newPage;1837}18381839/**1840* Set the number of copies to be printed.1841*/1842public void setCopies(int copies) {1843mNumCopies = copies;1844}18451846/**1847* Get the number of copies to be printed.1848*/1849public int getCopies() {1850return mNumCopies;1851}18521853/* Used when executing a print job where an attribute set may1854* over ride API values.1855*/1856protected int getCopiesInt() {1857return (copiesAttr > 0) ? copiesAttr : getCopies();1858}18591860/**1861* Get the name of the printing user.1862* The caller must have security permission to read system properties.1863*/1864public String getUserName() {1865return System.getProperty("user.name");1866}18671868/* Used when executing a print job where an attribute set may1869* over ride API values.1870*/1871protected String getUserNameInt() {1872if (userNameAttr != null) {1873return userNameAttr;1874} else {1875try {1876return getUserName();1877} catch (SecurityException e) {1878return "";1879}1880}1881}18821883/**1884* Set the name of the document to be printed.1885* The document name can not be null.1886*/1887public void setJobName(String jobName) {1888if (jobName != null) {1889mDocName = jobName;1890} else {1891throw new NullPointerException();1892}1893}18941895/**1896* Get the name of the document to be printed.1897*/1898public String getJobName() {1899return mDocName;1900}19011902/* Used when executing a print job where an attribute set may1903* over ride API values.1904*/1905protected String getJobNameInt() {1906return (jobNameAttr != null) ? jobNameAttr : getJobName();1907}19081909/**1910* Set the range of pages from a Book to be printed.1911* Both 'firstPage' and 'lastPage' are zero based1912* page indices. If either parameter is less than1913* zero then the page range is set to be from the1914* first page to the last.1915*/1916protected void setPageRange(int firstPage, int lastPage) {1917if(firstPage >= 0 && lastPage >= 0) {1918mFirstPage = firstPage;1919mLastPage = lastPage;1920if(mLastPage < mFirstPage) mLastPage = mFirstPage;1921} else {1922mFirstPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;1923mLastPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;1924}1925}19261927/**1928* Return the zero based index of the first page to1929* be printed in this job.1930*/1931protected int getFirstPage() {1932return mFirstPage == Book.UNKNOWN_NUMBER_OF_PAGES ? 0 : mFirstPage;1933}19341935/**1936* Return the zero based index of the last page to1937* be printed in this job.1938*/1939protected int getLastPage() {1940return mLastPage;1941}19421943/**1944* Set whether copies should be collated or not.1945* Two collated copies of a three page document1946* print in this order: 1, 2, 3, 1, 2, 3 while1947* uncollated copies print in this order:1948* 1, 1, 2, 2, 3, 3.1949* This is set when request is using an attribute set.1950*/1951protected void setCollated(boolean collate) {1952mCollate = collate;1953collateAttReq = true;1954}19551956/**1957* Return true if collated copies will be printed as determined1958* in an attribute set.1959*/1960protected boolean isCollated() {1961return mCollate;1962}19631964protected final int getSelectAttrib() {1965if (attributes != null) {1966SunPageSelection pages =1967(SunPageSelection)attributes.get(SunPageSelection.class);1968if (pages == SunPageSelection.RANGE) {1969return PD_PAGENUMS;1970} else if (pages == SunPageSelection.SELECTION) {1971return PD_SELECTION;1972} else if (pages == SunPageSelection.ALL) {1973return PD_ALLPAGES;1974}1975}1976return PD_NOSELECTION;1977}19781979//returns 1-based index for "From" page1980protected final int getFromPageAttrib() {1981if (attributes != null) {1982PageRanges pageRangesAttr =1983(PageRanges)attributes.get(PageRanges.class);1984if (pageRangesAttr != null) {1985int[][] range = pageRangesAttr.getMembers();1986return range[0][0];1987}1988}1989return getMinPageAttrib();1990}19911992//returns 1-based index for "To" page1993protected final int getToPageAttrib() {1994if (attributes != null) {1995PageRanges pageRangesAttr =1996(PageRanges)attributes.get(PageRanges.class);1997if (pageRangesAttr != null) {1998int[][] range = pageRangesAttr.getMembers();1999return range[range.length-1][1];2000}2001}2002return getMaxPageAttrib();2003}20042005protected final int getMinPageAttrib() {2006if (attributes != null) {2007SunMinMaxPage s =2008(SunMinMaxPage)attributes.get(SunMinMaxPage.class);2009if (s != null) {2010return s.getMin();2011}2012}2013return 1;2014}20152016protected final int getMaxPageAttrib() {2017if (attributes != null) {2018SunMinMaxPage s =2019(SunMinMaxPage)attributes.get(SunMinMaxPage.class);2020if (s != null) {2021return s.getMax();2022}2023}20242025Pageable pageable = getPageable();2026if (pageable != null) {2027int numPages = pageable.getNumberOfPages();2028if (numPages <= Pageable.UNKNOWN_NUMBER_OF_PAGES) {2029numPages = MAX_UNKNOWN_PAGES;2030}2031return ((numPages == 0) ? 1 : numPages);2032}20332034return Integer.MAX_VALUE;2035}2036/**2037* Called by the print() method at the start of2038* a print job.2039*/2040protected abstract void startDoc() throws PrinterException;20412042/**2043* Called by the print() method at the end of2044* a print job.2045*/2046protected abstract void endDoc() throws PrinterException;20472048/* Called by cancelDoc */2049protected abstract void abortDoc();20502051// MacOSX - made protected so subclasses can reference it.2052protected void cancelDoc() throws PrinterAbortException {2053abortDoc();2054synchronized (this) {2055userCancelled = false;2056performingPrinting = false;2057notify();2058}2059throw new PrinterAbortException();2060}20612062/**2063* Returns how many times the entire book should2064* be printed by the PrintJob. If the printer2065* itself supports collation then this method2066* should return 1 indicating that the entire2067* book need only be printed once and the copies2068* will be collated and made in the printer.2069*/2070protected int getCollatedCopies() {2071return isCollated() ? getCopiesInt() : 1;2072}20732074/**2075* Returns how many times each page in the book2076* should be consecutively printed by PrintJob.2077* If the printer makes copies itself then this2078* method should return 1.2079*/2080protected int getNoncollatedCopies() {2081return isCollated() ? 1 : getCopiesInt();2082}208320842085/* The printer graphics config is cached on the job, so that it can2086* be created once, and updated only as needed (for now only to change2087* the bounds if when using a Pageable the page sizes changes).2088*/20892090private int deviceWidth, deviceHeight;2091private AffineTransform defaultDeviceTransform;2092private PrinterGraphicsConfig pgConfig;20932094synchronized void setGraphicsConfigInfo(AffineTransform at,2095double pw, double ph) {2096Point2D.Double pt = new Point2D.Double(pw, ph);2097at.transform(pt, pt);20982099if (pgConfig == null ||2100defaultDeviceTransform == null ||2101!at.equals(defaultDeviceTransform) ||2102deviceWidth != (int)pt.getX() ||2103deviceHeight != (int)pt.getY()) {21042105deviceWidth = (int)pt.getX();2106deviceHeight = (int)pt.getY();2107defaultDeviceTransform = at;2108pgConfig = null;2109}2110}21112112synchronized PrinterGraphicsConfig getPrinterGraphicsConfig() {2113if (pgConfig != null) {2114return pgConfig;2115}2116String deviceID = "Printer Device";2117PrintService service = getPrintService();2118if (service != null) {2119deviceID = service.toString();2120}2121pgConfig = new PrinterGraphicsConfig(deviceID,2122defaultDeviceTransform,2123deviceWidth, deviceHeight);2124return pgConfig;2125}21262127/**2128* Print a page from the provided document.2129* @return int Printable.PAGE_EXISTS if the page existed and was drawn and2130* Printable.NO_SUCH_PAGE if the page did not exist.2131* @see java.awt.print.Printable2132*/2133protected int printPage(Pageable document, int pageIndex)2134throws PrinterException2135{2136PageFormat page;2137PageFormat origPage;2138Printable painter;2139try {2140origPage = document.getPageFormat(pageIndex);2141page = (PageFormat)origPage.clone();2142painter = document.getPrintable(pageIndex);2143} catch (Exception e) {2144PrinterException pe =2145new PrinterException("Error getting page or printable.[ " +2146e +" ]");2147pe.initCause(e);2148throw pe;2149}21502151/* Get the imageable area from Paper instead of PageFormat2152* because we do not want it adjusted by the page orientation.2153*/2154Paper paper = page.getPaper();2155// if non-portrait and 270 degree landscape rotation2156if (page.getOrientation() != PageFormat.PORTRAIT &&2157landscapeRotates270) {21582159double left = paper.getImageableX();2160double top = paper.getImageableY();2161double width = paper.getImageableWidth();2162double height = paper.getImageableHeight();2163paper.setImageableArea(paper.getWidth()-left-width,2164paper.getHeight()-top-height,2165width, height);2166page.setPaper(paper);2167if (page.getOrientation() == PageFormat.LANDSCAPE) {2168page.setOrientation(PageFormat.REVERSE_LANDSCAPE);2169} else {2170page.setOrientation(PageFormat.LANDSCAPE);2171}2172}21732174double xScale = getXRes() / 72.0;2175double yScale = getYRes() / 72.0;21762177/* The deviceArea is the imageable area in the printer's2178* resolution.2179*/2180Rectangle2D deviceArea =2181new Rectangle2D.Double(paper.getImageableX() * xScale,2182paper.getImageableY() * yScale,2183paper.getImageableWidth() * xScale,2184paper.getImageableHeight() * yScale);21852186/* Build and hold on to a uniform transform so that2187* we can get back to device space at the beginning2188* of each band.2189*/2190AffineTransform uniformTransform = new AffineTransform();21912192/* The scale transform is used to switch from the2193* device space to the user's 72 dpi space.2194*/2195AffineTransform scaleTransform = new AffineTransform();2196scaleTransform.scale(xScale, yScale);21972198/* bandwidth is multiple of 4 as the data is used in a win32 DIB and2199* some drivers behave badly if scanlines aren't multiples of 4 bytes.2200*/2201int bandWidth = (int) deviceArea.getWidth();2202if (bandWidth % 4 != 0) {2203bandWidth += (4 - (bandWidth % 4));2204}2205if (bandWidth <= 0) {2206throw new PrinterException("Paper's imageable width is too small.");2207}22082209int deviceAreaHeight = (int)deviceArea.getHeight();2210if (deviceAreaHeight <= 0) {2211throw new PrinterException("Paper's imageable height is too small.");2212}22132214/* Figure out the number of lines that will fit into2215* our maximum band size. The hard coded 3 reflects the2216* fact that we can only create 24 bit per pixel 3 byte BGR2217* BufferedImages. FIX.2218*/2219int bandHeight = (MAX_BAND_SIZE / bandWidth / 3);22202221int deviceLeft = (int)Math.rint(paper.getImageableX() * xScale);2222int deviceTop = (int)Math.rint(paper.getImageableY() * yScale);22232224/* The device transform is used to move the band down2225* the page using translates. Normally this is all it2226* would do, but since, when printing, the Window's2227* DIB format wants the last line to be first (lowest) in2228* memory, the deviceTransform moves the origin to the2229* bottom of the band and flips the origin. This way the2230* app prints upside down into the band which is the DIB2231* format.2232*/2233AffineTransform deviceTransform = new AffineTransform();2234deviceTransform.translate(-deviceLeft, deviceTop);2235deviceTransform.translate(0, bandHeight);2236deviceTransform.scale(1, -1);22372238/* Create a BufferedImage to hold the band. We set the clip2239* of the band to be tight around the bits so that the2240* application can use it to figure what part of the2241* page needs to be drawn. The clip is never altered in2242* this method, but we do translate the band's coordinate2243* system so that the app will see the clip moving down the2244* page though it s always around the same set of pixels.2245*/2246BufferedImage pBand = new BufferedImage(1, 1,2247BufferedImage.TYPE_3BYTE_BGR);22482249/* Have the app draw into a PeekGraphics object so we can2250* learn something about the needs of the print job.2251*/22522253PeekGraphics peekGraphics = createPeekGraphics(pBand.createGraphics(),2254this);22552256Rectangle2D.Double pageFormatArea =2257new Rectangle2D.Double(page.getImageableX(),2258page.getImageableY(),2259page.getImageableWidth(),2260page.getImageableHeight());2261peekGraphics.transform(scaleTransform);2262peekGraphics.translate(-getPhysicalPrintableX(paper) / xScale,2263-getPhysicalPrintableY(paper) / yScale);2264peekGraphics.transform(new AffineTransform(page.getMatrix()));2265initPrinterGraphics(peekGraphics, pageFormatArea);2266AffineTransform pgAt = peekGraphics.getTransform();22672268/* Update the information used to return a GraphicsConfiguration2269* for this printer device. It needs to be updated per page as2270* not all pages in a job may be the same size (different bounds)2271* The transform is the scaling transform as this corresponds to2272* the default transform for the device. The width and height are2273* those of the paper, not the page format, as we want to describe2274* the bounds of the device in its natural coordinate system of2275* device coordinate whereas a page format may be in a rotated context.2276*/2277setGraphicsConfigInfo(scaleTransform,2278paper.getWidth(), paper.getHeight());2279int pageResult = painter.print(peekGraphics, origPage, pageIndex);2280debug_println("pageResult "+pageResult);2281if (pageResult == Printable.PAGE_EXISTS) {2282debug_println("startPage "+pageIndex);22832284/* We need to check if the paper size is changed.2285* Note that it is not sufficient to ask for the pageformat2286* of "pageIndex-1", since PageRanges mean that pages can be2287* skipped. So we have to look at the actual last paper size used.2288*/2289Paper thisPaper = page.getPaper();2290boolean paperChanged =2291previousPaper == null ||2292thisPaper.getWidth() != previousPaper.getWidth() ||2293thisPaper.getHeight() != previousPaper.getHeight();2294previousPaper = thisPaper;22952296startPage(page, painter, pageIndex, paperChanged);2297Graphics2D pathGraphics = createPathGraphics(peekGraphics, this,2298painter, page,2299pageIndex);23002301/* If we can convert the page directly to the2302* underlying graphics system then we do not2303* need to rasterize. We also may not need to2304* create the 'band' if all the pages can take2305* this path.2306*/2307if (pathGraphics != null) {2308pathGraphics.transform(scaleTransform);2309// user (0,0) should be origin of page, not imageable area2310pathGraphics.translate(-getPhysicalPrintableX(paper) / xScale,2311-getPhysicalPrintableY(paper) / yScale);2312pathGraphics.transform(new AffineTransform(page.getMatrix()));2313initPrinterGraphics(pathGraphics, pageFormatArea);23142315redrawList.clear();23162317AffineTransform initialTx = pathGraphics.getTransform();23182319painter.print(pathGraphics, origPage, pageIndex);23202321for (int i=0;i<redrawList.size();i++) {2322GraphicsState gstate = redrawList.get(i);2323pathGraphics.setTransform(initialTx);2324((PathGraphics)pathGraphics).redrawRegion(2325gstate.region,2326gstate.sx,2327gstate.sy,2328gstate.theClip,2329gstate.theTransform);2330}23312332/* This is the banded-raster printing loop.2333* It should be moved into its own method.2334*/2335} else {2336BufferedImage band = cachedBand;2337if (cachedBand == null ||2338bandWidth != cachedBandWidth ||2339bandHeight != cachedBandHeight) {2340band = new BufferedImage(bandWidth, bandHeight,2341BufferedImage.TYPE_3BYTE_BGR);2342cachedBand = band;2343cachedBandWidth = bandWidth;2344cachedBandHeight = bandHeight;2345}2346Graphics2D bandGraphics = band.createGraphics();23472348Rectangle2D.Double clipArea =2349new Rectangle2D.Double(0, 0, bandWidth, bandHeight);23502351initPrinterGraphics(bandGraphics, clipArea);23522353ProxyGraphics2D painterGraphics =2354new ProxyGraphics2D(bandGraphics, this);23552356Graphics2D clearGraphics = band.createGraphics();2357clearGraphics.setColor(Color.white);23582359/* We need the actual bits of the BufferedImage to send to2360* the native Window's code. 'data' points to the actual2361* pixels. Right now these are in ARGB format with 8 bits2362* per component. We need to use a monochrome BufferedImage2363* for monochrome printers when this is supported by2364* BufferedImage. FIX2365*/2366ByteInterleavedRaster tile = (ByteInterleavedRaster)band.getRaster();2367byte[] data = tile.getDataStorage();23682369/* Loop over the page moving our band down the page,2370* calling the app to render the band, and then send the band2371* to the printer.2372*/2373int deviceBottom = deviceTop + deviceAreaHeight;23742375/* device's printable x,y is really addressable origin2376* we address relative to media origin so when we print a2377* band we need to adjust for the different methods of2378* addressing it.2379*/2380int deviceAddressableX = (int)getPhysicalPrintableX(paper);2381int deviceAddressableY = (int)getPhysicalPrintableY(paper);23822383for (int bandTop = 0; bandTop <= deviceAreaHeight;2384bandTop += bandHeight)2385{23862387/* Put the band back into device space and2388* erase the contents of the band.2389*/2390clearGraphics.fillRect(0, 0, bandWidth, bandHeight);23912392/* Put the band into the correct location on the2393* page. Once the band is moved we translate the2394* device transform so that the band will move down2395* the page on the next iteration of the loop.2396*/2397bandGraphics.setTransform(uniformTransform);2398bandGraphics.transform(deviceTransform);2399deviceTransform.translate(0, -bandHeight);24002401/* Switch the band from device space to user,2402* 72 dpi, space.2403*/2404bandGraphics.transform(scaleTransform);2405bandGraphics.transform(new AffineTransform(page.getMatrix()));24062407Rectangle clip = bandGraphics.getClipBounds();2408clip = pgAt.createTransformedShape(clip).getBounds();24092410if ((clip == null) || peekGraphics.hitsDrawingArea(clip) &&2411(bandWidth > 0 && bandHeight > 0)) {24122413/* if the client has specified an imageable X or Y2414* which is off than the physically addressable2415* area of the page, then we need to adjust for that2416* here so that we pass only non -ve band coordinates2417* We also need to translate by the adjusted amount2418* so that printing appears in the correct place.2419*/2420int bandX = deviceLeft - deviceAddressableX;2421if (bandX < 0) {2422bandGraphics.translate(bandX/xScale,0);2423bandX = 0;2424}2425int bandY = deviceTop + bandTop - deviceAddressableY;2426if (bandY < 0) {2427bandGraphics.translate(0,bandY/yScale);2428bandY = 0;2429}2430/* Have the app's painter image into the band2431* and then send the band to the printer.2432*/2433painterGraphics.setDelegate((Graphics2D) bandGraphics.create());2434painter.print(painterGraphics, origPage, pageIndex);2435painterGraphics.dispose();2436printBand(data, bandX, bandY, bandWidth, bandHeight);2437}2438}24392440clearGraphics.dispose();2441bandGraphics.dispose();24422443}2444debug_println("calling endPage "+pageIndex);2445endPage(page, painter, pageIndex);2446}24472448return pageResult;2449}24502451/**2452* If a print job is in progress, print() has been2453* called but has not returned, then this signals2454* that the job should be cancelled and the next2455* chance. If there is no print job in progress then2456* this call does nothing.2457*/2458public void cancel() {2459synchronized (this) {2460if (performingPrinting) {2461userCancelled = true;2462}2463notify();2464}2465}24662467/**2468* Returns true is a print job is ongoing but will2469* be cancelled and the next opportunity. false is2470* returned otherwise.2471*/2472public boolean isCancelled() {24732474boolean cancelled = false;24752476synchronized (this) {2477cancelled = (performingPrinting && userCancelled);2478notify();2479}24802481return cancelled;2482}24832484/**2485* Return the Pageable describing the pages to be printed.2486*/2487protected Pageable getPageable() {2488return mDocument;2489}24902491/**2492* Examine the metrics captured by the2493* {@code PeekGraphics} instance and2494* if capable of directly converting this2495* print job to the printer's control language2496* or the native OS's graphics primitives, then2497* return a {@code PathGraphics} to perform2498* that conversion. If there is not an object2499* capable of the conversion then return2500* {@code null}. Returning {@code null}2501* causes the print job to be rasterized.2502*/2503protected Graphics2D createPathGraphics(PeekGraphics graphics,2504PrinterJob printerJob,2505Printable painter,2506PageFormat pageFormat,2507int pageIndex) {25082509return null;2510}25112512/**2513* Create and return an object that will2514* gather and hold metrics about the print2515* job. This method is passed a {@code Graphics2D}2516* object that can be used as a proxy for the2517* object gathering the print job matrics. The2518* method is also supplied with the instance2519* controlling the print job, {@code printerJob}.2520*/2521protected PeekGraphics createPeekGraphics(Graphics2D graphics,2522PrinterJob printerJob) {25232524return new PeekGraphics(graphics, printerJob);2525}25262527/**2528* Configure the passed in Graphics2D so that2529* is contains the defined initial settings2530* for a print job. These settings are:2531* color: black.2532* clip: <as passed in>2533*/2534// MacOSX - made protected so subclasses can reference it.2535protected void initPrinterGraphics(Graphics2D g, Rectangle2D clip) {25362537g.setClip(clip);2538g.setPaint(Color.black);2539}254025412542/**2543* User dialogs should disable "File" buttons if this returns false.2544*2545*/2546public boolean checkAllowedToPrintToFile() {2547try {2548throwPrintToFile();2549return true;2550} catch (SecurityException e) {2551return false;2552}2553}25542555/**2556* Break this out as it may be useful when we allow API to2557* specify printing to a file. In that case its probably right2558* to throw a SecurityException if the permission is not granted2559*/2560private void throwPrintToFile() {2561@SuppressWarnings("removal")2562SecurityManager security = System.getSecurityManager();2563if (security != null) {2564if (printToFilePermission == null) {2565printToFilePermission =2566new FilePermission("<<ALL FILES>>", "read,write");2567}2568security.checkPermission(printToFilePermission);2569}2570}25712572/* On-screen drawString renders most control chars as the missing glyph2573* and have the non-zero advance of that glyph.2574* Exceptions are \t, \n and \r which are considered zero-width.2575* This is a utility method used by subclasses to remove them so we2576* don't have to worry about platform or font specific handling of them.2577*/2578protected String removeControlChars(String s) {2579char[] in_chars = s.toCharArray();2580int len = in_chars.length;2581char[] out_chars = new char[len];2582int pos = 0;25832584for (int i = 0; i < len; i++) {2585char c = in_chars[i];2586if (c > '\r' || c < '\t' || c == '\u000b' || c == '\u000c') {2587out_chars[pos++] = c;2588}2589}2590if (pos == len) {2591return s; // no need to make a new String.2592} else {2593return new String(out_chars, 0, pos);2594}2595}25962597private DialogOwner onTop = null;25982599private long parentWindowID = 0L;26002601/* Called from native code */2602private long getParentWindowID() {2603return parentWindowID;2604}26052606private void clearParentWindowID() {2607parentWindowID = 0L;2608onTop = null;2609}26102611private void setParentWindowID(PrintRequestAttributeSet attrs) {2612parentWindowID = 0L;2613onTop = (DialogOwner)attrs.get(DialogOwner.class);2614if (onTop != null) {2615parentWindowID = DialogOwnerAccessor.getID(onTop);2616}2617}2618}261926202621