/* * This file is modified by Ivan Maidanski * Project name: JCGO-SUNAWT (http://www.ivmaisoft.com/jcgo/) */ /* * @(#)RasterPrinterJob.java 1.50 03/01/23 * * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package sun.print; import java.io.FilePermission; import java.awt.Color; import java.awt.Dialog; import java.awt.Frame; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.HeadlessException; import java.awt.KeyboardFocusManager; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Window; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.print.Book; import java.awt.print.Pageable; import java.awt.print.PageFormat; import java.awt.print.Paper; import java.awt.print.Printable; import java.awt.print.PrinterAbortException; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.io.File; import java.util.ArrayList; import java.util.Enumeration; import java.util.Locale; import sun.awt.image.ByteInterleavedRaster; import javax.print.Doc; import javax.print.DocFlavor; import javax.print.DocPrintJob; import javax.print.PrintException; import javax.print.PrintService; import javax.print.PrintServiceLookup; import javax.print.ServiceUI; import javax.print.StreamPrintService; import javax.print.StreamPrintServiceFactory; import javax.print.attribute.Attribute; import javax.print.attribute.AttributeSet; import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.PrintRequestAttributeSet; import javax.print.attribute.Size2DSyntax; import javax.print.attribute.standard.Chromaticity; import javax.print.attribute.standard.Copies; import javax.print.attribute.standard.Destination; import javax.print.attribute.standard.Fidelity; import javax.print.attribute.standard.JobName; import javax.print.attribute.standard.JobSheets; import javax.print.attribute.standard.Media; import javax.print.attribute.standard.MediaPrintableArea; import javax.print.attribute.standard.MediaSize; import javax.print.attribute.standard.MediaSizeName; import javax.print.attribute.standard.OrientationRequested; import javax.print.attribute.standard.PageRanges; import javax.print.attribute.standard.RequestingUserName; import javax.print.attribute.standard.SheetCollate; import javax.print.attribute.standard.Sides; import sun.print.PageableDoc; import sun.print.ServiceDialog; import sun.print.SunPrinterJobService; /** * A class which rasterizes a printer job. * * @version 1.0 26 Jan 1998 * @author Richard Blanchard */ public abstract class RasterPrinterJob extends PrinterJob { /* Class Constants */ /* Printer destination type. */ protected static final int PRINTER = 0; /* File destination type. */ protected static final int FILE = 1; /* Stream destination type. */ protected static final int STREAM = 2; /** * Maximum amount of memory in bytes to use for the * buffered image "band". 4Mb is a compromise between * limiting the number of bands on hi-res printers and * not using too much of the Java heap or causing paging * on systems with little RAM. */ private static final int MAX_BAND_SIZE = (1024 * 1024 * 4); private static final float DPI = 72f; /* Instance Variables */ /** * Used to minimise GC & reallocation of band when printing */ private int cachedBandWidth = 0; private int cachedBandHeight = 0; private BufferedImage cachedBand = null; /** * The number of book copies to be printed. */ private int mNumCopies = 1; /** * Collation effects the order of the pages printed * when multiple copies are requested. For two copies * of a three page document the page order is: * mCollate true: 1, 2, 3, 1, 2, 3 * mCollate false: 1, 1, 2, 2, 3, 3 */ private boolean mCollate = false; /** * The zero based indices of the first and last * pages to be printed. If 'mFirstPage' is * UNDEFINED_PAGE_NUM then the first page to * be printed is page 0. If 'mLastPage' is * UNDEFINED_PAGE_NUM then the last page to * be printed is the last one in the book. */ private int mFirstPage = Pageable.UNKNOWN_NUMBER_OF_PAGES; private int mLastPage = Pageable.UNKNOWN_NUMBER_OF_PAGES; /** * The document to be printed. It is initialized to an * empty (zero pages) book. */ private Pageable mDocument = new Book(); /** * The name of the job being printed. */ private String mDocName = new String("Java Printing"); /** * Printing cancellation flags */ private boolean performingPrinting = false; private boolean userCancelled = false; /** * Print to file permission variables. */ private FilePermission printToFilePermission; /** * List of areas & the graphics state for redrawing */ private ArrayList redrawList = new ArrayList(); /* variables representing values extracted from an attribute set. * These take precedence over values set on a printer job */ private int copiesAttr; private String jobNameAttr; private String userNameAttr; private PageRanges pageRangesAttr; protected Sides sidesAttr; protected String destinationAttr; protected boolean noJobSheet = false; protected int mDestType = RasterPrinterJob.FILE; protected String mDestination = ""; protected boolean collateAttReq = false; /** * attributes used by no-args page and print dialog and print method to * communicate state */ protected PrintRequestAttributeSet attributes = null; /** * Class to keep state information for redrawing areas */ private class GraphicsState { Rectangle2D region; // Shape theClip; AffineTransform theTransform; double sx; double sy; Rectangle2D imageSrcRect; // source part of the image AffineTransform imageTransform; // used to set device clip } /** * Service for this job */ private PrintService myService; /* Constructors */ public RasterPrinterJob() { } /* Abstract Methods */ /** * Returns the resolution in dots per inch across the width * of the page. */ abstract protected double getXRes(); /** * Returns the resolution in dots per inch down the height * of the page. */ abstract protected double getYRes(); /** * Must be obtained from the current printer. * Value is in device pixels. * Not adjusted for orientation of the paper. */ abstract protected double getPhysicalPrintableX(Paper p); /** * Must be obtained from the current printer. * Value is in device pixels. * Not adjusted for orientation of the paper. */ abstract protected double getPhysicalPrintableY(Paper p); /** * Must be obtained from the current printer. * Value is in device pixels. * Not adjusted for orientation of the paper. */ abstract protected double getPhysicalPrintableWidth(Paper p); /** * Must be obtained from the current printer. * Value is in device pixels. * Not adjusted for orientation of the paper. */ abstract protected double getPhysicalPrintableHeight(Paper p); /** * Must be obtained from the current printer. * Value is in device pixels. * Not adjusted for orientation of the paper. */ abstract protected double getPhysicalPageWidth(Paper p); /** * Must be obtained from the current printer. * Value is in device pixels. * Not adjusted for orientation of the paper. */ abstract protected double getPhysicalPageHeight(Paper p); /** * Begin a new page. This call's Window's * StartPage routine. */ abstract protected void startPage(PageFormat format, Printable painter, int index) throws PrinterException; /** * End a page. */ abstract protected void endPage(PageFormat format, Printable painter, int index) throws PrinterException; /** * Prints the contents of the array of ints, 'data' * to the current page. The band is placed at the * location (x, y) in device coordinates on the * page. The width and height of the band is * specified by the caller. */ abstract protected void printBand(byte[] data, int x, int y, int width, int height) throws PrinterException; /* Instance Methods */ /** * save graphics state of a PathGraphics for later redrawing * of part of page represented by the region in that state */ public void saveState(AffineTransform at, Shape clip, Rectangle2D region, double sx, double sy, Rectangle2D srcRect, AffineTransform xform) { GraphicsState gstate = new GraphicsState(); gstate.theTransform = at; gstate.theClip = clip; gstate.region = region; gstate.sx = sx; gstate.sy = sy; gstate.imageTransform = xform; gstate.imageSrcRect = srcRect; redrawList.add(gstate); } /* * A convenience method which returns the default service * for 2D PrinterJobs. * May return null if there is no suitable default (although there * may still be 2D services available). * @return default 2D print service, or null. * @since 1.4 */ protected static PrintService lookupDefaultPrintService() { PrintService service = PrintServiceLookup.lookupDefaultPrintService(); /* Pageable implies Printable so checking both isn't strictly needed */ if (service != null && service.isDocFlavorSupported( DocFlavor.SERVICE_FORMATTED.PAGEABLE) && service.isDocFlavorSupported( DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { return service; } else { PrintService []services = PrintServiceLookup.lookupPrintServices( DocFlavor.SERVICE_FORMATTED.PAGEABLE, null); if (services.length > 0) { return services[0]; } } return null; } /** * Returns the service (printer) for this printer job. * Implementations of this class which do not support print services * may return null; * @return the service for this printer job. * */ public PrintService getPrintService() { if (myService == null) { PrintService svc = PrintServiceLookup.lookupDefaultPrintService(); if (svc != null && svc.isDocFlavorSupported( DocFlavor.SERVICE_FORMATTED.PAGEABLE)) { try { setPrintService(svc); myService = svc; } catch (PrinterException e) { } } if (myService == null) { PrintService[] svcs = PrintServiceLookup.lookupPrintServices( DocFlavor.SERVICE_FORMATTED.PAGEABLE, null); if (svcs.length > 0) { try { setPrintService(svcs[0]); myService = svcs[0]; } catch (PrinterException e) { } } } } return myService; } /** * Associate this PrinterJob with a new PrintService. * * Throws PrinterException if the specified service * cannot support the Pageable and * Printable interfaces necessary to support 2D printing. * @param a print service which supports 2D printing. * * @throws PrinterException if the specified service does not support * 2D printing. */ public void setPrintService(PrintService service) throws PrinterException { if (service == null) { throw new PrinterException("Service cannot be null"); } else if (service.isDocFlavorSupported( DocFlavor.SERVICE_FORMATTED.PAGEABLE) && service.isDocFlavorSupported( DocFlavor.SERVICE_FORMATTED.PRINTABLE)) { myService = service; } else { throw new PrinterException("Not a 2D print service: " + service); } } /** * Display a dialog to the user allowing the modification of a * PageFormat instance. * The page argument is used to initialize controls * in the page setup dialog. * If the user cancels the dialog, then the method returns the * original page object unmodified. * If the user okays the dialog then the method returns a new * PageFormat object with the indicated changes. * In either case the original page object will * not be modified. * @param page the default PageFormat presented to the user * for modification * @return the original page object if the dialog * is cancelled, or a new PageFormat object containing * the format indicated by the user if the dialog is * acknowledged * @exception HeadlessException if GraphicsEnvironment.isHeadless() * returns true. * @see java.awt.GraphicsEnvironment#isHeadless * @since JDK1.2 */ public PageFormat pageDialog(PageFormat page) throws HeadlessException { if (GraphicsEnvironment.isHeadless()) { throw new HeadlessException(); } GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().getDefaultConfiguration(); PrintService service = getPrintService(); if (service == null) { ServiceDialog.showNoPrintService(gc); return page; } updatePageAttributes(service, page); PageFormat newPage = pageDialog(attributes); if (newPage == null) { return page; } else { return newPage; } } protected void updatePageAttributes(PrintService service, PageFormat page) { if (service == null || page == null) return; float x = (float)Math.rint( (page.getPaper().getWidth()*Size2DSyntax.INCH)/ (72.0))/(float)Size2DSyntax.INCH; float y = (float)Math.rint( (page.getPaper().getHeight()*Size2DSyntax.INCH)/ (72.0))/(float)Size2DSyntax.INCH; OrientationRequested orient; switch (page.getOrientation()) { case PageFormat.LANDSCAPE : orient = OrientationRequested.LANDSCAPE; break; case PageFormat.REVERSE_LANDSCAPE: orient = OrientationRequested.REVERSE_LANDSCAPE; break; default: orient = OrientationRequested.PORTRAIT; } if (attributes == null) { attributes = new HashPrintRequestAttributeSet(); } attributes.add(orient); Media mediaArr[] = (Media[]) service.getSupportedAttributeValues( Media.class, null, null); Media media = null; try { media = findMedia(mediaArr, x, y, Size2DSyntax.INCH); } catch (IllegalArgumentException e) { } if (media == null || !service.isAttributeValueSupported(media, null, null)) { media = (Media)service.getDefaultAttributeValue(Media.class); } if (media != null) { attributes.add(media); } float x2 = (float)(page.getPaper().getImageableX() / 72.0); float w = (float)(page.getPaper().getImageableWidth() / 72.0); float y2 = (float)(page.getPaper().getImageableY() / 72.0); float h = (float)(page.getPaper().getImageableHeight() / 72.0); try { attributes.add(new MediaPrintableArea(x2, y2, w, h, Size2DSyntax.INCH)); } catch (IllegalArgumentException e) { // ignore } } private MediaSizeName findMedia(Media[] mediaArr, float x, float y, int units) { if (x <= 0 || y <= 0 || units < 1) throw new IllegalArgumentException("args must be +ve values"); if (mediaArr == null || mediaArr.length == 0) throw new IllegalArgumentException( "args must have valid array of media"); int count = 0; MediaSizeName names[] = new MediaSizeName[mediaArr.length]; for (int i = 0; i < mediaArr.length; i++) { if (mediaArr[i] instanceof MediaSizeName) { names[count++] = (MediaSizeName)mediaArr[i]; } } if (count == 0) { return null; } int bestIndex = 0; double d = x * x + y * y; Object obj = null; for (int i = 0; i < count; i++) { MediaSize mediasize = MediaSize.getMediaSizeForName(names[i]); if (mediasize == null) { continue; } float[] dim = mediasize.getSize(units); if (x == dim[0] && y == dim[1]) { bestIndex = i; break; } float x2 = x - dim[0]; float y2 = y - dim[1]; double d2 = x2 * x2 + y2 * y2; if (d2 < d) { d = d2; bestIndex = i; } } return names[bestIndex]; } /** * return a PageFormat corresponding to the updated attributes, * or null if the user cancelled the dialog. */ public PageFormat pageDialog(final PrintRequestAttributeSet attributes) throws HeadlessException { if (GraphicsEnvironment.isHeadless()) { throw new HeadlessException(); } PageFormat newPage = (PageFormat) java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run() { GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().getDefaultConfiguration(); Rectangle bounds = gc.getBounds(); int x = bounds.x+bounds.width/3; int y = bounds.y+bounds.height/3; PrintService service = getPrintService(); if (service == null) { ServiceDialog.showNoPrintService(gc); return null; } boolean isNewFrame = false; Window window = KeyboardFocusManager.getCurrentKeyboardFocusManager(). getActiveWindow(); if (!(window instanceof Dialog) && !(window instanceof Frame)) { window = new Frame(); isNewFrame = true; } ServiceDialog pageDialog; if (window instanceof Frame) { pageDialog = new ServiceDialog(gc, x, y, service, DocFlavor.SERVICE_FORMATTED.PAGEABLE, attributes, (Frame)window); } else { pageDialog = new ServiceDialog(gc, x, y, service, DocFlavor.SERVICE_FORMATTED.PAGEABLE, attributes, (Dialog)window); } pageDialog.show(); if (isNewFrame) { window.dispose(); } if (pageDialog.getStatus() == ServiceDialog.APPROVE) { PrintRequestAttributeSet newas = pageDialog.getAttributes(); Class amCategory = SunAlternateMedia.class; if (attributes.containsKey(amCategory) && newas.containsKey(amCategory)) { attributes.remove(amCategory); } attributes.addAll(newas); PageFormat page = defaultPage(); OrientationRequested orient = (OrientationRequested) attributes.get(OrientationRequested.class); int pfOrient = PageFormat.PORTRAIT; if (orient != null) { if (orient == OrientationRequested.REVERSE_LANDSCAPE) { pfOrient = PageFormat.REVERSE_LANDSCAPE; } else if (orient == OrientationRequested.LANDSCAPE) { pfOrient = PageFormat.LANDSCAPE; } } page.setOrientation(pfOrient); Media media = (Media)attributes.get(Media.class); if (media == null) { media = (Media)service.getDefaultAttributeValue(Media.class); } if (!(media instanceof MediaSizeName)) { media = MediaSizeName.NA_LETTER; } MediaSize size = MediaSize.getMediaSizeForName((MediaSizeName)media); if (size == null) { size = MediaSize.NA.LETTER; } int units = (int)(Size2DSyntax.INCH / 72.0); Paper paper = new Paper(); float dim[] = size.getSize(units); double w = Math.rint(dim[0]); double h = Math.rint(dim[1]); paper.setSize(w, h); MediaPrintableArea area = (MediaPrintableArea) attributes.get(MediaPrintableArea.class); double ix, iw, iy, ih; if (area != null) { ix = Math.rint(area.getX(Size2DSyntax.INCH) * DPI); iy = Math.rint(area.getY(Size2DSyntax.INCH) * DPI); iw = Math.rint(area.getWidth(Size2DSyntax.INCH) * DPI); ih = Math.rint(area.getHeight(Size2DSyntax.INCH) * DPI); } else { if (w >= 72.0 * 6.0) { ix = 72.0; iw = w - 2 * 72.0; } else { ix = w / 6.0; iw = w * 0.75; } if (h >= 72.0 * 6.0) { iy = 72.0; ih = h - 2 * 72.0; } else { iy = h / 6.0; ih = h * 0.75; } } paper.setImageableArea(ix, iy, iw, ih); page.setPaper(paper); return page; } else { return null; } } }); return newPage; } /** * Presents the user a dialog for changing properties of the * print job interactively. * The services browsable here are determined by the type of * service currently installed. * If the application installed a StreamPrintService on this * PrinterJob, only the available StreamPrintService (factories) are * browsable. * * @param attributes to store changed properties. * @return false if the user cancels the dialog and true otherwise. * @exception HeadlessException if GraphicsEnvironment.isHeadless() * returns true. * @see java.awt.GraphicsEnvironment#isHeadless */ public boolean printDialog(final PrintRequestAttributeSet attributes) throws HeadlessException { if (GraphicsEnvironment.isHeadless()) { throw new HeadlessException(); } /* A security check has already been performed in the * java.awt.print.printerJob.getPrinterJob method. * So by the time we get here, it is OK for the current thread * to print either to a file (from a Dialog we control!) or * to a chosen printer. * * We raise privilege when we put up the dialog, to avoid * the "warning applet window" banner. */ Boolean doPrint = (Boolean)java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run() { GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment(). getDefaultScreenDevice().getDefaultConfiguration(); PrintService service = getPrintService(); if (service == null) { ServiceDialog.showNoPrintService(gc); return Boolean.FALSE; } PrintService[] services; StreamPrintServiceFactory[] spsFactories = null; if (service instanceof StreamPrintService) { spsFactories = lookupStreamPrintServices(null); services = new StreamPrintService[spsFactories.length]; for (int i=0; i 0.0) ? wid : defaultPaper.getWidth()); hgt = ((hgt > 0.0) ? hgt : defaultPaper.getHeight()); ix = ((ix > 0.0) ? ix : defaultPaper.getImageableX()); iy = ((iy > 0.0) ? iy : defaultPaper.getImageableY()); iw = ((iw > 0.0) ? iw : defaultPaper.getImageableWidth()); ih = ((ih > 0.0) ? ih : defaultPaper.getImageableHeight()); /* full width/height is not likely to be imageable, but since we * don't know the limits we have to allow it */ if (iw > wid) { iw = wid; } if (ih > hgt) { ih = hgt; } if ((ix + iw) > wid) { ix = wid - iw; } if ((iy + ih) > hgt) { iy = hgt - ih; } newPaper.setSize(wid, hgt); newPaper.setImageableArea(ix, iy, iw, ih); } } /** * The passed in PageFormat will be copied and altered to describe * the default page size and orientation of the PrinterJob's * current printer. */ public PageFormat defaultPage(PageFormat page) { PageFormat newPage = (PageFormat)page.clone(); newPage.setOrientation(PageFormat.PORTRAIT); Paper newPaper = new Paper(); /* Default to A4 paper outside North America. Platform * subclasses which can access the actual default paper size * for a printer should override this method. */ String defaultCountry = Locale.getDefault().getCountry(); if (defaultCountry != null && (!defaultCountry.equals(Locale.US.getCountry()) && !defaultCountry.equals(Locale.CANADA.getCountry()))) { double mmPerInch = 25.4; double ptsPerInch = 72.0; double a4Width = Math.rint((210.0*ptsPerInch)/mmPerInch); double a4Height = Math.rint((297.0*ptsPerInch)/mmPerInch); newPaper.setSize(a4Width, a4Height); newPaper.setImageableArea(ptsPerInch, ptsPerInch, a4Width - 2.0*ptsPerInch, a4Height - 2.0*ptsPerInch); } newPage.setPaper(newPaper); return newPage; } /** * The passed in PageFormat is cloned and altered to be usable on * the PrinterJob's current printer. */ public PageFormat validatePage(PageFormat page) { PageFormat newPage = (PageFormat)page.clone(); Paper newPaper = new Paper(); validatePaper(newPage.getPaper(), newPaper); newPage.setPaper(newPaper); return newPage; } /** * Set the number of copies to be printed. */ public void setCopies(int copies) { mNumCopies = copies; } /** * Get the number of copies to be printed. */ public int getCopies() { return mNumCopies; } /* Used when executing a print job where an attribute set may * over ride API values. */ protected int getCopiesInt() { return (copiesAttr > 0) ? copiesAttr : getCopies(); } /** * Get the name of the printing user. * The caller must have security permission to read system properties. */ public String getUserName() { return System.getProperty("user.name"); } /* Used when executing a print job where an attribute set may * over ride API values. */ protected String getUserNameInt() { if (userNameAttr != null) { return userNameAttr; } else { try { return getUserName(); } catch (SecurityException e) { return ""; } } } /** * Set the name of the document to be printed. * The document name can not be null. */ public void setJobName(String jobName) { if (jobName != null) { mDocName = jobName; } else { throw new NullPointerException(); } } /** * Get the name of the document to be printed. */ public String getJobName() { return mDocName; } /* Used when executing a print job where an attribute set may * over ride API values. */ protected String getJobNameInt() { return (jobNameAttr != null) ? jobNameAttr : getJobName(); } /** * Set the range of pages from a Book to be printed. * Both 'firstPage' and 'lastPage' are zero based * page indices. If either parameter is less than * zero then the page range is set to be from the * first page to the last. */ protected void setPageRange(int firstPage, int lastPage) { if(firstPage >= 0 && lastPage >= 0) { mFirstPage = firstPage; mLastPage = lastPage; if(mLastPage < mFirstPage) mLastPage = mFirstPage; } else { mFirstPage = Pageable.UNKNOWN_NUMBER_OF_PAGES; mLastPage = Pageable.UNKNOWN_NUMBER_OF_PAGES; } } /** * Return the zero based index of the first page to * be printed in this job. */ protected int getFirstPage() { return mFirstPage == Book.UNKNOWN_NUMBER_OF_PAGES ? 0 : mFirstPage; } /** * Return the zero based index of the last page to * be printed in this job. */ protected int getLastPage() { return mLastPage; } /** * Set whether copies should be collated or not. * Two collated copies of a three page document * print in this order: 1, 2, 3, 1, 2, 3 while * uncollated copies print in this order: * 1, 1, 2, 2, 3, 3. * This is set when request is using an attribute set. */ protected void setCollated(boolean collate) { mCollate = collate; collateAttReq = true; } /** * Return true if collated copies will be printed as determined * in an attribute set. */ protected boolean isCollated() { return mCollate; } /** * Called by the print() method at the start of * a print job. */ protected abstract void startDoc() throws PrinterException; /** * Called by the print() method at the end of * a print job. */ protected abstract void endDoc() throws PrinterException; /* Called by cancelDoc */ protected abstract void abortDoc(); private void cancelDoc() throws PrinterAbortException { abortDoc(); synchronized (this) { userCancelled = false; performingPrinting = false; notify(); } throw new PrinterAbortException(); } /** * Returns how many times the entire book should * be printed by the PrintJob. If the printer * itself supports collation then this method * should return 1 indicating that the entire * book need only be printed once and the copies * will be collated and made in the printer. */ protected int getCollatedCopies() { return isCollated() ? getCopiesInt() : 1; } /** * Returns how many times each page in the book * should be consecutively printed by PrintJob. * If the printer makes copies itself then this * method should return 1. */ protected int getNoncollatedCopies() { return isCollated() ? 1 : getCopiesInt(); } /** * Print a page from the provided document. * @return int Printable.PAGE_EXISTS if the page existed and was drawn and * Printable.NO_SUCH_PAGE if the page did not exist. * @see java.awt.print.Printable */ protected int printPage(Pageable document, int pageIndex) throws PrinterException { PageFormat page; Printable painter; try { page = document.getPageFormat(pageIndex); painter = document.getPrintable(pageIndex); } catch (Exception e) { throw new PrinterException("No page or printable exists."); } /* Get the imageable area from Paper instead of PageFormat * because we do not want it adjusted by the page orientation. */ Paper paper = page.getPaper(); double xScale = getXRes() / 72.0; double yScale = getYRes() / 72.0; /* The deviceArea is the imageable area in the printer's * resolution. */ Rectangle2D deviceArea = new Rectangle2D.Double(paper.getImageableX() * xScale, paper.getImageableY() * yScale, paper.getImageableWidth() * xScale, paper.getImageableHeight() * yScale); /* Build and hold on to a uniform transform so that * we can get back to device space at the beginning * of each band. */ AffineTransform uniformTransform = new AffineTransform(); /* The scale transform is used to switch from the * device space to the user's 72 dpi space. */ AffineTransform scaleTransform = new AffineTransform(); scaleTransform.scale(xScale, yScale); /* bandwidth is multiple of 4 as the data is used in a win32 DIB and * some drivers behave badly if scanlines aren't multiples of 4 bytes. */ int bandWidth = (int) deviceArea.getWidth(); if (bandWidth % 4 != 0) { bandWidth += (4 - (bandWidth % 4)); } if (bandWidth <= 0) { throw new PrinterException("Paper's imageable width is too small."); } int deviceAreaHeight = (int)deviceArea.getHeight(); if (deviceAreaHeight <= 0) { throw new PrinterException("Paper's imageable height is too small."); } /* Figure out the number of lines that will fit into * our maximum band size. The hard coded 3 reflects the * fact that we can only create 24 bit per pixel 3 byte BGR * BufferedImages. FIX. */ int bandHeight = (int)(MAX_BAND_SIZE / bandWidth / 3); int deviceLeft = (int)Math.rint(paper.getImageableX() * xScale); int deviceTop = (int)Math.rint(paper.getImageableY() * yScale); /* The device transform is used to move the band down * the page using translates. Normally this is all it * would do, but since, when printing, the Window's * DIB format wants the last line to be first (lowest) in * memory, the deviceTransform moves the origin to the * bottom of the band and flips the origin. This way the * app prints upside down into the band which is the DIB * format. */ AffineTransform deviceTransform = new AffineTransform(); deviceTransform.translate(-deviceLeft, deviceTop); deviceTransform.translate(0, bandHeight); deviceTransform.scale(1, -1); /* Create a BufferedImage to hold the band. We set the clip * of the band to be tight around the bits so that the * application can use it to figure what part of the * page needs to be drawn. The clip is never altered in * this method, but we do translate the band's coordinate * system so that the app will see the clip moving down the * page though it s always around the same set of pixels. */ BufferedImage pBand = new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR); /* Have the app draw into a PeekGraphics object so we can * learn something about the needs of the print job. */ PeekGraphics peekGraphics = createPeekGraphics(pBand.createGraphics(), this); Rectangle2D.Double pageFormatArea = new Rectangle2D.Double(page.getImageableX(), page.getImageableY(), page.getImageableWidth(), page.getImageableHeight()); initPrinterGraphics(peekGraphics, pageFormatArea); //initPrinterGraphics(peekGraphics, deviceArea); int pageResult = painter.print(peekGraphics, page, pageIndex); if (pageResult == Printable.PAGE_EXISTS) { startPage(page, painter, pageIndex); Graphics2D pathGraphics = createPathGraphics(peekGraphics, this, painter, page, pageIndex); /* If we can convert the page directly to the * underlying graphics system then we do not * need to rasterize. We also may not need to * create the 'band' if all the pages can take * this path. */ if (pathGraphics != null) { pathGraphics.transform(scaleTransform); // user (0,0) should be origin of page, not imageable area pathGraphics.translate(-getPhysicalPrintableX(paper) / xScale, -getPhysicalPrintableY(paper) / yScale); pathGraphics.transform(new AffineTransform(page.getMatrix())); initPrinterGraphics(pathGraphics, pageFormatArea); redrawList.clear(); painter.print(pathGraphics, page, pageIndex); for (int i=0;i 0 && bandHeight > 0)) { /* if the client has specified an imageable X or Y * which is off than the physically addressable * area of the page, then we need to adjust for that * here so that we pass only non -ve band coordinates * We also need to translate by the adjusted amount * so that printing appears in the correct place. */ int bandX = deviceLeft - deviceAddressableX; if (bandX < 0) { bandGraphics.translate(bandX/xScale,0); bandX = 0; } int bandY = deviceTop + bandTop - deviceAddressableY; if (bandY < 0) { bandGraphics.translate(0,bandY/yScale); bandY = 0; } /* Have the app's painter image into the band * and then send the band to the printer. */ painterGraphics.setDelegate((Graphics2D) bandGraphics.create()); painter.print(painterGraphics, page, pageIndex); painterGraphics.dispose(); printBand(data, bandX, bandY, bandWidth, bandHeight); } } clearGraphics.dispose(); bandGraphics.dispose(); } endPage(page, painter, pageIndex); } return pageResult; } /** * If a print job is in progress, print() has been * called but has not returned, then this signals * that the job should be cancelled and the next * chance. If there is no print job in progress then * this call does nothing. */ public void cancel() { synchronized (this) { if (performingPrinting) { userCancelled = true; } notify(); } } /** * Returns true is a print job is ongoing but will * be cancelled and the next opportunity. false is * returned otherwise. */ public boolean isCancelled() { boolean cancelled = false; synchronized (this) { cancelled = (performingPrinting && userCancelled); notify(); } return cancelled; } /** * Return the Pageable describing the pages to be printed. */ protected Pageable getPageable() { return mDocument; } /** * Examine the metrics captured by the * PeekGraphics instance and * if capable of directly converting this * print job to the printer's control language * or the native OS's graphics primitives, then * return a PathGraphics to perform * that conversion. If there is not an object * capable of the conversion then return * null. Returning null * causes the print job to be rasterized. */ protected Graphics2D createPathGraphics(PeekGraphics graphics, PrinterJob printerJob, Printable painter, PageFormat pageFormat, int pageIndex) { return null; } /** * Create and return an object that will * gather and hold metrics about the print * job. This method is passed a Graphics2D * object that can be used as a proxy for the * object gathering the print job matrics. The * method is also supplied with the instance * controlling the print job, printerJob. */ protected PeekGraphics createPeekGraphics(Graphics2D graphics, PrinterJob printerJob) { return new PeekGraphics(graphics, printerJob); } /** * Configure the passed in Graphics2D so that * is contains the defined initial settings * for a print job. These settings are: * color: black. * clip: */ void initPrinterGraphics(Graphics2D g, Rectangle2D clip) { g.setClip(clip); g.setPaint(Color.black); } /** * User dialogs should disable "File" buttons if this returns false. * */ public boolean checkAllowedToPrintToFile() { try { throwPrintToFile(); return true; } catch (SecurityException e) { return false; } } /** * Break this out as it may be useful when we allow API to * specify printing to a file. In that case its probably right * to throw a SecurityException if the permission is not granted */ private void throwPrintToFile() { SecurityManager security = System.getSecurityManager(); if (security != null) { if (printToFilePermission == null) { printToFilePermission = new FilePermission("<>", "read,write"); } security.checkPermission(printToFilePermission); } } }