| /* |
| * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.print; |
| |
| import java.io.FilePermission; |
| |
| import java.awt.Color; |
| import java.awt.Dialog; |
| import java.awt.Frame; |
| 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.geom.AffineTransform; |
| import java.awt.geom.Point2D; |
| 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.awt.Window; |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| 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.ResolutionSyntax; |
| import javax.print.attribute.Size2DSyntax; |
| import javax.print.attribute.standard.Copies; |
| import javax.print.attribute.standard.Destination; |
| import javax.print.attribute.standard.DialogTypeSelection; |
| 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.PrinterResolution; |
| import javax.print.attribute.standard.PrinterState; |
| import javax.print.attribute.standard.PrinterStateReason; |
| import javax.print.attribute.standard.PrinterStateReasons; |
| import javax.print.attribute.standard.PrinterIsAcceptingJobs; |
| import javax.print.attribute.standard.RequestingUserName; |
| import javax.print.attribute.standard.SheetCollate; |
| import javax.print.attribute.standard.Sides; |
| |
| /** |
| * A class which rasterizes a printer job. |
| * |
| * @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; |
| |
| /** |
| * Pageable MAX pages |
| */ |
| protected static final int MAX_UNKNOWN_PAGES = 9999; |
| |
| protected static final int PD_ALLPAGES = 0x00000000; |
| protected static final int PD_SELECTION = 0x00000001; |
| protected static final int PD_PAGENUMS = 0x00000002; |
| protected static final int PD_NOSELECTION = 0x00000004; |
| |
| /** |
| * 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); |
| |
| /* Dots Per Inch */ |
| private static final float DPI = 72.0f; |
| |
| /** |
| * Useful mainly for debugging, this system property |
| * can be used to force the printing code to print |
| * using a particular pipeline. The two currently |
| * supported values are FORCE_RASTER and FORCE_PDL. |
| */ |
| private static final String FORCE_PIPE_PROP = "sun.java2d.print.pipeline"; |
| |
| /** |
| * When the system property FORCE_PIPE_PROP has this value |
| * then each page of a print job will be rendered through |
| * the raster pipeline. |
| */ |
| private static final String FORCE_RASTER = "raster"; |
| |
| /** |
| * When the system property FORCE_PIPE_PROP has this value |
| * then each page of a print job will be rendered through |
| * the PDL pipeline. |
| */ |
| private static final String FORCE_PDL = "pdl"; |
| |
| /** |
| * When the system property SHAPE_TEXT_PROP has this value |
| * then text is always rendered as a shape, and no attempt is made |
| * to match the font through GDI |
| */ |
| private static final String SHAPE_TEXT_PROP = "sun.java2d.print.shapetext"; |
| |
| /** |
| * values obtained from System properties in static initialiser block |
| */ |
| public static boolean forcePDL = false; |
| public static boolean forceRaster = false; |
| public static boolean shapeTextProp = false; |
| |
| static { |
| /* The system property FORCE_PIPE_PROP |
| * can be used to force the printing code to |
| * use a particular pipeline. Either the raster |
| * pipeline or the pdl pipeline can be forced. |
| */ |
| String forceStr = java.security.AccessController.doPrivileged( |
| new sun.security.action.GetPropertyAction(FORCE_PIPE_PROP)); |
| |
| if (forceStr != null) { |
| if (forceStr.equalsIgnoreCase(FORCE_PDL)) { |
| forcePDL = true; |
| } else if (forceStr.equalsIgnoreCase(FORCE_RASTER)) { |
| forceRaster = true; |
| } |
| } |
| |
| String shapeTextStr =java.security.AccessController.doPrivileged( |
| new sun.security.action.GetPropertyAction(SHAPE_TEXT_PROP)); |
| |
| if (shapeTextStr != null) { |
| shapeTextProp = true; |
| } |
| } |
| |
| /* Instance Variables */ |
| |
| /** |
| * Used to minimize 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 previous print stream Paper |
| * Used to check if the paper size has changed such that the |
| * implementation needs to emit the new paper size information |
| * into the print stream. |
| * Since we do our own rotation, and the margins aren't relevant, |
| * Its strictly the dimensions of the paper that we will check. |
| */ |
| private Paper previousPaper; |
| |
| /** |
| * The document to be printed. It is initialized to an |
| * empty (zero pages) book. |
| */ |
| // MacOSX - made protected so subclasses can reference it. |
| protected Pageable mDocument = new Book(); |
| |
| /** |
| * The name of the job being printed. |
| */ |
| private String mDocName = "Java Printing"; |
| |
| |
| /** |
| * Printing cancellation flags |
| */ |
| // MacOSX - made protected so subclasses can reference it. |
| protected boolean performingPrinting = false; |
| // MacOSX - made protected so subclasses can reference it. |
| protected boolean userCancelled = false; |
| |
| /** |
| * Print to file permission variables. |
| */ |
| private FilePermission printToFilePermission; |
| |
| /** |
| * List of areas & the graphics state for redrawing |
| */ |
| private ArrayList<GraphicsState> 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 PrinterResolution printerResAttr; |
| protected Sides sidesAttr; |
| protected String destinationAttr; |
| protected boolean noJobSheet = false; |
| protected int mDestType = RasterPrinterJob.FILE; |
| protected String mDestination = ""; |
| protected boolean collateAttReq = false; |
| |
| /** |
| * Device rotation flag, if it support 270, this is set to true; |
| */ |
| protected boolean landscapeRotates270 = 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 |
| * "region" is an area at as a high a resolution as possible. |
| * The redrawing code needs to look at sx, sy to calculate the scale |
| * to device resolution. |
| */ |
| private class GraphicsState { |
| Rectangle2D region; // Area of page to repaint |
| Shape theClip; // image drawing clip. |
| AffineTransform theTransform; // to transform clip to dev coords. |
| double sx; // X scale from region to device resolution |
| double sy; // Y scale from region to device resolution |
| } |
| |
| /** |
| * Service for this job |
| */ |
| protected PrintService myService; |
| |
| /* Constructors */ |
| |
| public RasterPrinterJob() |
| { |
| } |
| |
| /* Abstract Methods */ |
| |
| /** |
| * Returns the resolution in dots per inch across the width |
| * of the page. |
| */ |
| protected abstract double getXRes(); |
| |
| /** |
| * Returns the resolution in dots per inch down the height |
| * of the page. |
| */ |
| protected abstract double getYRes(); |
| |
| /** |
| * Must be obtained from the current printer. |
| * Value is in device pixels. |
| * Not adjusted for orientation of the paper. |
| */ |
| protected abstract double getPhysicalPrintableX(Paper p); |
| |
| /** |
| * Must be obtained from the current printer. |
| * Value is in device pixels. |
| * Not adjusted for orientation of the paper. |
| */ |
| protected abstract double getPhysicalPrintableY(Paper p); |
| |
| /** |
| * Must be obtained from the current printer. |
| * Value is in device pixels. |
| * Not adjusted for orientation of the paper. |
| */ |
| protected abstract double getPhysicalPrintableWidth(Paper p); |
| |
| /** |
| * Must be obtained from the current printer. |
| * Value is in device pixels. |
| * Not adjusted for orientation of the paper. |
| */ |
| protected abstract double getPhysicalPrintableHeight(Paper p); |
| |
| /** |
| * Must be obtained from the current printer. |
| * Value is in device pixels. |
| * Not adjusted for orientation of the paper. |
| */ |
| protected abstract double getPhysicalPageWidth(Paper p); |
| |
| /** |
| * Must be obtained from the current printer. |
| * Value is in device pixels. |
| * Not adjusted for orientation of the paper. |
| */ |
| protected abstract double getPhysicalPageHeight(Paper p); |
| |
| /** |
| * Begin a new page. |
| */ |
| protected abstract void startPage(PageFormat format, Printable painter, |
| int index, boolean paperChanged) |
| throws PrinterException; |
| |
| /** |
| * End a page. |
| */ |
| protected abstract 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. |
| */ |
| protected abstract 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) { |
| GraphicsState gstate = new GraphicsState(); |
| gstate.theTransform = at; |
| gstate.theClip = clip; |
| gstate.region = region; |
| gstate.sx = sx; |
| gstate.sy = sy; |
| redrawList.add(gstate); |
| } |
| |
| |
| /* |
| * A convenience method which returns the default service |
| * for 2D {@code PrinterJob}s. |
| * 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 {@code PrinterException} if the specified service |
| * cannot support the {@code Pageable} and |
| * {@code Printable} interfaces necessary to support 2D printing. |
| * @param service print service which supports 2D printing. |
| * |
| * @throws PrinterException if the specified service does not support |
| * 2D printing or no longer available. |
| */ |
| public void setPrintService(PrintService service) |
| throws PrinterException { |
| if (service == null) { |
| throw new PrinterException("Service cannot be null"); |
| } else if (!(service instanceof StreamPrintService) && |
| service.getName() == null) { |
| throw new PrinterException("Null PrintService name."); |
| } else { |
| // Check the list of services. This service may have been |
| // deleted already |
| PrinterState prnState = service.getAttribute(PrinterState.class); |
| if (prnState == PrinterState.STOPPED) { |
| PrinterStateReasons prnStateReasons = |
| service.getAttribute(PrinterStateReasons.class); |
| if ((prnStateReasons != null) && |
| (prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN))) |
| { |
| throw new PrinterException("PrintService is no longer available."); |
| } |
| } |
| |
| |
| 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); |
| } |
| } |
| } |
| |
| private PageFormat attributeToPageFormat(PrintService service, |
| PrintRequestAttributeSet attSet) { |
| PageFormat page = defaultPage(); |
| |
| if (service == null) { |
| return page; |
| } |
| |
| OrientationRequested orient = (OrientationRequested) |
| attSet.get(OrientationRequested.class); |
| if (orient == null) { |
| orient = (OrientationRequested) |
| service.getDefaultAttributeValue(OrientationRequested.class); |
| } |
| if (orient == OrientationRequested.REVERSE_LANDSCAPE) { |
| page.setOrientation(PageFormat.REVERSE_LANDSCAPE); |
| } else if (orient == OrientationRequested.LANDSCAPE) { |
| page.setOrientation(PageFormat.LANDSCAPE); |
| } else { |
| page.setOrientation(PageFormat.PORTRAIT); |
| } |
| |
| Media media = (Media)attSet.get(Media.class); |
| MediaSize size = getMediaSize(media, service, page); |
| |
| Paper paper = new Paper(); |
| float dim[] = size.getSize(1); //units == 1 to avoid FP error |
| double w = Math.rint((dim[0]*72.0)/Size2DSyntax.INCH); |
| double h = Math.rint((dim[1]*72.0)/Size2DSyntax.INCH); |
| paper.setSize(w, h); |
| MediaPrintableArea area = |
| (MediaPrintableArea) |
| attSet.get(MediaPrintableArea.class); |
| if (area == null) { |
| area = getDefaultPrintableArea(page, w, h); |
| } |
| |
| double ix, iw, iy, ih; |
| // Should pass in same unit as updatePageAttributes |
| // to avoid rounding off errors. |
| ix = Math.rint( |
| area.getX(MediaPrintableArea.INCH) * DPI); |
| iy = Math.rint( |
| area.getY(MediaPrintableArea.INCH) * DPI); |
| iw = Math.rint( |
| area.getWidth(MediaPrintableArea.INCH) * DPI); |
| ih = Math.rint( |
| area.getHeight(MediaPrintableArea.INCH) * DPI); |
| paper.setImageableArea(ix, iy, iw, ih); |
| page.setPaper(paper); |
| return page; |
| } |
| protected MediaSize getMediaSize(Media media, PrintService service, |
| PageFormat page) { |
| if (media == null) { |
| media = (Media)service.getDefaultAttributeValue(Media.class); |
| } |
| if (!(media instanceof MediaSizeName)) { |
| media = MediaSizeName.NA_LETTER; |
| } |
| MediaSize size = MediaSize.getMediaSizeForName((MediaSizeName) media); |
| return size != null ? size : MediaSize.NA.LETTER; |
| } |
| |
| protected MediaPrintableArea getDefaultPrintableArea(PageFormat page, |
| double w, double h) { |
| double ix, iw, iy, ih; |
| 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; |
| } |
| |
| return new MediaPrintableArea((float) (ix / DPI), (float) (iy / DPI), |
| (float) (iw / DPI), (float) (ih / DPI), MediaPrintableArea.INCH); |
| } |
| |
| protected void updatePageAttributes(PrintService service, |
| PageFormat page) { |
| if (this.attributes == null) { |
| this.attributes = new HashPrintRequestAttributeSet(); |
| } |
| |
| updateAttributesWithPageFormat(service, page, this.attributes); |
| } |
| |
| protected void updateAttributesWithPageFormat(PrintService service, |
| PageFormat page, |
| PrintRequestAttributeSet pageAttributes) { |
| if (service == null || page == null || pageAttributes == 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; |
| |
| // We should limit the list where we search the matching |
| // media, this will prevent mapping to wrong media ex. Ledger |
| // can be mapped to B. Especially useful when creating |
| // custom MediaSize. |
| Media[] mediaList = (Media[])service.getSupportedAttributeValues( |
| Media.class, null, null); |
| Media media = null; |
| try { |
| media = CustomMediaSizeName.findMedia(mediaList, x, y, |
| Size2DSyntax.INCH); |
| } catch (IllegalArgumentException iae) { |
| } |
| if ((media == null) || |
| !(service.isAttributeValueSupported(media, null, null))) { |
| media = (Media)service.getDefaultAttributeValue(Media.class); |
| } |
| |
| 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 (media != null) { |
| pageAttributes.add(media); |
| } |
| pageAttributes.add(orient); |
| |
| float ix = (float)(page.getPaper().getImageableX()/DPI); |
| float iw = (float)(page.getPaper().getImageableWidth()/DPI); |
| float iy = (float)(page.getPaper().getImageableY()/DPI); |
| float ih = (float)(page.getPaper().getImageableHeight()/DPI); |
| |
| if (ix < 0) ix = 0; if (iy < 0) iy = 0; |
| if (iw <= 0) iw = (float)(page.getPaper().getWidth()/DPI) - (ix*2); |
| |
| // If iw is still negative, it means ix is too large to print |
| // anything inside printable area if we have to leave the same margin |
| // in the right side of paper so we go back to default mpa values |
| if (iw < 0) iw = 0; |
| |
| if (ih <= 0) ih = (float)(page.getPaper().getHeight()/DPI) - (iy*2); |
| |
| // If ih is still negative, it means iy is too large to print |
| // anything inside printable area if we have to leave the same margin |
| // in the bottom side of paper so we go back to default mpa values |
| if (ih < 0) ih = 0; |
| try { |
| pageAttributes.add(new MediaPrintableArea(ix, iy, iw, ih, |
| MediaPrintableArea.INCH)); |
| } catch (IllegalArgumentException iae) { |
| } |
| } |
| |
| /** |
| * Display a dialog to the user allowing the modification of a |
| * PageFormat instance. |
| * The {@code page} argument is used to initialize controls |
| * in the page setup dialog. |
| * If the user cancels the dialog, then the method returns the |
| * original {@code 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 {@code page} object will |
| * not be modified. |
| * @param page the default PageFormat presented to the user |
| * for modification |
| * @return the original {@code 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 1.2 |
| */ |
| public PageFormat pageDialog(PageFormat page) |
| throws HeadlessException { |
| if (GraphicsEnvironment.isHeadless()) { |
| throw new HeadlessException(); |
| } |
| |
| final GraphicsConfiguration gc = |
| GraphicsEnvironment.getLocalGraphicsEnvironment(). |
| getDefaultScreenDevice().getDefaultConfiguration(); |
| |
| PrintService service = java.security.AccessController.doPrivileged( |
| new java.security.PrivilegedAction<PrintService>() { |
| public PrintService run() { |
| PrintService service = getPrintService(); |
| if (service == null) { |
| ServiceDialog.showNoPrintService(gc); |
| return null; |
| } |
| return service; |
| } |
| }); |
| |
| if (service == null) { |
| return page; |
| } |
| updatePageAttributes(service, page); |
| |
| PageFormat newPage = null; |
| DialogTypeSelection dts = |
| (DialogTypeSelection)attributes.get(DialogTypeSelection.class); |
| if (dts == DialogTypeSelection.NATIVE) { |
| // Remove DialogTypeSelection.NATIVE to prevent infinite loop in |
| // RasterPrinterJob. |
| attributes.remove(DialogTypeSelection.class); |
| newPage = pageDialog(attributes); |
| // restore attribute |
| attributes.add(DialogTypeSelection.NATIVE); |
| } else { |
| newPage = pageDialog(attributes); |
| } |
| |
| if (newPage == null) { |
| return page; |
| } else { |
| return newPage; |
| } |
| } |
| |
| /** |
| * return a PageFormat corresponding to the updated attributes, |
| * or null if the user cancelled the dialog. |
| */ |
| @SuppressWarnings("deprecation") |
| public PageFormat pageDialog(final PrintRequestAttributeSet attributes) |
| throws HeadlessException { |
| if (GraphicsEnvironment.isHeadless()) { |
| throw new HeadlessException(); |
| } |
| |
| DialogTypeSelection dlg = |
| (DialogTypeSelection)attributes.get(DialogTypeSelection.class); |
| |
| // Check for native, note that default dialog is COMMON. |
| if (dlg == DialogTypeSelection.NATIVE) { |
| PrintService pservice = getPrintService(); |
| PageFormat pageFrmAttrib = attributeToPageFormat(pservice, |
| attributes); |
| PageFormat page = pageDialog(pageFrmAttrib); |
| |
| // If user cancels the dialog, pageDialog() will return the original |
| // page object and as per spec, we should return null in that case. |
| if (page == pageFrmAttrib) { |
| return null; |
| } |
| updateAttributesWithPageFormat(pservice, page, attributes); |
| return page; |
| } |
| |
| GraphicsConfiguration grCfg = null; |
| Window w = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); |
| if (w != null) { |
| grCfg = w.getGraphicsConfiguration(); |
| } else { |
| grCfg = GraphicsEnvironment.getLocalGraphicsEnvironment(). |
| getDefaultScreenDevice().getDefaultConfiguration(); |
| } |
| final GraphicsConfiguration gc = grCfg; |
| |
| PrintService service = java.security.AccessController.doPrivileged( |
| new java.security.PrivilegedAction<PrintService>() { |
| public PrintService run() { |
| PrintService service = getPrintService(); |
| if (service == null) { |
| ServiceDialog.showNoPrintService(gc); |
| return null; |
| } |
| return service; |
| } |
| }); |
| |
| if (service == null) { |
| return null; |
| } |
| |
| // we position the dialog a little beyond the upper-left corner of the window |
| // which is consistent with the NATIVE page dialog |
| Rectangle gcBounds = gc.getBounds(); |
| int x = gcBounds.x+50; |
| int y = gcBounds.y+50; |
| ServiceDialog pageDialog; |
| if (w instanceof Frame) { |
| pageDialog = new ServiceDialog(gc, x, y, service, |
| DocFlavor.SERVICE_FORMATTED.PAGEABLE, |
| attributes,(Frame)w); |
| } else { |
| pageDialog = new ServiceDialog(gc, x, y, service, |
| DocFlavor.SERVICE_FORMATTED.PAGEABLE, |
| attributes, (Dialog)w); |
| } |
| Rectangle dlgBounds = pageDialog.getBounds(); |
| |
| // if portion of dialog is not within the gc boundary |
| if (!gcBounds.contains(dlgBounds)) { |
| // check if dialog exceed window bounds at left or bottom |
| // Then position the dialog by moving it by the amount it exceeds |
| // the window bounds |
| // If it results in dialog moving beyond the window bounds at top/left |
| // then position it at window top/left |
| if (dlgBounds.x + dlgBounds.width > gcBounds.x + gcBounds.width) { |
| if ((gcBounds.x + gcBounds.width - dlgBounds.width) > gcBounds.x) { |
| x = (gcBounds.x + gcBounds.width) - dlgBounds.width; |
| } else { |
| x = gcBounds.x; |
| } |
| } |
| if (dlgBounds.y + dlgBounds.height > gcBounds.y + gcBounds.height) { |
| if ((gcBounds.y + gcBounds.height - dlgBounds.height) > gcBounds.y) { |
| y = (gcBounds.y + gcBounds.height) - dlgBounds.height; |
| } else { |
| y = gcBounds.y; |
| } |
| } |
| pageDialog.setBounds(x, y, dlgBounds.width, dlgBounds.height); |
| } |
| pageDialog.show(); |
| |
| 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); |
| return attributeToPageFormat(service, attributes); |
| } else { |
| return null; |
| } |
| } |
| |
| protected PageFormat getPageFormatFromAttributes() { |
| if (attributes == null || attributes.isEmpty()) { |
| return null; |
| } |
| return attributeToPageFormat(getPrintService(), this.attributes); |
| } |
| |
| |
| /** |
| * 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(); |
| } |
| |
| DialogTypeSelection dlg = |
| (DialogTypeSelection)attributes.get(DialogTypeSelection.class); |
| |
| // Check for native, note that default dialog is COMMON. |
| if (dlg == DialogTypeSelection.NATIVE) { |
| this.attributes = attributes; |
| try { |
| debug_println("calling setAttributes in printDialog"); |
| setAttributes(attributes); |
| |
| } catch (PrinterException e) { |
| |
| } |
| |
| boolean ret = printDialog(); |
| this.attributes = attributes; |
| return ret; |
| |
| } |
| |
| /* 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. |
| */ |
| GraphicsConfiguration grCfg = null; |
| Window w = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); |
| if (w != null) { |
| grCfg = w.getGraphicsConfiguration(); |
| /* Add DialogOwner attribute to set the owner of this print dialog |
| * only if it is not set already |
| * (it might be set in java.awt.PrintJob.printDialog) |
| */ |
| if (attributes.get(DialogOwner.class) == null) { |
| attributes.add(w instanceof Frame ? new DialogOwner((Frame)w) : |
| new DialogOwner((Dialog)w)); |
| } |
| } else { |
| grCfg = GraphicsEnvironment.getLocalGraphicsEnvironment(). |
| getDefaultScreenDevice().getDefaultConfiguration(); |
| } |
| final GraphicsConfiguration gc = grCfg; |
| |
| PrintService service = java.security.AccessController.doPrivileged( |
| new java.security.PrivilegedAction<PrintService>() { |
| public PrintService run() { |
| PrintService service = getPrintService(); |
| if (service == null) { |
| ServiceDialog.showNoPrintService(gc); |
| return null; |
| } |
| return service; |
| } |
| }); |
| |
| if (service == null) { |
| return false; |
| } |
| |
| PrintService[] services; |
| StreamPrintServiceFactory[] spsFactories = null; |
| if (service instanceof StreamPrintService) { |
| spsFactories = lookupStreamPrintServices(null); |
| services = new StreamPrintService[spsFactories.length]; |
| for (int i=0; i<spsFactories.length; i++) { |
| services[i] = spsFactories[i].getPrintService(null); |
| } |
| } else { |
| services = java.security.AccessController.doPrivileged( |
| new java.security.PrivilegedAction<PrintService[]>() { |
| public PrintService[] run() { |
| PrintService[] services = PrinterJob.lookupPrintServices(); |
| return services; |
| } |
| }); |
| |
| if ((services == null) || (services.length == 0)) { |
| /* |
| * No services but default PrintService exists? |
| * Create services using defaultService. |
| */ |
| services = new PrintService[1]; |
| services[0] = service; |
| } |
| } |
| |
| // we position the dialog a little beyond the upper-left corner of the window |
| // which is consistent with the NATIVE print dialog |
| int x = 50; |
| int y = 50; |
| PrintService newService; |
| // temporarily add an attribute pointing back to this job. |
| PrinterJobWrapper jobWrapper = new PrinterJobWrapper(this); |
| attributes.add(jobWrapper); |
| PageRanges pgRng = (PageRanges)attributes.get(PageRanges.class); |
| if (pgRng == null && mDocument.getNumberOfPages() > 1) { |
| attributes.add(new PageRanges(1, mDocument.getNumberOfPages())); |
| } |
| try { |
| newService = |
| ServiceUI.printDialog(gc, x, y, |
| services, service, |
| DocFlavor.SERVICE_FORMATTED.PAGEABLE, |
| attributes); |
| } catch (IllegalArgumentException iae) { |
| newService = ServiceUI.printDialog(gc, x, y, |
| services, services[0], |
| DocFlavor.SERVICE_FORMATTED.PAGEABLE, |
| attributes); |
| } |
| attributes.remove(PrinterJobWrapper.class); |
| attributes.remove(DialogOwner.class); |
| |
| if (newService == null) { |
| return false; |
| } |
| |
| if (!service.equals(newService)) { |
| try { |
| setPrintService(newService); |
| } catch (PrinterException e) { |
| /* |
| * The only time it would throw an exception is when |
| * newService is no longer available but we should still |
| * select this printer. |
| */ |
| myService = newService; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Presents the user a dialog for changing properties of the |
| * print job interactively. |
| * @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() throws HeadlessException { |
| |
| if (GraphicsEnvironment.isHeadless()) { |
| throw new HeadlessException(); |
| } |
| |
| PrintRequestAttributeSet attributes = |
| new HashPrintRequestAttributeSet(); |
| attributes.add(new Copies(getCopies())); |
| attributes.add(new JobName(getJobName(), null)); |
| boolean doPrint = printDialog(attributes); |
| if (doPrint) { |
| JobName jobName = (JobName)attributes.get(JobName.class); |
| if (jobName != null) { |
| setJobName(jobName.getValue()); |
| } |
| Copies copies = (Copies)attributes.get(Copies.class); |
| if (copies != null) { |
| setCopies(copies.getValue()); |
| } |
| |
| Destination dest = (Destination)attributes.get(Destination.class); |
| |
| if (dest != null) { |
| try { |
| mDestType = RasterPrinterJob.FILE; |
| mDestination = (new File(dest.getURI())).getPath(); |
| } catch (Exception e) { |
| mDestination = "out.prn"; |
| PrintService ps = getPrintService(); |
| if (ps != null) { |
| Destination defaultDest = (Destination)ps. |
| getDefaultAttributeValue(Destination.class); |
| if (defaultDest != null) { |
| mDestination = (new File(defaultDest.getURI())).getPath(); |
| } |
| } |
| } |
| } else { |
| mDestType = RasterPrinterJob.PRINTER; |
| PrintService ps = getPrintService(); |
| if (ps != null) { |
| mDestination = ps.getName(); |
| } |
| } |
| } |
| |
| return doPrint; |
| } |
| |
| /** |
| * The pages in the document to be printed by this PrinterJob |
| * are drawn by the Printable object 'painter'. The PageFormat |
| * for each page is the default page format. |
| * @param painter Called to render each page of the document. |
| */ |
| public void setPrintable(Printable painter) { |
| setPageable(new OpenBook(defaultPage(new PageFormat()), painter)); |
| } |
| |
| /** |
| * The pages in the document to be printed by this PrinterJob |
| * are drawn by the Printable object 'painter'. The PageFormat |
| * of each page is 'format'. |
| * @param painter Called to render each page of the document. |
| * @param format The size and orientation of each page to |
| * be printed. |
| */ |
| public void setPrintable(Printable painter, PageFormat format) { |
| setPageable(new OpenBook(format, painter)); |
| updatePageAttributes(getPrintService(), format); |
| } |
| |
| /** |
| * The pages in the document to be printed are held by the |
| * Pageable instance 'document'. 'document' will be queried |
| * for the number of pages as well as the PageFormat and |
| * Printable for each page. |
| * @param document The document to be printed. It may not be null. |
| * @exception NullPointerException the Pageable passed in was null. |
| * @see PageFormat |
| * @see Printable |
| */ |
| public void setPageable(Pageable document) throws NullPointerException { |
| if (document != null) { |
| mDocument = document; |
| |
| } else { |
| throw new NullPointerException(); |
| } |
| } |
| |
| protected void initPrinter() { |
| return; |
| } |
| |
| protected boolean isSupportedValue(Attribute attrval, |
| PrintRequestAttributeSet attrset) { |
| PrintService ps = getPrintService(); |
| return |
| (attrval != null && ps != null && |
| ps.isAttributeValueSupported(attrval, |
| DocFlavor.SERVICE_FORMATTED.PAGEABLE, |
| attrset)); |
| } |
| |
| /** |
| * Set the device resolution. |
| * Overridden and used only by the postscript code. |
| * Windows code pulls the information from the attribute set itself. |
| */ |
| protected void setXYRes(double x, double y) { |
| } |
| |
| /* subclasses may need to pull extra information out of the attribute set |
| * They can override this method & call super.setAttributes() |
| */ |
| protected void setAttributes(PrintRequestAttributeSet attributes) |
| throws PrinterException { |
| /* reset all values to defaults */ |
| setCollated(false); |
| sidesAttr = null; |
| printerResAttr = null; |
| pageRangesAttr = null; |
| copiesAttr = 0; |
| jobNameAttr = null; |
| userNameAttr = null; |
| destinationAttr = null; |
| collateAttReq = false; |
| |
| PrintService service = getPrintService(); |
| if (attributes == null || service == null) { |
| return; |
| } |
| |
| boolean fidelity = false; |
| Fidelity attrFidelity = (Fidelity)attributes.get(Fidelity.class); |
| if (attrFidelity != null && attrFidelity == Fidelity.FIDELITY_TRUE) { |
| fidelity = true; |
| } |
| |
| if (fidelity == true) { |
| AttributeSet unsupported = |
| service.getUnsupportedAttributes( |
| DocFlavor.SERVICE_FORMATTED.PAGEABLE, |
| attributes); |
| if (unsupported != null) { |
| throw new PrinterException("Fidelity cannot be satisfied"); |
| } |
| } |
| |
| /* |
| * Since we have verified supported values if fidelity is true, |
| * we can either ignore unsupported values, or substitute a |
| * reasonable alternative |
| */ |
| |
| SheetCollate collateAttr = |
| (SheetCollate)attributes.get(SheetCollate.class); |
| if (isSupportedValue(collateAttr, attributes)) { |
| setCollated(collateAttr == SheetCollate.COLLATED); |
| } |
| |
| sidesAttr = (Sides)attributes.get(Sides.class); |
| if (!isSupportedValue(sidesAttr, attributes)) { |
| sidesAttr = Sides.ONE_SIDED; |
| } |
| |
| printerResAttr = (PrinterResolution)attributes.get(PrinterResolution.class); |
| if (service.isAttributeCategorySupported(PrinterResolution.class)) { |
| if (!isSupportedValue(printerResAttr, attributes)) { |
| printerResAttr = (PrinterResolution) |
| service.getDefaultAttributeValue(PrinterResolution.class); |
| } |
| double xr = |
| printerResAttr.getCrossFeedResolution(ResolutionSyntax.DPI); |
| double yr = printerResAttr.getFeedResolution(ResolutionSyntax.DPI); |
| setXYRes(xr, yr); |
| } |
| |
| pageRangesAttr = (PageRanges)attributes.get(PageRanges.class); |
| if (!isSupportedValue(pageRangesAttr, attributes)) { |
| pageRangesAttr = null; |
| setPageRange(-1, -1); |
| } else { |
| if ((SunPageSelection)attributes.get(SunPageSelection.class) |
| == SunPageSelection.RANGE) { |
| // get to, from, min, max page ranges |
| int[][] range = pageRangesAttr.getMembers(); |
| // setPageRanges uses 0-based indexing so we subtract 1 |
| setPageRange(range[0][0] - 1, range[0][1] - 1); |
| } else { |
| setPageRange(-1, - 1); |
| } |
| } |
| |
| Copies copies = (Copies)attributes.get(Copies.class); |
| if (isSupportedValue(copies, attributes) || |
| (!fidelity && copies != null)) { |
| copiesAttr = copies.getValue(); |
| setCopies(copiesAttr); |
| } else { |
| copiesAttr = getCopies(); |
| } |
| |
| Destination destination = |
| (Destination)attributes.get(Destination.class); |
| |
| if (isSupportedValue(destination, attributes)) { |
| try { |
| // Old code (new File(destination.getURI())).getPath() |
| // would generate a "URI is not hierarchical" IAE |
| // for "file:out.prn" so we use getSchemeSpecificPart instead |
| destinationAttr = "" + new File(destination.getURI(). |
| getSchemeSpecificPart()); |
| } catch (Exception e) { // paranoid exception |
| Destination defaultDest = (Destination)service. |
| getDefaultAttributeValue(Destination.class); |
| if (defaultDest != null) { |
| destinationAttr = "" + new File(defaultDest.getURI(). |
| getSchemeSpecificPart()); |
| } |
| } |
| } |
| |
| JobSheets jobSheets = (JobSheets)attributes.get(JobSheets.class); |
| if (jobSheets != null) { |
| noJobSheet = jobSheets == JobSheets.NONE; |
| } |
| |
| JobName jobName = (JobName)attributes.get(JobName.class); |
| if (isSupportedValue(jobName, attributes) || |
| (!fidelity && jobName != null)) { |
| jobNameAttr = jobName.getValue(); |
| setJobName(jobNameAttr); |
| } else { |
| jobNameAttr = getJobName(); |
| } |
| |
| RequestingUserName userName = |
| (RequestingUserName)attributes.get(RequestingUserName.class); |
| if (isSupportedValue(userName, attributes) || |
| (!fidelity && userName != null)) { |
| userNameAttr = userName.getValue(); |
| } else { |
| try { |
| userNameAttr = getUserName(); |
| } catch (SecurityException e) { |
| userNameAttr = ""; |
| } |
| } |
| |
| /* OpenBook is used internally only when app uses Printable. |
| * This is the case when we use the values from the attribute set. |
| */ |
| Media media = (Media)attributes.get(Media.class); |
| OrientationRequested orientReq = |
| (OrientationRequested)attributes.get(OrientationRequested.class); |
| MediaPrintableArea mpa = |
| (MediaPrintableArea)attributes.get(MediaPrintableArea.class); |
| |
| if ((orientReq != null || media != null || mpa != null) && |
| getPageable() instanceof OpenBook) { |
| |
| /* We could almost(!) use PrinterJob.getPageFormat() except |
| * here we need to start with the PageFormat from the OpenBook : |
| */ |
| Pageable pageable = getPageable(); |
| Printable printable = pageable.getPrintable(0); |
| PageFormat pf = (PageFormat)pageable.getPageFormat(0).clone(); |
| Paper paper = pf.getPaper(); |
| |
| /* If there's a media but no media printable area, we can try |
| * to retrieve the default value for mpa and use that. |
| */ |
| if (mpa == null && media != null && |
| service. |
| isAttributeCategorySupported(MediaPrintableArea.class)) { |
| Object mpaVals = service. |
| getSupportedAttributeValues(MediaPrintableArea.class, |
| null, attributes); |
| if (mpaVals instanceof MediaPrintableArea[] && |
| ((MediaPrintableArea[])mpaVals).length > 0) { |
| mpa = ((MediaPrintableArea[])mpaVals)[0]; |
| } |
| } |
| |
| if (isSupportedValue(orientReq, attributes) || |
| (!fidelity && orientReq != null)) { |
| int orient; |
| if (orientReq.equals(OrientationRequested.REVERSE_LANDSCAPE)) { |
| orient = PageFormat.REVERSE_LANDSCAPE; |
| } else if (orientReq.equals(OrientationRequested.LANDSCAPE)) { |
| orient = PageFormat.LANDSCAPE; |
| } else { |
| orient = PageFormat.PORTRAIT; |
| } |
| pf.setOrientation(orient); |
| } |
| |
| if (isSupportedValue(media, attributes) || |
| (!fidelity && media != null)) { |
| if (media instanceof MediaSizeName) { |
| MediaSizeName msn = (MediaSizeName)media; |
| MediaSize msz = MediaSize.getMediaSizeForName(msn); |
| if (msz != null) { |
| float paperWid = msz.getX(MediaSize.INCH) * 72.0f; |
| float paperHgt = msz.getY(MediaSize.INCH) * 72.0f; |
| paper.setSize(paperWid, paperHgt); |
| if (mpa == null) { |
| paper.setImageableArea(72.0, 72.0, |
| paperWid-144.0, |
| paperHgt-144.0); |
| } |
| } |
| } |
| } |
| |
| if (isSupportedValue(mpa, attributes) || |
| (!fidelity && mpa != null)) { |
| float [] printableArea = |
| mpa.getPrintableArea(MediaPrintableArea.INCH); |
| for (int i=0; i < printableArea.length; i++) { |
| printableArea[i] = printableArea[i]*72.0f; |
| } |
| paper.setImageableArea(printableArea[0], printableArea[1], |
| printableArea[2], printableArea[3]); |
| } |
| |
| pf.setPaper(paper); |
| pf = validatePage(pf); |
| setPrintable(printable, pf); |
| } else { |
| // for AWT where pageable is not an instance of OpenBook, |
| // we need to save paper info |
| this.attributes = attributes; |
| } |
| |
| } |
| |
| /* |
| * Services we don't recognize as built-in services can't be |
| * implemented as subclasses of PrinterJob, therefore we create |
| * a DocPrintJob from their service and pass a Doc representing |
| * the application's printjob |
| */ |
| // MacOSX - made protected so subclasses can reference it. |
| protected void spoolToService(PrintService psvc, |
| PrintRequestAttributeSet attributes) |
| throws PrinterException { |
| |
| if (psvc == null) { |
| throw new PrinterException("No print service found."); |
| } |
| |
| DocPrintJob job = psvc.createPrintJob(); |
| Doc doc = new PageableDoc(getPageable()); |
| if (attributes == null) { |
| attributes = new HashPrintRequestAttributeSet(); |
| attributes.add(new Copies(getCopies())); |
| attributes.add(new JobName(getJobName(), null)); |
| } |
| try { |
| job.print(doc, attributes); |
| } catch (PrintException e) { |
| throw new PrinterException(e.toString()); |
| } |
| } |
| |
| /** |
| * Prints a set of pages. |
| * @exception java.awt.print.PrinterException an error in the print system |
| * caused the job to be aborted |
| * @see java.awt.print.Book |
| * @see java.awt.print.Pageable |
| * @see java.awt.print.Printable |
| */ |
| public void print() throws PrinterException { |
| print(attributes); |
| } |
| |
| public static boolean debugPrint = false; |
| protected void debug_println(String str) { |
| if (debugPrint) { |
| System.out.println("RasterPrinterJob "+str+" "+this); |
| } |
| } |
| |
| public void print(PrintRequestAttributeSet attributes) |
| throws PrinterException { |
| |
| /* |
| * In the future PrinterJob will probably always dispatch |
| * the print job to the PrintService. |
| * This is how third party 2D Print Services will be invoked |
| * when applications use the PrinterJob API. |
| * However the JRE's concrete PrinterJob implementations have |
| * not yet been re-worked to be implemented as standalone |
| * services, and are implemented only as subclasses of PrinterJob. |
| * So here we dispatch only those services we do not recognize |
| * as implemented through platform subclasses of PrinterJob |
| * (and this class). |
| */ |
| PrintService psvc = getPrintService(); |
| debug_println("psvc = "+psvc); |
| if (psvc == null) { |
| throw new PrinterException("No print service found."); |
| } |
| |
| // Check the list of services. This service may have been |
| // deleted already |
| PrinterState prnState = psvc.getAttribute(PrinterState.class); |
| if (prnState == PrinterState.STOPPED) { |
| PrinterStateReasons prnStateReasons = |
| psvc.getAttribute(PrinterStateReasons.class); |
| if ((prnStateReasons != null) && |
| (prnStateReasons.containsKey(PrinterStateReason.SHUTDOWN))) |
| { |
| throw new PrinterException("PrintService is no longer available."); |
| } |
| } |
| |
| if ((psvc.getAttribute(PrinterIsAcceptingJobs.class)) == |
| PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS) { |
| throw new PrinterException("Printer is not accepting job."); |
| } |
| |
| /* |
| * Check the default job-sheet value on underlying platform. If IPP |
| * reports job-sheets=none, then honour that and modify noJobSheet since |
| * by default, noJobSheet is false which mean jdk will print banner page. |
| * This is because if "attributes" is null (if user directly calls print() |
| * without specifying any attributes and without showing printdialog) then |
| * setAttribute will return without changing noJobSheet value. |
| * Also, we do this before setAttributes() call so as to allow the user |
| * to override this via explicitly adding JobSheets attributes to |
| * PrintRequestAttributeSet while calling print(attributes) |
| */ |
| JobSheets js = (JobSheets)psvc.getDefaultAttributeValue(JobSheets.class); |
| if (js != null && js.equals(JobSheets.NONE)) { |
| noJobSheet = true; |
| } |
| |
| if ((psvc instanceof SunPrinterJobService) && |
| ((SunPrinterJobService)psvc).usesClass(getClass())) { |
| setAttributes(attributes); |
| // throw exception for invalid destination |
| if (destinationAttr != null) { |
| validateDestination(destinationAttr); |
| } |
| } else { |
| spoolToService(psvc, attributes); |
| return; |
| } |
| /* We need to make sure that the collation and copies |
| * settings are initialised */ |
| initPrinter(); |
| |
| int numCollatedCopies = getCollatedCopies(); |
| int numNonCollatedCopies = getNoncollatedCopies(); |
| debug_println("getCollatedCopies() "+numCollatedCopies |
| + " getNoncollatedCopies() "+ numNonCollatedCopies); |
| |
| /* Get the range of pages we are to print. If the |
| * last page to print is unknown, then we print to |
| * the end of the document. Note that firstPage |
| * and lastPage are 0 based page indices. |
| */ |
| int numPages = mDocument.getNumberOfPages(); |
| if (numPages == 0) { |
| return; |
| } |
| |
| int firstPage = getFirstPage(); |
| int lastPage = getLastPage(); |
| if(lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES){ |
| int totalPages = mDocument.getNumberOfPages(); |
| if (totalPages != Pageable.UNKNOWN_NUMBER_OF_PAGES) { |
| lastPage = mDocument.getNumberOfPages() - 1; |
| } |
| } |
| |
| try { |
| synchronized (this) { |
| performingPrinting = true; |
| userCancelled = false; |
| } |
| |
| startDoc(); |
| if (isCancelled()) { |
| cancelDoc(); |
| } |
| |
| // PageRanges can be set even if RANGE is not selected |
| // so we need to check if it is selected. |
| boolean rangeIsSelected = true; |
| if (attributes != null) { |
| SunPageSelection pages = |
| (SunPageSelection)attributes.get(SunPageSelection.class); |
| if ((pages != null) && (pages != SunPageSelection.RANGE)) { |
| rangeIsSelected = false; |
| } |
| } |
| |
| |
| debug_println("after startDoc rangeSelected? "+rangeIsSelected |
| + " numNonCollatedCopies "+ numNonCollatedCopies); |
| |
| |
| /* Three nested loops iterate over the document. The outer loop |
| * counts the number of collated copies while the inner loop |
| * counts the number of nonCollated copies. Normally, one of |
| * these two loops will only execute once; that is we will |
| * either print collated copies or noncollated copies. The |
| * middle loop iterates over the pages. |
| * If a PageRanges attribute is used, it constrains the pages |
| * that are imaged. If a platform subclass (though a user dialog) |
| * requests a page range via setPageRange(). it too can |
| * constrain the page ranges that are imaged. |
| * It is expected that only one of these will be used in a |
| * job but both should be able to co-exist. |
| */ |
| for(int collated = 0; collated < numCollatedCopies; collated++) { |
| for(int i = firstPage, pageResult = Printable.PAGE_EXISTS; |
| (i <= lastPage || |
| lastPage == Pageable.UNKNOWN_NUMBER_OF_PAGES) |
| && pageResult == Printable.PAGE_EXISTS; |
| i++) |
| { |
| |
| if ((pageRangesAttr != null) && rangeIsSelected ){ |
| int nexti = pageRangesAttr.next(i); |
| if (nexti == -1) { |
| break; |
| } else if (nexti != i+1) { |
| continue; |
| } |
| } |
| |
| for(int nonCollated = 0; |
| nonCollated < numNonCollatedCopies |
| && pageResult == Printable.PAGE_EXISTS; |
| nonCollated++) |
| { |
| if (isCancelled()) { |
| cancelDoc(); |
| } |
| debug_println("printPage "+i); |
| pageResult = printPage(mDocument, i); |
| |
| } |
| } |
| } |
| |
| if (isCancelled()) { |
| cancelDoc(); |
| } |
| |
| } finally { |
| // reset previousPaper in case this job is invoked again. |
| previousPaper = null; |
| synchronized (this) { |
| if (performingPrinting) { |
| endDoc(); |
| } |
| performingPrinting = false; |
| notify(); |
| } |
| } |
| } |
| |
| protected void validateDestination(String dest) throws PrinterException { |
| if (dest == null) { |
| return; |
| } |
| // dest is null for Destination(new URI("")) |
| // because isAttributeValueSupported returns false in setAttributes |
| |
| // Destination(new URI(" ")) throws URISyntaxException |
| File f = new File(dest); |
| try { |
| // check if this is a new file and if filename chars are valid |
| if (f.createNewFile()) { |
| f.delete(); |
| } |
| } catch (IOException ioe) { |
| throw new PrinterException("Cannot write to file:"+ |
| dest); |
| } catch (SecurityException se) { |
| //There is already file read/write access so at this point |
| // only delete access is denied. Just ignore it because in |
| // most cases the file created in createNewFile gets overwritten |
| // anyway. |
| } |
| |
| File pFile = f.getParentFile(); |
| if ((f.exists() && |
| (!f.isFile() || !f.canWrite())) || |
| ((pFile != null) && |
| (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) { |
| if (f.exists()) { |
| f.delete(); |
| } |
| throw new PrinterException("Cannot write to file:"+ |
| dest); |
| } |
| } |
| |
| /** |
| * updates a Paper object to reflect the current printer's selected |
| * paper size and imageable area for that paper size. |
| * Default implementation copies settings from the original, applies |
| * applies some validity checks, changes them only if they are |
| * clearly unreasonable, then sets them into the new Paper. |
| * Subclasses are expected to override this method to make more |
| * informed decisons. |
| */ |
| protected void validatePaper(Paper origPaper, Paper newPaper) { |
| if (origPaper == null || newPaper == null) { |
| return; |
| } else { |
| double wid = origPaper.getWidth(); |
| double hgt = origPaper.getHeight(); |
| double ix = origPaper.getImageableX(); |
| double iy = origPaper.getImageableY(); |
| double iw = origPaper.getImageableWidth(); |
| double ih = origPaper.getImageableHeight(); |
| |
| /* Assume any +ve values are legal. Overall paper dimensions |
| * take precedence. Make sure imageable area fits on the paper. |
| */ |
| Paper defaultPaper = new Paper(); |
| wid = ((wid > 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. |
| * Platform subclasses which can access the actual default paper size |
| * for a printer may override this method. |
| */ |
| public PageFormat defaultPage(PageFormat page) { |
| PageFormat newPage = (PageFormat)page.clone(); |
| newPage.setOrientation(PageFormat.PORTRAIT); |
| Paper newPaper = new Paper(); |
| double ptsPerInch = 72.0; |
| double w, h; |
| Media media = null; |
| |
| PrintService service = getPrintService(); |
| if (service != null) { |
| MediaSize size; |
| media = |
| (Media)service.getDefaultAttributeValue(Media.class); |
| |
| if (media instanceof MediaSizeName && |
| ((size = MediaSize.getMediaSizeForName((MediaSizeName)media)) != |
| null)) { |
| w = size.getX(MediaSize.INCH) * ptsPerInch; |
| h = size.getY(MediaSize.INCH) * ptsPerInch; |
| newPaper.setSize(w, h); |
| newPaper.setImageableArea(ptsPerInch, ptsPerInch, |
| w - 2.0*ptsPerInch, |
| h - 2.0*ptsPerInch); |
| newPage.setPaper(newPaper); |
| return newPage; |
| |
| } |
| } |
| |
| /* Default to A4 paper outside North America. |
| */ |
| String defaultCountry = Locale.getDefault().getCountry(); |
| if (!Locale.getDefault().equals(Locale.ENGLISH) && // ie "C" |
| defaultCountry != null && |
| !defaultCountry.equals(Locale.US.getCountry()) && |
| !defaultCountry.equals(Locale.CANADA.getCountry())) { |
| |
| double mmPerInch = 25.4; |
| w = Math.rint((210.0*ptsPerInch)/mmPerInch); |
| h = Math.rint((297.0*ptsPerInch)/mmPerInch); |
| newPaper.setSize(w, h); |
| newPaper.setImageableArea(ptsPerInch, ptsPerInch, |
| w - 2.0*ptsPerInch, |
| h - 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; |
| } |
| |
| protected final int getSelectAttrib() { |
| if (attributes != null) { |
| SunPageSelection pages = |
| (SunPageSelection)attributes.get(SunPageSelection.class); |
| if (pages == SunPageSelection.RANGE) { |
| return PD_PAGENUMS; |
| } else if (pages == SunPageSelection.SELECTION) { |
| return PD_SELECTION; |
| } else if (pages == SunPageSelection.ALL) { |
| return PD_ALLPAGES; |
| } |
| } |
| return PD_NOSELECTION; |
| } |
| |
| //returns 1-based index for "From" page |
| protected final int getFromPageAttrib() { |
| if (attributes != null) { |
| PageRanges pageRangesAttr = |
| (PageRanges)attributes.get(PageRanges.class); |
| if (pageRangesAttr != null) { |
| int[][] range = pageRangesAttr.getMembers(); |
| return range[0][0]; |
| } |
| } |
| return getMinPageAttrib(); |
| } |
| |
| //returns 1-based index for "To" page |
| protected final int getToPageAttrib() { |
| if (attributes != null) { |
| PageRanges pageRangesAttr = |
| (PageRanges)attributes.get(PageRanges.class); |
| if (pageRangesAttr != null) { |
| int[][] range = pageRangesAttr.getMembers(); |
| return range[range.length-1][1]; |
| } |
| } |
| return getMaxPageAttrib(); |
| } |
| |
| protected final int getMinPageAttrib() { |
| if (attributes != null) { |
| SunMinMaxPage s = |
| (SunMinMaxPage)attributes.get(SunMinMaxPage.class); |
| if (s != null) { |
| return s.getMin(); |
| } |
| } |
| return 1; |
| } |
| |
| protected final int getMaxPageAttrib() { |
| if (attributes != null) { |
| SunMinMaxPage s = |
| (SunMinMaxPage)attributes.get(SunMinMaxPage.class); |
| if (s != null) { |
| return s.getMax(); |
| } |
| } |
| |
| Pageable pageable = getPageable(); |
| if (pageable != null) { |
| int numPages = pageable.getNumberOfPages(); |
| if (numPages <= Pageable.UNKNOWN_NUMBER_OF_PAGES) { |
| numPages = MAX_UNKNOWN_PAGES; |
| } |
| return ((numPages == 0) ? 1 : numPages); |
| } |
| |
| return Integer.MAX_VALUE; |
| } |
| /** |
| * 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(); |
| |
| // MacOSX - made protected so subclasses can reference it. |
| protected 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(); |
| } |
| |
| |
| /* The printer graphics config is cached on the job, so that it can |
| * be created once, and updated only as needed (for now only to change |
| * the bounds if when using a Pageable the page sizes changes). |
| */ |
| |
| private int deviceWidth, deviceHeight; |
| private AffineTransform defaultDeviceTransform; |
| private PrinterGraphicsConfig pgConfig; |
| |
| synchronized void setGraphicsConfigInfo(AffineTransform at, |
| double pw, double ph) { |
| Point2D.Double pt = new Point2D.Double(pw, ph); |
| at.transform(pt, pt); |
| |
| if (pgConfig == null || |
| defaultDeviceTransform == null || |
| !at.equals(defaultDeviceTransform) || |
| deviceWidth != (int)pt.getX() || |
| deviceHeight != (int)pt.getY()) { |
| |
| deviceWidth = (int)pt.getX(); |
| deviceHeight = (int)pt.getY(); |
| defaultDeviceTransform = at; |
| pgConfig = null; |
| } |
| } |
| |
| synchronized PrinterGraphicsConfig getPrinterGraphicsConfig() { |
| if (pgConfig != null) { |
| return pgConfig; |
| } |
| String deviceID = "Printer Device"; |
| PrintService service = getPrintService(); |
| if (service != null) { |
| deviceID = service.toString(); |
| } |
| pgConfig = new PrinterGraphicsConfig(deviceID, |
| defaultDeviceTransform, |
| deviceWidth, deviceHeight); |
| return pgConfig; |
| } |
| |
| /** |
| * 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; |
| PageFormat origPage; |
| Printable painter; |
| try { |
| origPage = document.getPageFormat(pageIndex); |
| page = (PageFormat)origPage.clone(); |
| painter = document.getPrintable(pageIndex); |
| } catch (Exception e) { |
| PrinterException pe = |
| new PrinterException("Error getting page or printable.[ " + |
| e +" ]"); |
| pe.initCause(e); |
| throw pe; |
| } |
| |
| /* Get the imageable area from Paper instead of PageFormat |
| * because we do not want it adjusted by the page orientation. |
| */ |
| Paper paper = page.getPaper(); |
| // if non-portrait and 270 degree landscape rotation |
| if (page.getOrientation() != PageFormat.PORTRAIT && |
| landscapeRotates270) { |
| |
| double left = paper.getImageableX(); |
| double top = paper.getImageableY(); |
| double width = paper.getImageableWidth(); |
| double height = paper.getImageableHeight(); |
| paper.setImageableArea(paper.getWidth()-left-width, |
| paper.getHeight()-top-height, |
| width, height); |
| page.setPaper(paper); |
| if (page.getOrientation() == PageFormat.LANDSCAPE) { |
| page.setOrientation(PageFormat.REVERSE_LANDSCAPE); |
| } else { |
| page.setOrientation(PageFormat.LANDSCAPE); |
| } |
| } |
| |
| 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 = (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()); |
| peekGraphics.transform(scaleTransform); |
| peekGraphics.translate(-getPhysicalPrintableX(paper) / xScale, |
| -getPhysicalPrintableY(paper) / yScale); |
| peekGraphics.transform(new AffineTransform(page.getMatrix())); |
| initPrinterGraphics(peekGraphics, pageFormatArea); |
| AffineTransform pgAt = peekGraphics.getTransform(); |
| |
| /* Update the information used to return a GraphicsConfiguration |
| * for this printer device. It needs to be updated per page as |
| * not all pages in a job may be the same size (different bounds) |
| * The transform is the scaling transform as this corresponds to |
| * the default transform for the device. The width and height are |
| * those of the paper, not the page format, as we want to describe |
| * the bounds of the device in its natural coordinate system of |
| * device coordinate whereas a page format may be in a rotated context. |
| */ |
| setGraphicsConfigInfo(scaleTransform, |
| paper.getWidth(), paper.getHeight()); |
| int pageResult = painter.print(peekGraphics, origPage, pageIndex); |
| debug_println("pageResult "+pageResult); |
| if (pageResult == Printable.PAGE_EXISTS) { |
| debug_println("startPage "+pageIndex); |
| |
| /* We need to check if the paper size is changed. |
| * Note that it is not sufficient to ask for the pageformat |
| * of "pageIndex-1", since PageRanges mean that pages can be |
| * skipped. So we have to look at the actual last paper size used. |
| */ |
| Paper thisPaper = page.getPaper(); |
| boolean paperChanged = |
| previousPaper == null || |
| thisPaper.getWidth() != previousPaper.getWidth() || |
| thisPaper.getHeight() != previousPaper.getHeight(); |
| previousPaper = thisPaper; |
| |
| startPage(page, painter, pageIndex, paperChanged); |
| 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(); |
| |
| AffineTransform initialTx = pathGraphics.getTransform(); |
| |
| painter.print(pathGraphics, origPage, pageIndex); |
| |
| for (int i=0;i<redrawList.size();i++) { |
| GraphicsState gstate = redrawList.get(i); |
| pathGraphics.setTransform(initialTx); |
| ((PathGraphics)pathGraphics).redrawRegion( |
| gstate.region, |
| gstate.sx, |
| gstate.sy, |
| gstate.theClip, |
| gstate.theTransform); |
| } |
| |
| /* This is the banded-raster printing loop. |
| * It should be moved into its own method. |
| */ |
| } else { |
| BufferedImage band = cachedBand; |
| if (cachedBand == null || |
| bandWidth != cachedBandWidth || |
| bandHeight != cachedBandHeight) { |
| band = new BufferedImage(bandWidth, bandHeight, |
| BufferedImage.TYPE_3BYTE_BGR); |
| cachedBand = band; |
| cachedBandWidth = bandWidth; |
| cachedBandHeight = bandHeight; |
| } |
| Graphics2D bandGraphics = band.createGraphics(); |
| |
| Rectangle2D.Double clipArea = |
| new Rectangle2D.Double(0, 0, bandWidth, bandHeight); |
| |
| initPrinterGraphics(bandGraphics, clipArea); |
| |
| ProxyGraphics2D painterGraphics = |
| new ProxyGraphics2D(bandGraphics, this); |
| |
| Graphics2D clearGraphics = band.createGraphics(); |
| clearGraphics.setColor(Color.white); |
| |
| /* We need the actual bits of the BufferedImage to send to |
| * the native Window's code. 'data' points to the actual |
| * pixels. Right now these are in ARGB format with 8 bits |
| * per component. We need to use a monochrome BufferedImage |
| * for monochrome printers when this is supported by |
| * BufferedImage. FIX |
| */ |
| ByteInterleavedRaster tile = (ByteInterleavedRaster)band.getRaster(); |
| byte[] data = tile.getDataStorage(); |
| |
| /* Loop over the page moving our band down the page, |
| * calling the app to render the band, and then send the band |
| * to the printer. |
| */ |
| int deviceBottom = deviceTop + deviceAreaHeight; |
| |
| /* device's printable x,y is really addressable origin |
| * we address relative to media origin so when we print a |
| * band we need to adjust for the different methods of |
| * addressing it. |
| */ |
| int deviceAddressableX = (int)getPhysicalPrintableX(paper); |
| int deviceAddressableY = (int)getPhysicalPrintableY(paper); |
| |
| for (int bandTop = 0; bandTop <= deviceAreaHeight; |
| bandTop += bandHeight) |
| { |
| |
| /* Put the band back into device space and |
| * erase the contents of the band. |
| */ |
| clearGraphics.fillRect(0, 0, bandWidth, bandHeight); |
| |
| /* Put the band into the correct location on the |
| * page. Once the band is moved we translate the |
| * device transform so that the band will move down |
| * the page on the next iteration of the loop. |
| */ |
| bandGraphics.setTransform(uniformTransform); |
| bandGraphics.transform(deviceTransform); |
| deviceTransform.translate(0, -bandHeight); |
| |
| /* Switch the band from device space to user, |
| * 72 dpi, space. |
| */ |
| bandGraphics.transform(scaleTransform); |
| bandGraphics.transform(new AffineTransform(page.getMatrix())); |
| |
| Rectangle clip = bandGraphics.getClipBounds(); |
| clip = pgAt.createTransformedShape(clip).getBounds(); |
| |
| if ((clip == null) || peekGraphics.hitsDrawingArea(clip) && |
| (bandWidth > 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, origPage, pageIndex); |
| painterGraphics.dispose(); |
| printBand(data, bandX, bandY, bandWidth, bandHeight); |
| } |
| } |
| |
| clearGraphics.dispose(); |
| bandGraphics.dispose(); |
| |
| } |
| debug_println("calling endPage "+pageIndex); |
| 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 |
| * {@code 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 {@code PathGraphics} to perform |
| * that conversion. If there is not an object |
| * capable of the conversion then return |
| * {@code null}. Returning {@code 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 {@code 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, {@code 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: <as passed in> |
| */ |
| // MacOSX - made protected so subclasses can reference it. |
| protected 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("<<ALL FILES>>", "read,write"); |
| } |
| security.checkPermission(printToFilePermission); |
| } |
| } |
| |
| /* On-screen drawString renders most control chars as the missing glyph |
| * and have the non-zero advance of that glyph. |
| * Exceptions are \t, \n and \r which are considered zero-width. |
| * This is a utility method used by subclasses to remove them so we |
| * don't have to worry about platform or font specific handling of them. |
| */ |
| protected String removeControlChars(String s) { |
| char[] in_chars = s.toCharArray(); |
| int len = in_chars.length; |
| char[] out_chars = new char[len]; |
| int pos = 0; |
| |
| for (int i = 0; i < len; i++) { |
| char c = in_chars[i]; |
| if (c > '\r' || c < '\t' || c == '\u000b' || c == '\u000c') { |
| out_chars[pos++] = c; |
| } |
| } |
| if (pos == len) { |
| return s; // no need to make a new String. |
| } else { |
| return new String(out_chars, 0, pos); |
| } |
| } |
| } |