Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/print/RasterPrinterJob.java
38829 views
/*1* Copyright (c) 1998, 2013, 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.Graphics;33import java.awt.Graphics2D;34import java.awt.GraphicsConfiguration;35import java.awt.GraphicsEnvironment;36import java.awt.HeadlessException;37import java.awt.KeyboardFocusManager;38import java.awt.Rectangle;39import java.awt.Shape;40import java.awt.geom.AffineTransform;41import java.awt.geom.Area;42import java.awt.geom.Point2D;43import java.awt.geom.Rectangle2D;44import java.awt.image.BufferedImage;45import java.awt.print.Book;46import java.awt.print.Pageable;47import java.awt.print.PageFormat;48import java.awt.print.Paper;49import java.awt.print.Printable;50import java.awt.print.PrinterAbortException;51import java.awt.print.PrinterException;52import java.awt.print.PrinterJob;53import java.awt.Window;54import java.io.File;55import java.io.IOException;56import java.util.ArrayList;57import java.util.Enumeration;58import java.util.Locale;59import sun.awt.image.ByteInterleavedRaster;6061import javax.print.Doc;62import javax.print.DocFlavor;63import javax.print.DocPrintJob;64import javax.print.PrintException;65import javax.print.PrintService;66import javax.print.PrintServiceLookup;67import javax.print.ServiceUI;68import javax.print.StreamPrintService;69import javax.print.StreamPrintServiceFactory;70import javax.print.attribute.Attribute;71import javax.print.attribute.AttributeSet;72import javax.print.attribute.HashPrintRequestAttributeSet;73import javax.print.attribute.PrintRequestAttributeSet;74import javax.print.attribute.Size2DSyntax;75import javax.print.attribute.standard.Chromaticity;76import javax.print.attribute.standard.Copies;77import javax.print.attribute.standard.Destination;78import javax.print.attribute.standard.DialogTypeSelection;79import javax.print.attribute.standard.Fidelity;80import javax.print.attribute.standard.JobName;81import javax.print.attribute.standard.JobSheets;82import javax.print.attribute.standard.Media;83import javax.print.attribute.standard.MediaPrintableArea;84import javax.print.attribute.standard.MediaSize;85import javax.print.attribute.standard.MediaSizeName;86import javax.print.attribute.standard.OrientationRequested;87import javax.print.attribute.standard.PageRanges;88import javax.print.attribute.standard.PrinterState;89import javax.print.attribute.standard.PrinterStateReason;90import javax.print.attribute.standard.PrinterStateReasons;91import javax.print.attribute.standard.PrinterIsAcceptingJobs;92import javax.print.attribute.standard.RequestingUserName;93import javax.print.attribute.standard.SheetCollate;94import javax.print.attribute.standard.Sides;9596import sun.print.PageableDoc;97import sun.print.ServiceDialog;98import sun.print.SunPrinterJobService;99import sun.print.SunPageSelection;100101/**102* A class which rasterizes a printer job.103*104* @author Richard Blanchard105*/106public abstract class RasterPrinterJob extends PrinterJob {107108/* Class Constants */109110/* Printer destination type. */111protected static final int PRINTER = 0;112113/* File destination type. */114protected static final int FILE = 1;115116/* Stream destination type. */117protected static final int STREAM = 2;118119/**120* Pageable MAX pages121*/122protected static final int MAX_UNKNOWN_PAGES = 9999;123124protected static final int PD_ALLPAGES = 0x00000000;125protected static final int PD_SELECTION = 0x00000001;126protected static final int PD_PAGENUMS = 0x00000002;127protected static final int PD_NOSELECTION = 0x00000004;128129/**130* Maximum amount of memory in bytes to use for the131* buffered image "band". 4Mb is a compromise between132* limiting the number of bands on hi-res printers and133* not using too much of the Java heap or causing paging134* on systems with little RAM.135*/136private static final int MAX_BAND_SIZE = (1024 * 1024 * 4);137138/* Dots Per Inch */139private static final float DPI = 72.0f;140141/**142* Useful mainly for debugging, this system property143* can be used to force the printing code to print144* using a particular pipeline. The two currently145* supported values are FORCE_RASTER and FORCE_PDL.146*/147private static final String FORCE_PIPE_PROP = "sun.java2d.print.pipeline";148149/**150* When the system property FORCE_PIPE_PROP has this value151* then each page of a print job will be rendered through152* the raster pipeline.153*/154private static final String FORCE_RASTER = "raster";155156/**157* When the system property FORCE_PIPE_PROP has this value158* then each page of a print job will be rendered through159* the PDL pipeline.160*/161private static final String FORCE_PDL = "pdl";162163/**164* When the system property SHAPE_TEXT_PROP has this value165* then text is always rendered as a shape, and no attempt is made166* to match the font through GDI167*/168private static final String SHAPE_TEXT_PROP = "sun.java2d.print.shapetext";169170/**171* values obtained from System properties in static initialiser block172*/173public static boolean forcePDL = false;174public static boolean forceRaster = false;175public static boolean shapeTextProp = false;176177static {178/* The system property FORCE_PIPE_PROP179* can be used to force the printing code to180* use a particular pipeline. Either the raster181* pipeline or the pdl pipeline can be forced.182*/183String forceStr =184(String)java.security.AccessController.doPrivileged(185new sun.security.action.GetPropertyAction(FORCE_PIPE_PROP));186187if (forceStr != null) {188if (forceStr.equalsIgnoreCase(FORCE_PDL)) {189forcePDL = true;190} else if (forceStr.equalsIgnoreCase(FORCE_RASTER)) {191forceRaster = true;192}193}194195String shapeTextStr =196(String)java.security.AccessController.doPrivileged(197new sun.security.action.GetPropertyAction(SHAPE_TEXT_PROP));198199if (shapeTextStr != null) {200shapeTextProp = true;201}202}203204/* Instance Variables */205206/**207* Used to minimize GC & reallocation of band when printing208*/209private int cachedBandWidth = 0;210private int cachedBandHeight = 0;211private BufferedImage cachedBand = null;212213/**214* The number of book copies to be printed.215*/216private int mNumCopies = 1;217218/**219* Collation effects the order of the pages printed220* when multiple copies are requested. For two copies221* of a three page document the page order is:222* mCollate true: 1, 2, 3, 1, 2, 3223* mCollate false: 1, 1, 2, 2, 3, 3224*/225private boolean mCollate = false;226227/**228* The zero based indices of the first and last229* pages to be printed. If 'mFirstPage' is230* UNDEFINED_PAGE_NUM then the first page to231* be printed is page 0. If 'mLastPage' is232* UNDEFINED_PAGE_NUM then the last page to233* be printed is the last one in the book.234*/235private int mFirstPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;236private int mLastPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;237238/**239* The previous print stream Paper240* Used to check if the paper size has changed such that the241* implementation needs to emit the new paper size information242* into the print stream.243* Since we do our own rotation, and the margins aren't relevant,244* Its strictly the dimensions of the paper that we will check.245*/246private Paper previousPaper;247248/**249* The document to be printed. It is initialized to an250* empty (zero pages) book.251*/252// MacOSX - made protected so subclasses can reference it.253protected Pageable mDocument = new Book();254255/**256* The name of the job being printed.257*/258private String mDocName = "Java Printing";259260261/**262* Printing cancellation flags263*/264// MacOSX - made protected so subclasses can reference it.265protected boolean performingPrinting = false;266// MacOSX - made protected so subclasses can reference it.267protected boolean userCancelled = false;268269/**270* Print to file permission variables.271*/272private FilePermission printToFilePermission;273274/**275* List of areas & the graphics state for redrawing276*/277private ArrayList redrawList = new ArrayList();278279280/* variables representing values extracted from an attribute set.281* These take precedence over values set on a printer job282*/283private int copiesAttr;284private String jobNameAttr;285private String userNameAttr;286private PageRanges pageRangesAttr;287protected Sides sidesAttr;288protected String destinationAttr;289protected boolean noJobSheet = false;290protected int mDestType = RasterPrinterJob.FILE;291protected String mDestination = "";292protected boolean collateAttReq = false;293294/**295* Device rotation flag, if it support 270, this is set to true;296*/297protected boolean landscapeRotates270 = false;298299/**300* attributes used by no-args page and print dialog and print method to301* communicate state302*/303protected PrintRequestAttributeSet attributes = null;304305/**306* Class to keep state information for redrawing areas307* "region" is an area at as a high a resolution as possible.308* The redrawing code needs to look at sx, sy to calculate the scale309* to device resolution.310*/311private class GraphicsState {312Rectangle2D region; // Area of page to repaint313Shape theClip; // image drawing clip.314AffineTransform theTransform; // to transform clip to dev coords.315double sx; // X scale from region to device resolution316double sy; // Y scale from region to device resolution317}318319/**320* Service for this job321*/322protected PrintService myService;323324/* Constructors */325326public RasterPrinterJob()327{328}329330/* Abstract Methods */331332/**333* Returns the resolution in dots per inch across the width334* of the page.335*/336abstract protected double getXRes();337338/**339* Returns the resolution in dots per inch down the height340* of the page.341*/342abstract protected double getYRes();343344/**345* Must be obtained from the current printer.346* Value is in device pixels.347* Not adjusted for orientation of the paper.348*/349abstract protected double getPhysicalPrintableX(Paper p);350351/**352* Must be obtained from the current printer.353* Value is in device pixels.354* Not adjusted for orientation of the paper.355*/356abstract protected double getPhysicalPrintableY(Paper p);357358/**359* Must be obtained from the current printer.360* Value is in device pixels.361* Not adjusted for orientation of the paper.362*/363abstract protected double getPhysicalPrintableWidth(Paper p);364365/**366* Must be obtained from the current printer.367* Value is in device pixels.368* Not adjusted for orientation of the paper.369*/370abstract protected double getPhysicalPrintableHeight(Paper p);371372/**373* Must be obtained from the current printer.374* Value is in device pixels.375* Not adjusted for orientation of the paper.376*/377abstract protected double getPhysicalPageWidth(Paper p);378379/**380* Must be obtained from the current printer.381* Value is in device pixels.382* Not adjusted for orientation of the paper.383*/384abstract protected double getPhysicalPageHeight(Paper p);385386/**387* Begin a new page.388*/389abstract protected void startPage(PageFormat format, Printable painter,390int index, boolean paperChanged)391throws PrinterException;392393/**394* End a page.395*/396abstract protected void endPage(PageFormat format, Printable painter,397int index)398throws PrinterException;399400/**401* Prints the contents of the array of ints, 'data'402* to the current page. The band is placed at the403* location (x, y) in device coordinates on the404* page. The width and height of the band is405* specified by the caller.406*/407abstract protected void printBand(byte[] data, int x, int y,408int width, int height)409throws PrinterException;410411/* Instance Methods */412413/**414* save graphics state of a PathGraphics for later redrawing415* of part of page represented by the region in that state416*/417418public void saveState(AffineTransform at, Shape clip,419Rectangle2D region, double sx, double sy) {420GraphicsState gstate = new GraphicsState();421gstate.theTransform = at;422gstate.theClip = clip;423gstate.region = region;424gstate.sx = sx;425gstate.sy = sy;426redrawList.add(gstate);427}428429430/*431* A convenience method which returns the default service432* for 2D <code>PrinterJob</code>s.433* May return null if there is no suitable default (although there434* may still be 2D services available).435* @return default 2D print service, or null.436* @since 1.4437*/438protected static PrintService lookupDefaultPrintService() {439PrintService service = PrintServiceLookup.lookupDefaultPrintService();440441/* Pageable implies Printable so checking both isn't strictly needed */442if (service != null &&443service.isDocFlavorSupported(444DocFlavor.SERVICE_FORMATTED.PAGEABLE) &&445service.isDocFlavorSupported(446DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {447return service;448} else {449PrintService []services =450PrintServiceLookup.lookupPrintServices(451DocFlavor.SERVICE_FORMATTED.PAGEABLE, null);452if (services.length > 0) {453return services[0];454}455}456return null;457}458459/**460* Returns the service (printer) for this printer job.461* Implementations of this class which do not support print services462* may return null;463* @return the service for this printer job.464*465*/466public PrintService getPrintService() {467if (myService == null) {468PrintService svc = PrintServiceLookup.lookupDefaultPrintService();469if (svc != null &&470svc.isDocFlavorSupported(471DocFlavor.SERVICE_FORMATTED.PAGEABLE)) {472try {473setPrintService(svc);474myService = svc;475} catch (PrinterException e) {476}477}478if (myService == null) {479PrintService[] svcs = PrintServiceLookup.lookupPrintServices(480DocFlavor.SERVICE_FORMATTED.PAGEABLE, null);481if (svcs.length > 0) {482try {483setPrintService(svcs[0]);484myService = svcs[0];485} catch (PrinterException e) {486}487}488}489}490return myService;491}492493/**494* Associate this PrinterJob with a new PrintService.495*496* Throws <code>PrinterException</code> if the specified service497* cannot support the <code>Pageable</code> and498* <code>Printable</code> interfaces necessary to support 2D printing.499* @param a print service which supports 2D printing.500*501* @throws PrinterException if the specified service does not support502* 2D printing or no longer available.503*/504public void setPrintService(PrintService service)505throws PrinterException {506if (service == null) {507throw new PrinterException("Service cannot be null");508} else if (!(service instanceof StreamPrintService) &&509service.getName() == null) {510throw new PrinterException("Null PrintService name.");511} else {512// Check the list of services. This service may have been513// deleted already514PrinterState prnState = (PrinterState)service.getAttribute(515PrinterState.class);516if (prnState == PrinterState.STOPPED) {517PrinterStateReasons prnStateReasons =518(PrinterStateReasons)service.getAttribute(519PrinterStateReasons.class);520if ((prnStateReasons != null) &&521(prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN)))522{523throw new PrinterException("PrintService is no longer available.");524}525}526527528if (service.isDocFlavorSupported(529DocFlavor.SERVICE_FORMATTED.PAGEABLE) &&530service.isDocFlavorSupported(531DocFlavor.SERVICE_FORMATTED.PRINTABLE)) {532myService = service;533} else {534throw new PrinterException("Not a 2D print service: " + service);535}536}537}538539private PageFormat attributeToPageFormat(PrintService service,540PrintRequestAttributeSet attSet) {541PageFormat page = defaultPage();542543if (service == null) {544return page;545}546547OrientationRequested orient = (OrientationRequested)548attSet.get(OrientationRequested.class);549if (orient == null) {550orient = (OrientationRequested)551service.getDefaultAttributeValue(OrientationRequested.class);552}553if (orient == OrientationRequested.REVERSE_LANDSCAPE) {554page.setOrientation(PageFormat.REVERSE_LANDSCAPE);555} else if (orient == OrientationRequested.LANDSCAPE) {556page.setOrientation(PageFormat.LANDSCAPE);557} else {558page.setOrientation(PageFormat.PORTRAIT);559}560561Media media = (Media)attSet.get(Media.class);562MediaSize size = getMediaSize(media, service, page);563564Paper paper = new Paper();565float dim[] = size.getSize(1); //units == 1 to avoid FP error566double w = Math.rint((dim[0]*72.0)/Size2DSyntax.INCH);567double h = Math.rint((dim[1]*72.0)/Size2DSyntax.INCH);568paper.setSize(w, h);569MediaPrintableArea area =570(MediaPrintableArea)571attSet.get(MediaPrintableArea.class);572if (area == null) {573area = getDefaultPrintableArea(page, w, h);574}575576double ix, iw, iy, ih;577// Should pass in same unit as updatePageAttributes578// to avoid rounding off errors.579ix = Math.rint(580area.getX(MediaPrintableArea.INCH) * DPI);581iy = Math.rint(582area.getY(MediaPrintableArea.INCH) * DPI);583iw = Math.rint(584area.getWidth(MediaPrintableArea.INCH) * DPI);585ih = Math.rint(586area.getHeight(MediaPrintableArea.INCH) * DPI);587paper.setImageableArea(ix, iy, iw, ih);588page.setPaper(paper);589return page;590}591protected MediaSize getMediaSize(Media media, PrintService service,592PageFormat page) {593if (media == null) {594media = (Media)service.getDefaultAttributeValue(Media.class);595}596if (!(media instanceof MediaSizeName)) {597media = MediaSizeName.NA_LETTER;598}599MediaSize size = MediaSize.getMediaSizeForName((MediaSizeName) media);600return size != null ? size : MediaSize.NA.LETTER;601}602603protected MediaPrintableArea getDefaultPrintableArea(PageFormat page,604double w, double h) {605double ix, iw, iy, ih;606if (w >= 72.0 * 6.0) {607ix = 72.0;608iw = w - 2 * 72.0;609} else {610ix = w / 6.0;611iw = w * 0.75;612}613if (h >= 72.0 * 6.0) {614iy = 72.0;615ih = h - 2 * 72.0;616} else {617iy = h / 6.0;618ih = h * 0.75;619}620621return new MediaPrintableArea((float) (ix / DPI), (float) (iy / DPI),622(float) (iw / DPI), (float) (ih / DPI), MediaPrintableArea.INCH);623}624625protected void updatePageAttributes(PrintService service,626PageFormat page) {627if (this.attributes == null) {628this.attributes = new HashPrintRequestAttributeSet();629}630631updateAttributesWithPageFormat(service, page, this.attributes);632}633634protected void updateAttributesWithPageFormat(PrintService service,635PageFormat page,636PrintRequestAttributeSet pageAttributes) {637if (service == null || page == null || pageAttributes == null) {638return;639}640641float x = (float)Math.rint(642(page.getPaper().getWidth()*Size2DSyntax.INCH)/643(72.0))/(float)Size2DSyntax.INCH;644float y = (float)Math.rint(645(page.getPaper().getHeight()*Size2DSyntax.INCH)/646(72.0))/(float)Size2DSyntax.INCH;647648// We should limit the list where we search the matching649// media, this will prevent mapping to wrong media ex. Ledger650// can be mapped to B. Especially useful when creating651// custom MediaSize.652Media[] mediaList = (Media[])service.getSupportedAttributeValues(653Media.class, null, null);654Media media = null;655try {656media = CustomMediaSizeName.findMedia(mediaList, x, y,657Size2DSyntax.INCH);658} catch (IllegalArgumentException iae) {659}660if ((media == null) ||661!(service.isAttributeValueSupported(media, null, null))) {662media = (Media)service.getDefaultAttributeValue(Media.class);663}664665OrientationRequested orient;666switch (page.getOrientation()) {667case PageFormat.LANDSCAPE :668orient = OrientationRequested.LANDSCAPE;669break;670case PageFormat.REVERSE_LANDSCAPE:671orient = OrientationRequested.REVERSE_LANDSCAPE;672break;673default:674orient = OrientationRequested.PORTRAIT;675}676677if (media != null) {678pageAttributes.add(media);679}680pageAttributes.add(orient);681682float ix = (float)(page.getPaper().getImageableX()/DPI);683float iw = (float)(page.getPaper().getImageableWidth()/DPI);684float iy = (float)(page.getPaper().getImageableY()/DPI);685float ih = (float)(page.getPaper().getImageableHeight()/DPI);686if (ix < 0) ix = 0f; if (iy < 0) iy = 0f;687try {688pageAttributes.add(new MediaPrintableArea(ix, iy, iw, ih,689MediaPrintableArea.INCH));690} catch (IllegalArgumentException iae) {691}692}693694/**695* Display a dialog to the user allowing the modification of a696* PageFormat instance.697* The <code>page</code> argument is used to initialize controls698* in the page setup dialog.699* If the user cancels the dialog, then the method returns the700* original <code>page</code> object unmodified.701* If the user okays the dialog then the method returns a new702* PageFormat object with the indicated changes.703* In either case the original <code>page</code> object will704* not be modified.705* @param page the default PageFormat presented to the user706* for modification707* @return the original <code>page</code> object if the dialog708* is cancelled, or a new PageFormat object containing709* the format indicated by the user if the dialog is710* acknowledged711* @exception HeadlessException if GraphicsEnvironment.isHeadless()712* returns true.713* @see java.awt.GraphicsEnvironment#isHeadless714* @since 1.2715*/716public PageFormat pageDialog(PageFormat page)717throws HeadlessException {718if (GraphicsEnvironment.isHeadless()) {719throw new HeadlessException();720}721722final GraphicsConfiguration gc =723GraphicsEnvironment.getLocalGraphicsEnvironment().724getDefaultScreenDevice().getDefaultConfiguration();725726PrintService service =727(PrintService)java.security.AccessController.doPrivileged(728new java.security.PrivilegedAction() {729public Object run() {730PrintService service = getPrintService();731if (service == null) {732ServiceDialog.showNoPrintService(gc);733return null;734}735return service;736}737});738739if (service == null) {740return page;741}742updatePageAttributes(service, page);743744PageFormat newPage = null;745DialogTypeSelection dts =746(DialogTypeSelection)attributes.get(DialogTypeSelection.class);747if (dts == DialogTypeSelection.NATIVE) {748// Remove DialogTypeSelection.NATIVE to prevent infinite loop in749// RasterPrinterJob.750attributes.remove(DialogTypeSelection.class);751newPage = pageDialog(attributes);752// restore attribute753attributes.add(DialogTypeSelection.NATIVE);754} else {755newPage = pageDialog(attributes);756}757758if (newPage == null) {759return page;760} else {761return newPage;762}763}764765/**766* return a PageFormat corresponding to the updated attributes,767* or null if the user cancelled the dialog.768*/769public PageFormat pageDialog(final PrintRequestAttributeSet attributes)770throws HeadlessException {771if (GraphicsEnvironment.isHeadless()) {772throw new HeadlessException();773}774775DialogTypeSelection dlg =776(DialogTypeSelection)attributes.get(DialogTypeSelection.class);777778// Check for native, note that default dialog is COMMON.779if (dlg == DialogTypeSelection.NATIVE) {780PrintService pservice = getPrintService();781PageFormat pageFrmAttrib = attributeToPageFormat(pservice,782attributes);783setParentWindowID(attributes);784PageFormat page = pageDialog(pageFrmAttrib);785clearParentWindowID();786787// If user cancels the dialog, pageDialog() will return the original788// page object and as per spec, we should return null in that case.789if (page == pageFrmAttrib) {790return null;791}792updateAttributesWithPageFormat(pservice, page, attributes);793return page;794}795796final GraphicsConfiguration gc =797GraphicsEnvironment.getLocalGraphicsEnvironment().798getDefaultScreenDevice().getDefaultConfiguration();799Rectangle bounds = gc.getBounds();800int x = bounds.x+bounds.width/3;801int y = bounds.y+bounds.height/3;802803PrintService service =804(PrintService)java.security.AccessController.doPrivileged(805new java.security.PrivilegedAction() {806public Object run() {807PrintService service = getPrintService();808if (service == null) {809ServiceDialog.showNoPrintService(gc);810return null;811}812return service;813}814});815816if (service == null) {817return null;818}819820if (onTop != null) {821attributes.add(onTop);822}823824ServiceDialog pageDialog = new ServiceDialog(gc, x, y, service,825DocFlavor.SERVICE_FORMATTED.PAGEABLE,826attributes, (Frame)null);827pageDialog.show();828829if (pageDialog.getStatus() == ServiceDialog.APPROVE) {830PrintRequestAttributeSet newas =831pageDialog.getAttributes();832Class amCategory = SunAlternateMedia.class;833834if (attributes.containsKey(amCategory) &&835!newas.containsKey(amCategory)) {836attributes.remove(amCategory);837}838attributes.addAll(newas);839return attributeToPageFormat(service, attributes);840} else {841return null;842}843}844845protected PageFormat getPageFormatFromAttributes() {846Pageable pageable = null;847if (attributes == null || attributes.isEmpty() ||848!((pageable = getPageable()) instanceof OpenBook)) {849return null;850}851852PageFormat newPf = attributeToPageFormat(853getPrintService(), attributes);854PageFormat oldPf = null;855if ((oldPf = pageable.getPageFormat(0)) != null) {856// If orientation, media, imageable area attributes are not in857// "attributes" set, then use respective values of the existing858// page format "oldPf".859if (attributes.get(OrientationRequested.class) == null) {860newPf.setOrientation(oldPf.getOrientation());861}862863Paper newPaper = newPf.getPaper();864Paper oldPaper = oldPf.getPaper();865boolean oldPaperValWasSet = false;866if (attributes.get(MediaSizeName.class) == null) {867newPaper.setSize(oldPaper.getWidth(), oldPaper.getHeight());868oldPaperValWasSet = true;869}870if (attributes.get(MediaPrintableArea.class) == null) {871newPaper.setImageableArea(872oldPaper.getImageableX(), oldPaper.getImageableY(),873oldPaper.getImageableWidth(),874oldPaper.getImageableHeight());875oldPaperValWasSet = true;876}877if (oldPaperValWasSet) {878newPf.setPaper(newPaper);879}880}881return newPf;882}883884885/**886* Presents the user a dialog for changing properties of the887* print job interactively.888* The services browsable here are determined by the type of889* service currently installed.890* If the application installed a StreamPrintService on this891* PrinterJob, only the available StreamPrintService (factories) are892* browsable.893*894* @param attributes to store changed properties.895* @return false if the user cancels the dialog and true otherwise.896* @exception HeadlessException if GraphicsEnvironment.isHeadless()897* returns true.898* @see java.awt.GraphicsEnvironment#isHeadless899*/900public boolean printDialog(final PrintRequestAttributeSet attributes)901throws HeadlessException {902if (GraphicsEnvironment.isHeadless()) {903throw new HeadlessException();904}905906DialogTypeSelection dlg =907(DialogTypeSelection)attributes.get(DialogTypeSelection.class);908909// Check for native, note that default dialog is COMMON.910if (dlg == DialogTypeSelection.NATIVE) {911this.attributes = attributes;912try {913debug_println("calling setAttributes in printDialog");914setAttributes(attributes);915916} catch (PrinterException e) {917918}919920setParentWindowID(attributes);921boolean ret = printDialog();922clearParentWindowID();923this.attributes = attributes;924return ret;925926}927928/* A security check has already been performed in the929* java.awt.print.printerJob.getPrinterJob method.930* So by the time we get here, it is OK for the current thread931* to print either to a file (from a Dialog we control!) or932* to a chosen printer.933*934* We raise privilege when we put up the dialog, to avoid935* the "warning applet window" banner.936*/937final GraphicsConfiguration gc =938GraphicsEnvironment.getLocalGraphicsEnvironment().939getDefaultScreenDevice().getDefaultConfiguration();940941PrintService service =942(PrintService)java.security.AccessController.doPrivileged(943new java.security.PrivilegedAction() {944public Object run() {945PrintService service = getPrintService();946if (service == null) {947ServiceDialog.showNoPrintService(gc);948return null;949}950return service;951}952});953954if (service == null) {955return false;956}957958PrintService[] services;959StreamPrintServiceFactory[] spsFactories = null;960if (service instanceof StreamPrintService) {961spsFactories = lookupStreamPrintServices(null);962services = new StreamPrintService[spsFactories.length];963for (int i=0; i<spsFactories.length; i++) {964services[i] = spsFactories[i].getPrintService(null);965}966} else {967services =968(PrintService[])java.security.AccessController.doPrivileged(969new java.security.PrivilegedAction() {970public Object run() {971PrintService[] services = PrinterJob.lookupPrintServices();972return services;973}974});975976if ((services == null) || (services.length == 0)) {977/*978* No services but default PrintService exists?979* Create services using defaultService.980*/981services = new PrintService[1];982services[0] = service;983}984}985986Rectangle bounds = gc.getBounds();987int x = bounds.x+bounds.width/3;988int y = bounds.y+bounds.height/3;989PrintService newService;990// temporarily add an attribute pointing back to this job.991PrinterJobWrapper jobWrapper = new PrinterJobWrapper(this);992attributes.add(jobWrapper);993try {994newService =995ServiceUI.printDialog(gc, x, y,996services, service,997DocFlavor.SERVICE_FORMATTED.PAGEABLE,998attributes);999} catch (IllegalArgumentException iae) {1000newService = ServiceUI.printDialog(gc, x, y,1001services, services[0],1002DocFlavor.SERVICE_FORMATTED.PAGEABLE,1003attributes);1004}1005attributes.remove(PrinterJobWrapper.class);10061007if (newService == null) {1008return false;1009}10101011if (!service.equals(newService)) {1012try {1013setPrintService(newService);1014} catch (PrinterException e) {1015/*1016* The only time it would throw an exception is when1017* newService is no longer available but we should still1018* select this printer.1019*/1020myService = newService;1021}1022}1023return true;1024}10251026/**1027* Presents the user a dialog for changing properties of the1028* print job interactively.1029* @returns false if the user cancels the dialog and1030* true otherwise.1031* @exception HeadlessException if GraphicsEnvironment.isHeadless()1032* returns true.1033* @see java.awt.GraphicsEnvironment#isHeadless1034*/1035public boolean printDialog() throws HeadlessException {10361037if (GraphicsEnvironment.isHeadless()) {1038throw new HeadlessException();1039}10401041PrintRequestAttributeSet attributes =1042new HashPrintRequestAttributeSet();1043attributes.add(new Copies(getCopies()));1044attributes.add(new JobName(getJobName(), null));1045boolean doPrint = printDialog(attributes);1046if (doPrint) {1047JobName jobName = (JobName)attributes.get(JobName.class);1048if (jobName != null) {1049setJobName(jobName.getValue());1050}1051Copies copies = (Copies)attributes.get(Copies.class);1052if (copies != null) {1053setCopies(copies.getValue());1054}10551056Destination dest = (Destination)attributes.get(Destination.class);10571058if (dest != null) {1059try {1060mDestType = RasterPrinterJob.FILE;1061mDestination = (new File(dest.getURI())).getPath();1062} catch (Exception e) {1063mDestination = "out.prn";1064PrintService ps = getPrintService();1065if (ps != null) {1066Destination defaultDest = (Destination)ps.1067getDefaultAttributeValue(Destination.class);1068if (defaultDest != null) {1069mDestination = (new File(defaultDest.getURI())).getPath();1070}1071}1072}1073} else {1074mDestType = RasterPrinterJob.PRINTER;1075PrintService ps = getPrintService();1076if (ps != null) {1077mDestination = ps.getName();1078}1079}1080}10811082return doPrint;1083}10841085/**1086* The pages in the document to be printed by this PrinterJob1087* are drawn by the Printable object 'painter'. The PageFormat1088* for each page is the default page format.1089* @param Printable Called to render each page of the document.1090*/1091public void setPrintable(Printable painter) {1092setPageable(new OpenBook(defaultPage(new PageFormat()), painter));1093}10941095/**1096* The pages in the document to be printed by this PrinterJob1097* are drawn by the Printable object 'painter'. The PageFormat1098* of each page is 'format'.1099* @param Printable Called to render each page of the document.1100* @param PageFormat The size and orientation of each page to1101* be printed.1102*/1103public void setPrintable(Printable painter, PageFormat format) {1104setPageable(new OpenBook(format, painter));1105updatePageAttributes(getPrintService(), format);1106}11071108/**1109* The pages in the document to be printed are held by the1110* Pageable instance 'document'. 'document' will be queried1111* for the number of pages as well as the PageFormat and1112* Printable for each page.1113* @param Pageable The document to be printed. It may not be null.1114* @exception NullPointerException the Pageable passed in was null.1115* @see PageFormat1116* @see Printable1117*/1118public void setPageable(Pageable document) throws NullPointerException {1119if (document != null) {1120mDocument = document;11211122} else {1123throw new NullPointerException();1124}1125}11261127protected void initPrinter() {1128return;1129}11301131protected boolean isSupportedValue(Attribute attrval,1132PrintRequestAttributeSet attrset) {1133PrintService ps = getPrintService();1134return1135(attrval != null && ps != null &&1136ps.isAttributeValueSupported(attrval,1137DocFlavor.SERVICE_FORMATTED.PAGEABLE,1138attrset));1139}11401141/* subclasses may need to pull extra information out of the attribute set1142* They can override this method & call super.setAttributes()1143*/1144protected void setAttributes(PrintRequestAttributeSet attributes)1145throws PrinterException {1146/* reset all values to defaults */1147setCollated(false);1148sidesAttr = null;1149pageRangesAttr = null;1150copiesAttr = 0;1151jobNameAttr = null;1152userNameAttr = null;1153destinationAttr = null;1154collateAttReq = false;11551156PrintService service = getPrintService();1157if (attributes == null || service == null) {1158return;1159}11601161boolean fidelity = false;1162Fidelity attrFidelity = (Fidelity)attributes.get(Fidelity.class);1163if (attrFidelity != null && attrFidelity == Fidelity.FIDELITY_TRUE) {1164fidelity = true;1165}11661167if (fidelity == true) {1168AttributeSet unsupported =1169service.getUnsupportedAttributes(1170DocFlavor.SERVICE_FORMATTED.PAGEABLE,1171attributes);1172if (unsupported != null) {1173throw new PrinterException("Fidelity cannot be satisfied");1174}1175}11761177/*1178* Since we have verified supported values if fidelity is true,1179* we can either ignore unsupported values, or substitute a1180* reasonable alternative1181*/11821183SheetCollate collateAttr =1184(SheetCollate)attributes.get(SheetCollate.class);1185if (isSupportedValue(collateAttr, attributes)) {1186setCollated(collateAttr == SheetCollate.COLLATED);1187}11881189sidesAttr = (Sides)attributes.get(Sides.class);1190if (!isSupportedValue(sidesAttr, attributes)) {1191sidesAttr = Sides.ONE_SIDED;1192}11931194pageRangesAttr = (PageRanges)attributes.get(PageRanges.class);1195if (!isSupportedValue(pageRangesAttr, attributes)) {1196pageRangesAttr = null;1197} else {1198if ((SunPageSelection)attributes.get(SunPageSelection.class)1199== SunPageSelection.RANGE) {1200// get to, from, min, max page ranges1201int[][] range = pageRangesAttr.getMembers();1202// setPageRanges uses 0-based indexing so we subtract 11203setPageRange(range[0][0] - 1, range[0][1] - 1);1204} else {1205setPageRange(-1, - 1);1206}1207}12081209Copies copies = (Copies)attributes.get(Copies.class);1210if (isSupportedValue(copies, attributes) ||1211(!fidelity && copies != null)) {1212copiesAttr = copies.getValue();1213setCopies(copiesAttr);1214} else {1215copiesAttr = getCopies();1216}12171218Destination destination =1219(Destination)attributes.get(Destination.class);12201221if (isSupportedValue(destination, attributes)) {1222try {1223// Old code (new File(destination.getURI())).getPath()1224// would generate a "URI is not hierarchical" IAE1225// for "file:out.prn" so we use getSchemeSpecificPart instead1226destinationAttr = "" + new File(destination.getURI().1227getSchemeSpecificPart());1228} catch (Exception e) { // paranoid exception1229Destination defaultDest = (Destination)service.1230getDefaultAttributeValue(Destination.class);1231if (defaultDest != null) {1232destinationAttr = "" + new File(defaultDest.getURI().1233getSchemeSpecificPart());1234}1235}1236}12371238JobSheets jobSheets = (JobSheets)attributes.get(JobSheets.class);1239if (jobSheets != null) {1240noJobSheet = jobSheets == JobSheets.NONE;1241}12421243JobName jobName = (JobName)attributes.get(JobName.class);1244if (isSupportedValue(jobName, attributes) ||1245(!fidelity && jobName != null)) {1246jobNameAttr = jobName.getValue();1247setJobName(jobNameAttr);1248} else {1249jobNameAttr = getJobName();1250}12511252RequestingUserName userName =1253(RequestingUserName)attributes.get(RequestingUserName.class);1254if (isSupportedValue(userName, attributes) ||1255(!fidelity && userName != null)) {1256userNameAttr = userName.getValue();1257} else {1258try {1259userNameAttr = getUserName();1260} catch (SecurityException e) {1261userNameAttr = "";1262}1263}12641265/* OpenBook is used internally only when app uses Printable.1266* This is the case when we use the values from the attribute set.1267*/1268Media media = (Media)attributes.get(Media.class);1269OrientationRequested orientReq =1270(OrientationRequested)attributes.get(OrientationRequested.class);1271MediaPrintableArea mpa =1272(MediaPrintableArea)attributes.get(MediaPrintableArea.class);12731274if ((orientReq != null || media != null || mpa != null) &&1275getPageable() instanceof OpenBook) {12761277/* We could almost(!) use PrinterJob.getPageFormat() except1278* here we need to start with the PageFormat from the OpenBook :1279*/1280Pageable pageable = getPageable();1281Printable printable = pageable.getPrintable(0);1282PageFormat pf = (PageFormat)pageable.getPageFormat(0).clone();1283Paper paper = pf.getPaper();12841285/* If there's a media but no media printable area, we can try1286* to retrieve the default value for mpa and use that.1287*/1288if (mpa == null && media != null &&1289service.1290isAttributeCategorySupported(MediaPrintableArea.class)) {1291Object mpaVals = service.1292getSupportedAttributeValues(MediaPrintableArea.class,1293null, attributes);1294if (mpaVals instanceof MediaPrintableArea[] &&1295((MediaPrintableArea[])mpaVals).length > 0) {1296mpa = ((MediaPrintableArea[])mpaVals)[0];1297}1298}12991300if (isSupportedValue(orientReq, attributes) ||1301(!fidelity && orientReq != null)) {1302int orient;1303if (orientReq.equals(OrientationRequested.REVERSE_LANDSCAPE)) {1304orient = PageFormat.REVERSE_LANDSCAPE;1305} else if (orientReq.equals(OrientationRequested.LANDSCAPE)) {1306orient = PageFormat.LANDSCAPE;1307} else {1308orient = PageFormat.PORTRAIT;1309}1310pf.setOrientation(orient);1311}13121313if (isSupportedValue(media, attributes) ||1314(!fidelity && media != null)) {1315if (media instanceof MediaSizeName) {1316MediaSizeName msn = (MediaSizeName)media;1317MediaSize msz = MediaSize.getMediaSizeForName(msn);1318if (msz != null) {1319float paperWid = msz.getX(MediaSize.INCH) * 72.0f;1320float paperHgt = msz.getY(MediaSize.INCH) * 72.0f;1321paper.setSize(paperWid, paperHgt);1322if (mpa == null) {1323paper.setImageableArea(72.0, 72.0,1324paperWid-144.0,1325paperHgt-144.0);1326}1327}1328}1329}13301331if (isSupportedValue(mpa, attributes) ||1332(!fidelity && mpa != null)) {1333float [] printableArea =1334mpa.getPrintableArea(MediaPrintableArea.INCH);1335for (int i=0; i < printableArea.length; i++) {1336printableArea[i] = printableArea[i]*72.0f;1337}1338paper.setImageableArea(printableArea[0], printableArea[1],1339printableArea[2], printableArea[3]);1340}13411342pf.setPaper(paper);1343pf = validatePage(pf);1344setPrintable(printable, pf);1345} else {1346// for AWT where pageable is not an instance of OpenBook,1347// we need to save paper info1348this.attributes = attributes;1349}13501351}13521353/*1354* Services we don't recognize as built-in services can't be1355* implemented as subclasses of PrinterJob, therefore we create1356* a DocPrintJob from their service and pass a Doc representing1357* the application's printjob1358*/1359// MacOSX - made protected so subclasses can reference it.1360protected void spoolToService(PrintService psvc,1361PrintRequestAttributeSet attributes)1362throws PrinterException {13631364if (psvc == null) {1365throw new PrinterException("No print service found.");1366}13671368DocPrintJob job = psvc.createPrintJob();1369Doc doc = new PageableDoc(getPageable());1370if (attributes == null) {1371attributes = new HashPrintRequestAttributeSet();1372}1373try {1374job.print(doc, attributes);1375} catch (PrintException e) {1376throw new PrinterException(e.toString());1377}1378}13791380/**1381* Prints a set of pages.1382* @exception java.awt.print.PrinterException an error in the print system1383* caused the job to be aborted1384* @see java.awt.print.Book1385* @see java.awt.print.Pageable1386* @see java.awt.print.Printable1387*/1388public void print() throws PrinterException {1389print(attributes);1390}13911392public static boolean debugPrint = false;1393protected void debug_println(String str) {1394if (debugPrint) {1395System.out.println("RasterPrinterJob "+str+" "+this);1396}1397}13981399public void print(PrintRequestAttributeSet attributes)1400throws PrinterException {14011402/*1403* In the future PrinterJob will probably always dispatch1404* the print job to the PrintService.1405* This is how third party 2D Print Services will be invoked1406* when applications use the PrinterJob API.1407* However the JRE's concrete PrinterJob implementations have1408* not yet been re-worked to be implemented as standalone1409* services, and are implemented only as subclasses of PrinterJob.1410* So here we dispatch only those services we do not recognize1411* as implemented through platform subclasses of PrinterJob1412* (and this class).1413*/1414PrintService psvc = getPrintService();1415debug_println("psvc = "+psvc);1416if (psvc == null) {1417throw new PrinterException("No print service found.");1418}14191420// Check the list of services. This service may have been1421// deleted already1422PrinterState prnState = (PrinterState)psvc.getAttribute(1423PrinterState.class);1424if (prnState == PrinterState.STOPPED) {1425PrinterStateReasons prnStateReasons =1426(PrinterStateReasons)psvc.getAttribute(1427PrinterStateReasons.class);1428if ((prnStateReasons != null) &&1429(prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN)))1430{1431throw new PrinterException("PrintService is no longer available.");1432}1433}14341435if ((PrinterIsAcceptingJobs)(psvc.getAttribute(1436PrinterIsAcceptingJobs.class)) ==1437PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS) {1438throw new PrinterException("Printer is not accepting job.");1439}14401441if ((psvc instanceof SunPrinterJobService) &&1442((SunPrinterJobService)psvc).usesClass(getClass())) {1443setAttributes(attributes);1444// throw exception for invalid destination1445if (destinationAttr != null) {1446validateDestination(destinationAttr);1447}1448} else {1449spoolToService(psvc, attributes);1450return;1451}1452/* We need to make sure that the collation and copies1453* settings are initialised */1454initPrinter();14551456int numCollatedCopies = getCollatedCopies();1457int numNonCollatedCopies = getNoncollatedCopies();1458debug_println("getCollatedCopies() "+numCollatedCopies1459+ " getNoncollatedCopies() "+ numNonCollatedCopies);14601461/* Get the range of pages we are to print. If the1462* last page to print is unknown, then we print to1463* the end of the document. Note that firstPage1464* and lastPage are 0 based page indices.1465*/1466int numPages = mDocument.getNumberOfPages();1467if (numPages == 0) {1468return;1469}14701471int firstPage = getFirstPage();1472int lastPage = getLastPage();1473if(lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES){1474int totalPages = mDocument.getNumberOfPages();1475if (totalPages != Pageable.UNKNOWN_NUMBER_OF_PAGES) {1476lastPage = mDocument.getNumberOfPages() - 1;1477}1478}14791480try {1481synchronized (this) {1482performingPrinting = true;1483userCancelled = false;1484}14851486startDoc();1487if (isCancelled()) {1488cancelDoc();1489}14901491// PageRanges can be set even if RANGE is not selected1492// so we need to check if it is selected.1493boolean rangeIsSelected = true;1494if (attributes != null) {1495SunPageSelection pages =1496(SunPageSelection)attributes.get(SunPageSelection.class);1497if ((pages != null) && (pages != SunPageSelection.RANGE)) {1498rangeIsSelected = false;1499}1500}150115021503debug_println("after startDoc rangeSelected? "+rangeIsSelected1504+ " numNonCollatedCopies "+ numNonCollatedCopies);150515061507/* Three nested loops iterate over the document. The outer loop1508* counts the number of collated copies while the inner loop1509* counts the number of nonCollated copies. Normally, one of1510* these two loops will only execute once; that is we will1511* either print collated copies or noncollated copies. The1512* middle loop iterates over the pages.1513* If a PageRanges attribute is used, it constrains the pages1514* that are imaged. If a platform subclass (though a user dialog)1515* requests a page range via setPageRange(). it too can1516* constrain the page ranges that are imaged.1517* It is expected that only one of these will be used in a1518* job but both should be able to co-exist.1519*/1520for(int collated = 0; collated < numCollatedCopies; collated++) {1521for(int i = firstPage, pageResult = Printable.PAGE_EXISTS;1522(i <= lastPage ||1523lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES)1524&& pageResult == Printable.PAGE_EXISTS;1525i++)1526{15271528if ((pageRangesAttr != null) && rangeIsSelected ){1529int nexti = pageRangesAttr.next(i);1530if (nexti == -1) {1531break;1532} else if (nexti != i+1) {1533continue;1534}1535}15361537for(int nonCollated = 0;1538nonCollated < numNonCollatedCopies1539&& pageResult == Printable.PAGE_EXISTS;1540nonCollated++)1541{1542if (isCancelled()) {1543cancelDoc();1544}1545debug_println("printPage "+i);1546pageResult = printPage(mDocument, i);15471548}1549}1550}15511552if (isCancelled()) {1553cancelDoc();1554}15551556} finally {1557// reset previousPaper in case this job is invoked again.1558previousPaper = null;1559synchronized (this) {1560if (performingPrinting) {1561endDoc();1562}1563performingPrinting = false;1564notify();1565}1566}1567}15681569protected void validateDestination(String dest) throws PrinterException {1570if (dest == null) {1571return;1572}1573// dest is null for Destination(new URI(""))1574// because isAttributeValueSupported returns false in setAttributes15751576// Destination(new URI(" ")) throws URISyntaxException1577File f = new File(dest);1578try {1579// check if this is a new file and if filename chars are valid1580if (f.createNewFile()) {1581f.delete();1582}1583} catch (IOException ioe) {1584throw new PrinterException("Cannot write to file:"+1585dest);1586} catch (SecurityException se) {1587//There is already file read/write access so at this point1588// only delete access is denied. Just ignore it because in1589// most cases the file created in createNewFile gets overwritten1590// anyway.1591}15921593File pFile = f.getParentFile();1594if ((f.exists() &&1595(!f.isFile() || !f.canWrite())) ||1596((pFile != null) &&1597(!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) {1598throw new PrinterException("Cannot write to file:"+1599dest);1600}1601}16021603/**1604* updates a Paper object to reflect the current printer's selected1605* paper size and imageable area for that paper size.1606* Default implementation copies settings from the original, applies1607* applies some validity checks, changes them only if they are1608* clearly unreasonable, then sets them into the new Paper.1609* Subclasses are expected to override this method to make more1610* informed decisons.1611*/1612protected void validatePaper(Paper origPaper, Paper newPaper) {1613if (origPaper == null || newPaper == null) {1614return;1615} else {1616double wid = origPaper.getWidth();1617double hgt = origPaper.getHeight();1618double ix = origPaper.getImageableX();1619double iy = origPaper.getImageableY();1620double iw = origPaper.getImageableWidth();1621double ih = origPaper.getImageableHeight();16221623/* Assume any +ve values are legal. Overall paper dimensions1624* take precedence. Make sure imageable area fits on the paper.1625*/1626Paper defaultPaper = new Paper();1627wid = ((wid > 0.0) ? wid : defaultPaper.getWidth());1628hgt = ((hgt > 0.0) ? hgt : defaultPaper.getHeight());1629ix = ((ix > 0.0) ? ix : defaultPaper.getImageableX());1630iy = ((iy > 0.0) ? iy : defaultPaper.getImageableY());1631iw = ((iw > 0.0) ? iw : defaultPaper.getImageableWidth());1632ih = ((ih > 0.0) ? ih : defaultPaper.getImageableHeight());1633/* full width/height is not likely to be imageable, but since we1634* don't know the limits we have to allow it1635*/1636if (iw > wid) {1637iw = wid;1638}1639if (ih > hgt) {1640ih = hgt;1641}1642if ((ix + iw) > wid) {1643ix = wid - iw;1644}1645if ((iy + ih) > hgt) {1646iy = hgt - ih;1647}1648newPaper.setSize(wid, hgt);1649newPaper.setImageableArea(ix, iy, iw, ih);1650}1651}16521653/**1654* The passed in PageFormat will be copied and altered to describe1655* the default page size and orientation of the PrinterJob's1656* current printer.1657* Platform subclasses which can access the actual default paper size1658* for a printer may override this method.1659*/1660public PageFormat defaultPage(PageFormat page) {1661PageFormat newPage = (PageFormat)page.clone();1662newPage.setOrientation(PageFormat.PORTRAIT);1663Paper newPaper = new Paper();1664double ptsPerInch = 72.0;1665double w, h;1666Media media = null;16671668PrintService service = getPrintService();1669if (service != null) {1670MediaSize size;1671media =1672(Media)service.getDefaultAttributeValue(Media.class);16731674if (media instanceof MediaSizeName &&1675((size = MediaSize.getMediaSizeForName((MediaSizeName)media)) !=1676null)) {1677w = size.getX(MediaSize.INCH) * ptsPerInch;1678h = size.getY(MediaSize.INCH) * ptsPerInch;1679newPaper.setSize(w, h);1680newPaper.setImageableArea(ptsPerInch, ptsPerInch,1681w - 2.0*ptsPerInch,1682h - 2.0*ptsPerInch);1683newPage.setPaper(newPaper);1684return newPage;16851686}1687}16881689/* Default to A4 paper outside North America.1690*/1691String defaultCountry = Locale.getDefault().getCountry();1692if (!Locale.getDefault().equals(Locale.ENGLISH) && // ie "C"1693defaultCountry != null &&1694!defaultCountry.equals(Locale.US.getCountry()) &&1695!defaultCountry.equals(Locale.CANADA.getCountry())) {16961697double mmPerInch = 25.4;1698w = Math.rint((210.0*ptsPerInch)/mmPerInch);1699h = Math.rint((297.0*ptsPerInch)/mmPerInch);1700newPaper.setSize(w, h);1701newPaper.setImageableArea(ptsPerInch, ptsPerInch,1702w - 2.0*ptsPerInch,1703h - 2.0*ptsPerInch);1704}17051706newPage.setPaper(newPaper);17071708return newPage;1709}17101711/**1712* The passed in PageFormat is cloned and altered to be usable on1713* the PrinterJob's current printer.1714*/1715public PageFormat validatePage(PageFormat page) {1716PageFormat newPage = (PageFormat)page.clone();1717Paper newPaper = new Paper();1718validatePaper(newPage.getPaper(), newPaper);1719newPage.setPaper(newPaper);17201721return newPage;1722}17231724/**1725* Set the number of copies to be printed.1726*/1727public void setCopies(int copies) {1728mNumCopies = copies;1729}17301731/**1732* Get the number of copies to be printed.1733*/1734public int getCopies() {1735return mNumCopies;1736}17371738/* Used when executing a print job where an attribute set may1739* over ride API values.1740*/1741protected int getCopiesInt() {1742return (copiesAttr > 0) ? copiesAttr : getCopies();1743}17441745/**1746* Get the name of the printing user.1747* The caller must have security permission to read system properties.1748*/1749public String getUserName() {1750return System.getProperty("user.name");1751}17521753/* Used when executing a print job where an attribute set may1754* over ride API values.1755*/1756protected String getUserNameInt() {1757if (userNameAttr != null) {1758return userNameAttr;1759} else {1760try {1761return getUserName();1762} catch (SecurityException e) {1763return "";1764}1765}1766}17671768/**1769* Set the name of the document to be printed.1770* The document name can not be null.1771*/1772public void setJobName(String jobName) {1773if (jobName != null) {1774mDocName = jobName;1775} else {1776throw new NullPointerException();1777}1778}17791780/**1781* Get the name of the document to be printed.1782*/1783public String getJobName() {1784return mDocName;1785}17861787/* Used when executing a print job where an attribute set may1788* over ride API values.1789*/1790protected String getJobNameInt() {1791return (jobNameAttr != null) ? jobNameAttr : getJobName();1792}17931794/**1795* Set the range of pages from a Book to be printed.1796* Both 'firstPage' and 'lastPage' are zero based1797* page indices. If either parameter is less than1798* zero then the page range is set to be from the1799* first page to the last.1800*/1801protected void setPageRange(int firstPage, int lastPage) {1802if(firstPage >= 0 && lastPage >= 0) {1803mFirstPage = firstPage;1804mLastPage = lastPage;1805if(mLastPage < mFirstPage) mLastPage = mFirstPage;1806} else {1807mFirstPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;1808mLastPage = Pageable.UNKNOWN_NUMBER_OF_PAGES;1809}1810}18111812/**1813* Return the zero based index of the first page to1814* be printed in this job.1815*/1816protected int getFirstPage() {1817return mFirstPage == Book.UNKNOWN_NUMBER_OF_PAGES ? 0 : mFirstPage;1818}18191820/**1821* Return the zero based index of the last page to1822* be printed in this job.1823*/1824protected int getLastPage() {1825return mLastPage;1826}18271828/**1829* Set whether copies should be collated or not.1830* Two collated copies of a three page document1831* print in this order: 1, 2, 3, 1, 2, 3 while1832* uncollated copies print in this order:1833* 1, 1, 2, 2, 3, 3.1834* This is set when request is using an attribute set.1835*/1836protected void setCollated(boolean collate) {1837mCollate = collate;1838collateAttReq = true;1839}18401841/**1842* Return true if collated copies will be printed as determined1843* in an attribute set.1844*/1845protected boolean isCollated() {1846return mCollate;1847}18481849protected final int getSelectAttrib() {1850if (attributes != null) {1851SunPageSelection pages =1852(SunPageSelection)attributes.get(SunPageSelection.class);1853if (pages == SunPageSelection.RANGE) {1854return PD_PAGENUMS;1855} else if (pages == SunPageSelection.SELECTION) {1856return PD_SELECTION;1857} else if (pages == SunPageSelection.ALL) {1858return PD_ALLPAGES;1859}1860}1861return PD_NOSELECTION;1862}18631864//returns 1-based index for "From" page1865protected final int getFromPageAttrib() {1866if (attributes != null) {1867PageRanges pageRangesAttr =1868(PageRanges)attributes.get(PageRanges.class);1869if (pageRangesAttr != null) {1870int[][] range = pageRangesAttr.getMembers();1871return range[0][0];1872}1873}1874return getMinPageAttrib();1875}18761877//returns 1-based index for "To" page1878protected final int getToPageAttrib() {1879if (attributes != null) {1880PageRanges pageRangesAttr =1881(PageRanges)attributes.get(PageRanges.class);1882if (pageRangesAttr != null) {1883int[][] range = pageRangesAttr.getMembers();1884return range[range.length-1][1];1885}1886}1887return getMaxPageAttrib();1888}18891890protected final int getMinPageAttrib() {1891if (attributes != null) {1892SunMinMaxPage s =1893(SunMinMaxPage)attributes.get(SunMinMaxPage.class);1894if (s != null) {1895return s.getMin();1896}1897}1898return 1;1899}19001901protected final int getMaxPageAttrib() {1902if (attributes != null) {1903SunMinMaxPage s =1904(SunMinMaxPage)attributes.get(SunMinMaxPage.class);1905if (s != null) {1906return s.getMax();1907}1908}19091910Pageable pageable = getPageable();1911if (pageable != null) {1912int numPages = pageable.getNumberOfPages();1913if (numPages <= Pageable.UNKNOWN_NUMBER_OF_PAGES) {1914numPages = MAX_UNKNOWN_PAGES;1915}1916return ((numPages == 0) ? 1 : numPages);1917}19181919return Integer.MAX_VALUE;1920}1921/**1922* Called by the print() method at the start of1923* a print job.1924*/1925protected abstract void startDoc() throws PrinterException;19261927/**1928* Called by the print() method at the end of1929* a print job.1930*/1931protected abstract void endDoc() throws PrinterException;19321933/* Called by cancelDoc */1934protected abstract void abortDoc();19351936// MacOSX - made protected so subclasses can reference it.1937protected void cancelDoc() throws PrinterAbortException {1938abortDoc();1939synchronized (this) {1940userCancelled = false;1941performingPrinting = false;1942notify();1943}1944throw new PrinterAbortException();1945}19461947/**1948* Returns how many times the entire book should1949* be printed by the PrintJob. If the printer1950* itself supports collation then this method1951* should return 1 indicating that the entire1952* book need only be printed once and the copies1953* will be collated and made in the printer.1954*/1955protected int getCollatedCopies() {1956return isCollated() ? getCopiesInt() : 1;1957}19581959/**1960* Returns how many times each page in the book1961* should be consecutively printed by PrintJob.1962* If the printer makes copies itself then this1963* method should return 1.1964*/1965protected int getNoncollatedCopies() {1966return isCollated() ? 1 : getCopiesInt();1967}196819691970/* The printer graphics config is cached on the job, so that it can1971* be created once, and updated only as needed (for now only to change1972* the bounds if when using a Pageable the page sizes changes).1973*/19741975private int deviceWidth, deviceHeight;1976private AffineTransform defaultDeviceTransform;1977private PrinterGraphicsConfig pgConfig;19781979synchronized void setGraphicsConfigInfo(AffineTransform at,1980double pw, double ph) {1981Point2D.Double pt = new Point2D.Double(pw, ph);1982at.transform(pt, pt);19831984if (pgConfig == null ||1985defaultDeviceTransform == null ||1986!at.equals(defaultDeviceTransform) ||1987deviceWidth != (int)pt.getX() ||1988deviceHeight != (int)pt.getY()) {19891990deviceWidth = (int)pt.getX();1991deviceHeight = (int)pt.getY();1992defaultDeviceTransform = at;1993pgConfig = null;1994}1995}19961997synchronized PrinterGraphicsConfig getPrinterGraphicsConfig() {1998if (pgConfig != null) {1999return pgConfig;2000}2001String deviceID = "Printer Device";2002PrintService service = getPrintService();2003if (service != null) {2004deviceID = service.toString();2005}2006pgConfig = new PrinterGraphicsConfig(deviceID,2007defaultDeviceTransform,2008deviceWidth, deviceHeight);2009return pgConfig;2010}20112012/**2013* Print a page from the provided document.2014* @return int Printable.PAGE_EXISTS if the page existed and was drawn and2015* Printable.NO_SUCH_PAGE if the page did not exist.2016* @see java.awt.print.Printable2017*/2018protected int printPage(Pageable document, int pageIndex)2019throws PrinterException2020{2021PageFormat page;2022PageFormat origPage;2023Printable painter;2024try {2025origPage = document.getPageFormat(pageIndex);2026page = (PageFormat)origPage.clone();2027painter = document.getPrintable(pageIndex);2028} catch (Exception e) {2029PrinterException pe =2030new PrinterException("Error getting page or printable.[ " +2031e +" ]");2032pe.initCause(e);2033throw pe;2034}20352036/* Get the imageable area from Paper instead of PageFormat2037* because we do not want it adjusted by the page orientation.2038*/2039Paper paper = page.getPaper();2040// if non-portrait and 270 degree landscape rotation2041if (page.getOrientation() != PageFormat.PORTRAIT &&2042landscapeRotates270) {20432044double left = paper.getImageableX();2045double top = paper.getImageableY();2046double width = paper.getImageableWidth();2047double height = paper.getImageableHeight();2048paper.setImageableArea(paper.getWidth()-left-width,2049paper.getHeight()-top-height,2050width, height);2051page.setPaper(paper);2052if (page.getOrientation() == PageFormat.LANDSCAPE) {2053page.setOrientation(PageFormat.REVERSE_LANDSCAPE);2054} else {2055page.setOrientation(PageFormat.LANDSCAPE);2056}2057}20582059double xScale = getXRes() / 72.0;2060double yScale = getYRes() / 72.0;20612062/* The deviceArea is the imageable area in the printer's2063* resolution.2064*/2065Rectangle2D deviceArea =2066new Rectangle2D.Double(paper.getImageableX() * xScale,2067paper.getImageableY() * yScale,2068paper.getImageableWidth() * xScale,2069paper.getImageableHeight() * yScale);20702071/* Build and hold on to a uniform transform so that2072* we can get back to device space at the beginning2073* of each band.2074*/2075AffineTransform uniformTransform = new AffineTransform();20762077/* The scale transform is used to switch from the2078* device space to the user's 72 dpi space.2079*/2080AffineTransform scaleTransform = new AffineTransform();2081scaleTransform.scale(xScale, yScale);20822083/* bandwidth is multiple of 4 as the data is used in a win32 DIB and2084* some drivers behave badly if scanlines aren't multiples of 4 bytes.2085*/2086int bandWidth = (int) deviceArea.getWidth();2087if (bandWidth % 4 != 0) {2088bandWidth += (4 - (bandWidth % 4));2089}2090if (bandWidth <= 0) {2091throw new PrinterException("Paper's imageable width is too small.");2092}20932094int deviceAreaHeight = (int)deviceArea.getHeight();2095if (deviceAreaHeight <= 0) {2096throw new PrinterException("Paper's imageable height is too small.");2097}20982099/* Figure out the number of lines that will fit into2100* our maximum band size. The hard coded 3 reflects the2101* fact that we can only create 24 bit per pixel 3 byte BGR2102* BufferedImages. FIX.2103*/2104int bandHeight = (int)(MAX_BAND_SIZE / bandWidth / 3);21052106int deviceLeft = (int)Math.rint(paper.getImageableX() * xScale);2107int deviceTop = (int)Math.rint(paper.getImageableY() * yScale);21082109/* The device transform is used to move the band down2110* the page using translates. Normally this is all it2111* would do, but since, when printing, the Window's2112* DIB format wants the last line to be first (lowest) in2113* memory, the deviceTransform moves the origin to the2114* bottom of the band and flips the origin. This way the2115* app prints upside down into the band which is the DIB2116* format.2117*/2118AffineTransform deviceTransform = new AffineTransform();2119deviceTransform.translate(-deviceLeft, deviceTop);2120deviceTransform.translate(0, bandHeight);2121deviceTransform.scale(1, -1);21222123/* Create a BufferedImage to hold the band. We set the clip2124* of the band to be tight around the bits so that the2125* application can use it to figure what part of the2126* page needs to be drawn. The clip is never altered in2127* this method, but we do translate the band's coordinate2128* system so that the app will see the clip moving down the2129* page though it s always around the same set of pixels.2130*/2131BufferedImage pBand = new BufferedImage(1, 1,2132BufferedImage.TYPE_3BYTE_BGR);21332134/* Have the app draw into a PeekGraphics object so we can2135* learn something about the needs of the print job.2136*/21372138PeekGraphics peekGraphics = createPeekGraphics(pBand.createGraphics(),2139this);21402141Rectangle2D.Double pageFormatArea =2142new Rectangle2D.Double(page.getImageableX(),2143page.getImageableY(),2144page.getImageableWidth(),2145page.getImageableHeight());2146peekGraphics.transform(scaleTransform);2147peekGraphics.translate(-getPhysicalPrintableX(paper) / xScale,2148-getPhysicalPrintableY(paper) / yScale);2149peekGraphics.transform(new AffineTransform(page.getMatrix()));2150initPrinterGraphics(peekGraphics, pageFormatArea);2151AffineTransform pgAt = peekGraphics.getTransform();21522153/* Update the information used to return a GraphicsConfiguration2154* for this printer device. It needs to be updated per page as2155* not all pages in a job may be the same size (different bounds)2156* The transform is the scaling transform as this corresponds to2157* the default transform for the device. The width and height are2158* those of the paper, not the page format, as we want to describe2159* the bounds of the device in its natural coordinate system of2160* device coordinate whereas a page format may be in a rotated context.2161*/2162setGraphicsConfigInfo(scaleTransform,2163paper.getWidth(), paper.getHeight());2164int pageResult = painter.print(peekGraphics, origPage, pageIndex);2165debug_println("pageResult "+pageResult);2166if (pageResult == Printable.PAGE_EXISTS) {2167debug_println("startPage "+pageIndex);21682169/* We need to check if the paper size is changed.2170* Note that it is not sufficient to ask for the pageformat2171* of "pageIndex-1", since PageRanges mean that pages can be2172* skipped. So we have to look at the actual last paper size used.2173*/2174Paper thisPaper = page.getPaper();2175boolean paperChanged =2176previousPaper == null ||2177thisPaper.getWidth() != previousPaper.getWidth() ||2178thisPaper.getHeight() != previousPaper.getHeight();2179previousPaper = thisPaper;21802181startPage(page, painter, pageIndex, paperChanged);2182Graphics2D pathGraphics = createPathGraphics(peekGraphics, this,2183painter, page,2184pageIndex);21852186/* If we can convert the page directly to the2187* underlying graphics system then we do not2188* need to rasterize. We also may not need to2189* create the 'band' if all the pages can take2190* this path.2191*/2192if (pathGraphics != null) {2193pathGraphics.transform(scaleTransform);2194// user (0,0) should be origin of page, not imageable area2195pathGraphics.translate(-getPhysicalPrintableX(paper) / xScale,2196-getPhysicalPrintableY(paper) / yScale);2197pathGraphics.transform(new AffineTransform(page.getMatrix()));2198initPrinterGraphics(pathGraphics, pageFormatArea);21992200redrawList.clear();22012202AffineTransform initialTx = pathGraphics.getTransform();22032204painter.print(pathGraphics, origPage, pageIndex);22052206for (int i=0;i<redrawList.size();i++) {2207GraphicsState gstate = (GraphicsState)redrawList.get(i);2208pathGraphics.setTransform(initialTx);2209((PathGraphics)pathGraphics).redrawRegion(2210gstate.region,2211gstate.sx,2212gstate.sy,2213gstate.theClip,2214gstate.theTransform);2215}22162217/* This is the banded-raster printing loop.2218* It should be moved into its own method.2219*/2220} else {2221BufferedImage band = cachedBand;2222if (cachedBand == null ||2223bandWidth != cachedBandWidth ||2224bandHeight != cachedBandHeight) {2225band = new BufferedImage(bandWidth, bandHeight,2226BufferedImage.TYPE_3BYTE_BGR);2227cachedBand = band;2228cachedBandWidth = bandWidth;2229cachedBandHeight = bandHeight;2230}2231Graphics2D bandGraphics = band.createGraphics();22322233Rectangle2D.Double clipArea =2234new Rectangle2D.Double(0, 0, bandWidth, bandHeight);22352236initPrinterGraphics(bandGraphics, clipArea);22372238ProxyGraphics2D painterGraphics =2239new ProxyGraphics2D(bandGraphics, this);22402241Graphics2D clearGraphics = band.createGraphics();2242clearGraphics.setColor(Color.white);22432244/* We need the actual bits of the BufferedImage to send to2245* the native Window's code. 'data' points to the actual2246* pixels. Right now these are in ARGB format with 8 bits2247* per component. We need to use a monochrome BufferedImage2248* for monochrome printers when this is supported by2249* BufferedImage. FIX2250*/2251ByteInterleavedRaster tile = (ByteInterleavedRaster)band.getRaster();2252byte[] data = tile.getDataStorage();22532254/* Loop over the page moving our band down the page,2255* calling the app to render the band, and then send the band2256* to the printer.2257*/2258int deviceBottom = deviceTop + deviceAreaHeight;22592260/* device's printable x,y is really addressable origin2261* we address relative to media origin so when we print a2262* band we need to adjust for the different methods of2263* addressing it.2264*/2265int deviceAddressableX = (int)getPhysicalPrintableX(paper);2266int deviceAddressableY = (int)getPhysicalPrintableY(paper);22672268for (int bandTop = 0; bandTop <= deviceAreaHeight;2269bandTop += bandHeight)2270{22712272/* Put the band back into device space and2273* erase the contents of the band.2274*/2275clearGraphics.fillRect(0, 0, bandWidth, bandHeight);22762277/* Put the band into the correct location on the2278* page. Once the band is moved we translate the2279* device transform so that the band will move down2280* the page on the next iteration of the loop.2281*/2282bandGraphics.setTransform(uniformTransform);2283bandGraphics.transform(deviceTransform);2284deviceTransform.translate(0, -bandHeight);22852286/* Switch the band from device space to user,2287* 72 dpi, space.2288*/2289bandGraphics.transform(scaleTransform);2290bandGraphics.transform(new AffineTransform(page.getMatrix()));22912292Rectangle clip = bandGraphics.getClipBounds();2293clip = pgAt.createTransformedShape(clip).getBounds();22942295if ((clip == null) || peekGraphics.hitsDrawingArea(clip) &&2296(bandWidth > 0 && bandHeight > 0)) {22972298/* if the client has specified an imageable X or Y2299* which is off than the physically addressable2300* area of the page, then we need to adjust for that2301* here so that we pass only non -ve band coordinates2302* We also need to translate by the adjusted amount2303* so that printing appears in the correct place.2304*/2305int bandX = deviceLeft - deviceAddressableX;2306if (bandX < 0) {2307bandGraphics.translate(bandX/xScale,0);2308bandX = 0;2309}2310int bandY = deviceTop + bandTop - deviceAddressableY;2311if (bandY < 0) {2312bandGraphics.translate(0,bandY/yScale);2313bandY = 0;2314}2315/* Have the app's painter image into the band2316* and then send the band to the printer.2317*/2318painterGraphics.setDelegate((Graphics2D) bandGraphics.create());2319painter.print(painterGraphics, origPage, pageIndex);2320painterGraphics.dispose();2321printBand(data, bandX, bandY, bandWidth, bandHeight);2322}2323}23242325clearGraphics.dispose();2326bandGraphics.dispose();23272328}2329debug_println("calling endPage "+pageIndex);2330endPage(page, painter, pageIndex);2331}23322333return pageResult;2334}23352336/**2337* If a print job is in progress, print() has been2338* called but has not returned, then this signals2339* that the job should be cancelled and the next2340* chance. If there is no print job in progress then2341* this call does nothing.2342*/2343public void cancel() {2344synchronized (this) {2345if (performingPrinting) {2346userCancelled = true;2347}2348notify();2349}2350}23512352/**2353* Returns true is a print job is ongoing but will2354* be cancelled and the next opportunity. false is2355* returned otherwise.2356*/2357public boolean isCancelled() {23582359boolean cancelled = false;23602361synchronized (this) {2362cancelled = (performingPrinting && userCancelled);2363notify();2364}23652366return cancelled;2367}23682369/**2370* Return the Pageable describing the pages to be printed.2371*/2372protected Pageable getPageable() {2373return mDocument;2374}23752376/**2377* Examine the metrics captured by the2378* <code>PeekGraphics</code> instance and2379* if capable of directly converting this2380* print job to the printer's control language2381* or the native OS's graphics primitives, then2382* return a <code>PathGraphics</code> to perform2383* that conversion. If there is not an object2384* capable of the conversion then return2385* <code>null</code>. Returning <code>null</code>2386* causes the print job to be rasterized.2387*/2388protected Graphics2D createPathGraphics(PeekGraphics graphics,2389PrinterJob printerJob,2390Printable painter,2391PageFormat pageFormat,2392int pageIndex) {23932394return null;2395}23962397/**2398* Create and return an object that will2399* gather and hold metrics about the print2400* job. This method is passed a <code>Graphics2D</code>2401* object that can be used as a proxy for the2402* object gathering the print job matrics. The2403* method is also supplied with the instance2404* controlling the print job, <code>printerJob</code>.2405*/2406protected PeekGraphics createPeekGraphics(Graphics2D graphics,2407PrinterJob printerJob) {24082409return new PeekGraphics(graphics, printerJob);2410}24112412/**2413* Configure the passed in Graphics2D so that2414* is contains the defined initial settings2415* for a print job. These settings are:2416* color: black.2417* clip: <as passed in>2418*/2419// MacOSX - made protected so subclasses can reference it.2420protected void initPrinterGraphics(Graphics2D g, Rectangle2D clip) {24212422g.setClip(clip);2423g.setPaint(Color.black);2424}242524262427/**2428* User dialogs should disable "File" buttons if this returns false.2429*2430*/2431public boolean checkAllowedToPrintToFile() {2432try {2433throwPrintToFile();2434return true;2435} catch (SecurityException e) {2436return false;2437}2438}24392440/**2441* Break this out as it may be useful when we allow API to2442* specify printing to a file. In that case its probably right2443* to throw a SecurityException if the permission is not granted2444*/2445private void throwPrintToFile() {2446SecurityManager security = System.getSecurityManager();2447if (security != null) {2448if (printToFilePermission == null) {2449printToFilePermission =2450new FilePermission("<<ALL FILES>>", "read,write");2451}2452security.checkPermission(printToFilePermission);2453}2454}24552456/* On-screen drawString renders most control chars as the missing glyph2457* and have the non-zero advance of that glyph.2458* Exceptions are \t, \n and \r which are considered zero-width.2459* This is a utility method used by subclasses to remove them so we2460* don't have to worry about platform or font specific handling of them.2461*/2462protected String removeControlChars(String s) {2463char[] in_chars = s.toCharArray();2464int len = in_chars.length;2465char[] out_chars = new char[len];2466int pos = 0;24672468for (int i = 0; i < len; i++) {2469char c = in_chars[i];2470if (c > '\r' || c < '\t' || c == '\u000b' || c == '\u000c') {2471out_chars[pos++] = c;2472}2473}2474if (pos == len) {2475return s; // no need to make a new String.2476} else {2477return new String(out_chars, 0, pos);2478}2479}24802481private DialogOnTop onTop = null;24822483private long parentWindowID = 0L;24842485/* Called from native code */2486private long getParentWindowID() {2487return parentWindowID;2488}24892490private void clearParentWindowID() {2491parentWindowID = 0L;2492onTop = null;2493}24942495private void setParentWindowID(PrintRequestAttributeSet attrs) {2496parentWindowID = 0L;2497onTop = (DialogOnTop)attrs.get(DialogOnTop.class);2498if (onTop != null) {2499parentWindowID = onTop.getID();2500}2501}2502}250325042505