/* * Copyright (c) 1997, 2015, 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.awt.windows; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.GraphicsEnvironment; import java.awt.HeadlessException; import java.awt.Toolkit; import java.awt.BasicStroke; import java.awt.Button; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.FileDialog; import java.awt.Dialog; import java.awt.Label; import java.awt.Panel; import java.awt.Rectangle; import java.awt.Window; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import java.awt.print.Pageable; import java.awt.print.PageFormat; import java.awt.print.Paper; import java.awt.print.Printable; import java.awt.print.PrinterJob; import java.awt.print.PrinterException; import javax.print.PrintService; import java.io.File; import java.util.MissingResourceException; import java.util.ResourceBundle; import sun.awt.AWTAccessor; import sun.awt.AWTAccessor.ComponentAccessor; import sun.print.PeekGraphics; import sun.print.PeekMetrics; import java.net.URI; import java.net.URISyntaxException; import javax.print.PrintServiceLookup; import javax.print.attribute.PrintRequestAttributeSet; import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.Attribute; import javax.print.attribute.standard.Sides; import javax.print.attribute.standard.Chromaticity; import javax.print.attribute.standard.PrintQuality; import javax.print.attribute.standard.PrinterResolution; import javax.print.attribute.standard.SheetCollate; import javax.print.attribute.standard.Copies; import javax.print.attribute.standard.Destination; import javax.print.attribute.standard.OrientationRequested; import javax.print.attribute.standard.Media; import javax.print.attribute.standard.MediaSizeName; import javax.print.attribute.standard.MediaSize; import javax.print.attribute.standard.MediaTray; import javax.print.attribute.standard.PageRanges; import sun.awt.Win32FontManager; import sun.print.RasterPrinterJob; import sun.print.SunAlternateMedia; import sun.print.SunPageSelection; import sun.print.Win32MediaTray; import sun.print.Win32PrintService; import sun.print.PrintServiceLookupProvider; import sun.print.ServiceDialog; import sun.print.DialogOwner; import java.awt.Frame; import java.io.FilePermission; import sun.java2d.Disposer; import sun.java2d.DisposerRecord; import sun.java2d.DisposerTarget; /** * A class which initiates and executes a Win32 printer job. * * @author Richard Blanchard */ public final class WPrinterJob extends RasterPrinterJob implements DisposerTarget { /* Class Constants */ /* Instance Variables */ /** * These are Windows' ExtCreatePen End Cap Styles * and must match the values in */ protected static final long PS_ENDCAP_ROUND = 0x00000000; protected static final long PS_ENDCAP_SQUARE = 0x00000100; protected static final long PS_ENDCAP_FLAT = 0x00000200; /** * These are Windows' ExtCreatePen Line Join Styles * and must match the values in */ protected static final long PS_JOIN_ROUND = 0x00000000; protected static final long PS_JOIN_BEVEL = 0x00001000; protected static final long PS_JOIN_MITER = 0x00002000; /** * This is the Window's Polygon fill rule which * Selects alternate mode (fills the area between odd-numbered * and even-numbered polygon sides on each scan line). * It must match the value in It can be passed * to setPolyFillMode(). */ protected static final int POLYFILL_ALTERNATE = 1; /** * This is the Window's Polygon fill rule which * Selects winding mode which fills any region * with a nonzero winding value). It must match * the value in It can be passed * to setPolyFillMode(). */ protected static final int POLYFILL_WINDING = 2; /** * The maximum value for a Window's color component * as passed to selectSolidBrush. */ private static final int MAX_WCOLOR = 255; /** * Flags for setting values from devmode in native code. * Values must match those defined in awt_PrintControl.cpp */ private static final int SET_DUP_VERTICAL = 0x00000010; private static final int SET_DUP_HORIZONTAL = 0x00000020; private static final int SET_RES_HIGH = 0x00000040; private static final int SET_RES_LOW = 0x00000080; private static final int SET_COLOR = 0x00000200; private static final int SET_ORIENTATION = 0x00004000; private static final int SET_COLLATED = 0x00008000; /** * Values must match those defined in wingdi.h & commdlg.h */ private static final int PD_COLLATE = 0x00000010; private static final int PD_PRINTTOFILE = 0x00000020; private static final int DM_ORIENTATION = 0x00000001; private static final int DM_PAPERSIZE = 0x00000002; private static final int DM_COPIES = 0x00000100; private static final int DM_DEFAULTSOURCE = 0x00000200; private static final int DM_PRINTQUALITY = 0x00000400; private static final int DM_COLOR = 0x00000800; private static final int DM_DUPLEX = 0x00001000; private static final int DM_YRESOLUTION = 0x00002000; private static final int DM_COLLATE = 0x00008000; private static final short DMCOLLATE_FALSE = 0; private static final short DMCOLLATE_TRUE = 1; private static final short DMORIENT_PORTRAIT = 1; private static final short DMORIENT_LANDSCAPE = 2; private static final short DMCOLOR_MONOCHROME = 1; private static final short DMCOLOR_COLOR = 2; private static final short DMRES_DRAFT = -1; private static final short DMRES_LOW = -2; private static final short DMRES_MEDIUM = -3; private static final short DMRES_HIGH = -4; private static final short DMDUP_SIMPLEX = 1; private static final short DMDUP_VERTICAL = 2; private static final short DMDUP_HORIZONTAL = 3; /** * Pageable MAX pages */ private static final int MAX_UNKNOWN_PAGES = 9999; /* Collation and copy flags. * The Windows PRINTDLG struct has a nCopies field which on return * indicates how many copies of a print job an application must render. * There is also a PD_COLLATE member of the flags field which if * set on return indicates the application generated copies should be * collated. * Windows printer drivers typically - but not always - support * generating multiple copies themselves, but uncollated is more * universal than collated copies. * When they do, they read the initial values from the PRINTDLG structure * and set them into the driver's DEVMODE structure and intialise * the printer DC based on that, so that when printed those settings * will be used. * For drivers supporting both these capabilities via DEVMODE, then on * return from the Print Dialog, nCopies is set to 1 and the PD_COLLATE is * cleared, so that the application will only render 1 copy and the * driver takes care of the rest. * * Applications which want to know what's going on have to be DEVMODE * savvy and peek at that. * DM_COPIES flag indicates support for multiple driver copies * and dmCopies is the number of copies the driver will print * DM_COLLATE flag indicates support for collated driver copies and * dmCollate == DMCOLLATE_TRUE indicates the option is in effect. * * Multiple copies from Java applications: * We provide API to get & set the number of copies as well as allowing the * user to choose it, so we need to be savvy about DEVMODE, so that * we can accurately report back the number of copies selected by * the user, as well as make use of the driver to render multiple copies. * * Collation and Java applications: * We presently provide no API for specifying collation, but its * present on the Windows Print Dialog, and when a user checks it * they expect it to be obeyed. * The best thing to do is to detect exactly the cases where the * driver doesn't support this and render multiple copies ourselves. * To support all this we need several flags which signal the * printer's capabilities and the user's requests. * Its questionable if we (yet) need to make a distinction between * the user requesting collation and the driver supporting it. * Since for now we only need to know whether we need to render the * copies. However it allows the logic to be clearer. * These fields are changed by native code which detects the driver's * capabilities and the user's choices. */ //initialize to false because the Flags that we initialized in PRINTDLG // tells GDI that we can handle our own collation and multiple copies private boolean driverDoesMultipleCopies = false; private boolean driverDoesCollation = false; private boolean userRequestedCollation = false; private boolean noDefaultPrinter = false; /* The HandleRecord holds the native resources that need to be freed * when this WPrinterJob is GC'd. */ static class HandleRecord implements DisposerRecord { /** * The Windows device context we will print into. * This variable is set after the Print dialog * is okayed by the user. If the user cancels * the print dialog, then this variable is 0. * Much of the configuration information for a printer is * obtained through printer device specific handles. * We need to associate these with, and free with, the mPrintDC. */ private long mPrintDC; private long mPrintHDevMode; private long mPrintHDevNames; @Override public void dispose() { WPrinterJob.deleteDC(mPrintDC, mPrintHDevMode, mPrintHDevNames); } } private HandleRecord handleRecord = new HandleRecord(); private int mPrintPaperSize; /* These fields are directly set in upcalls from the values * obtained from calling DeviceCapabilities() */ private int mPrintXRes; // pixels per inch in x direction private int mPrintYRes; // pixels per inch in y direction private int mPrintPhysX; // x offset in pixels of printable area private int mPrintPhysY; // y offset in pixels of printable area private int mPrintWidth; // width in pixels of printable area private int mPrintHeight; // height in pixels of printable area private int mPageWidth; // width in pixels of entire page private int mPageHeight; // height in pixels of entire page /* The values of the following variables are pulled directly * into native code (even bypassing getter methods) when starting a doc. * So these need to be synced up from any resulting native changes * by a user dialog. * But the native changes call up to into the attributeset, and that * should be sufficient, since before heading down to native either * to (re-)display a dialog, or to print the doc, these are all * re-populated from the AttributeSet, * Nonetheless having them in sync with the attributeset and native * state is probably safer. * Also whereas the startDoc native code pulls the variables directly, * the dialog code does use getter to pull some of these values. * That's an inconsistency we should fix if it causes problems. */ private int mAttSides; private int mAttChromaticity; private int mAttXRes; private int mAttYRes; private int mAttQuality; private int mAttCollate; private int mAttCopies; private int mAttMediaSizeName; private int mAttMediaTray; private String mDestination = null; /** * The last color set into the print device context or * {@code null} if no color has been set. */ private Color mLastColor; /** * The last text color set into the print device context or * {@code null} if no color has been set. */ private Color mLastTextColor; /** * Define the most recent java font set as a GDI font in the printer * device context. mLastFontFamily will be NULL if no * GDI font has been set. */ private String mLastFontFamily; private float mLastFontSize; private int mLastFontStyle; private int mLastRotation; private float mLastAwScale; // for AwtPrintControl::InitPrintDialog private PrinterJob pjob; private java.awt.peer.ComponentPeer dialogOwnerPeer = null; /* Static Initializations */ static { // AWT has to be initialized for the native code to function correctly. Toolkit.getDefaultToolkit(); initIDs(); Win32FontManager.registerJREFontsForPrinting(); } /* Constructors */ public WPrinterJob() { Disposer.addRecord(disposerReferent, handleRecord = new HandleRecord()); initAttributeMembers(); } /* Implement DisposerTarget. Weak references to an Object can delay * its storage reclaimation marginally. * It won't make the native resources be release any more quickly, but * by pointing the reference held by Disposer at an object which becomes * no longer strongly reachable when this WPrinterJob is no longer * strongly reachable, we allow the WPrinterJob to be freed more promptly * than if it were the referenced object. */ private Object disposerReferent = new Object(); @Override public Object getDisposerReferent() { return disposerReferent; } /* Instance Methods */ /** * 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 */ @Override public PageFormat pageDialog(PageFormat page) throws HeadlessException { if (GraphicsEnvironment.isHeadless()) { throw new HeadlessException(); } if (!(getPrintService() instanceof Win32PrintService)) { return super.pageDialog(page); } PageFormat pageClone = (PageFormat) page.clone(); boolean result = false; /* * Fix for 4507585: show the native modal dialog the same way printDialog() does so * that it won't block event dispatching when called on EventDispatchThread. */ WPageDialog dialog = new WPageDialog((Frame)null, this, pageClone, null); dialog.setRetVal(false); dialog.setVisible(true); result = dialog.getRetVal(); dialog.dispose(); // myService => current PrintService if (result && (myService != null)) { // It's possible that current printer is changed through // the "Printer..." button so we query again from native. String printerName = getNativePrintService(); if (!myService.getName().equals(printerName)) { // native printer is different ! // we update the current PrintService try { setPrintService(PrintServiceLookupProvider. getWin32PrintLUS(). getPrintServiceByName(printerName)); } catch (PrinterException e) { } } // Update attributes, this will preserve the page settings. // - same code as in RasterPrinterJob.java updatePageAttributes(myService, pageClone); return pageClone; } else { return page; } } private boolean displayNativeDialog() { // "attributes" is required for getting the updated attributes if (attributes == null) { return false; } DialogOwner dlgOwner = (DialogOwner)attributes.get(DialogOwner.class); Frame ownerFrame = (dlgOwner != null) ? dlgOwner.getOwner() : null; WPrintDialog dialog = new WPrintDialog(ownerFrame, this); dialog.setRetVal(false); dialog.setVisible(true); boolean prv = dialog.getRetVal(); dialog.dispose(); Destination dest = (Destination)attributes.get(Destination.class); if ((dest == null) || !prv){ return prv; } else { String title = null; String strBundle = "sun.print.resources.serviceui"; ResourceBundle rb = ResourceBundle.getBundle(strBundle); try { title = rb.getString("dialog.printtofile"); } catch (MissingResourceException e) { } FileDialog fileDialog = new FileDialog(ownerFrame, title, FileDialog.SAVE); URI destURI = dest.getURI(); // Old code destURI.getPath() would return null for "file:out.prn" // so we use getSchemeSpecificPart instead. String pathName = (destURI != null) ? destURI.getSchemeSpecificPart() : null; if (pathName != null) { File file = new File(pathName); fileDialog.setFile(file.getName()); File parent = file.getParentFile(); if (parent != null) { fileDialog.setDirectory(parent.getPath()); } } else { fileDialog.setFile("out.prn"); } fileDialog.setVisible(true); String fileName = fileDialog.getFile(); if (fileName == null) { fileDialog.dispose(); return false; } String fullName = fileDialog.getDirectory() + fileName; File f = new File(fullName); File pFile = f.getParentFile(); while ((f.exists() && (!f.isFile() || !f.canWrite())) || ((pFile != null) && (!pFile.exists() || (pFile.exists() && !pFile.canWrite())))) { (new PrintToFileErrorDialog(ownerFrame, ServiceDialog.getMsg("dialog.owtitle"), ServiceDialog.getMsg("dialog.writeerror")+" "+fullName, ServiceDialog.getMsg("button.ok"))).setVisible(true); fileDialog.setVisible(true); fileName = fileDialog.getFile(); if (fileName == null) { fileDialog.dispose(); return false; } fullName = fileDialog.getDirectory() + fileName; f = new File(fullName); pFile = f.getParentFile(); } fileDialog.dispose(); attributes.add(new Destination(f.toURI())); 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 */ @Override public boolean printDialog() throws HeadlessException { if (GraphicsEnvironment.isHeadless()) { throw new HeadlessException(); } // current request attribute set should be reflected to the print dialog. // If null, create new instance of HashPrintRequestAttributeSet. if (attributes == null) { attributes = new HashPrintRequestAttributeSet(); } if (!(getPrintService() instanceof Win32PrintService)) { return super.printDialog(attributes); } if (noDefaultPrinter == true) { return false; } else { return displayNativeDialog(); } } /** * Associate this PrinterJob with a new PrintService. * * Throws {@code PrinterException} if the specified service * cannot support the {@code Pageable} and * 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. */ @Override public void setPrintService(PrintService service) throws PrinterException { super.setPrintService(service); if (!(service instanceof Win32PrintService)) { return; } driverDoesMultipleCopies = false; driverDoesCollation = false; setNativePrintServiceIfNeeded(service.getName()); } /* associates this job with the specified native service */ private native void setNativePrintService(String name) throws PrinterException; private String lastNativeService = null; private void setNativePrintServiceIfNeeded(String name) throws PrinterException { if (name != null && !(name.equals(lastNativeService))) { setNativePrintService(name); lastNativeService = name; } } @Override public PrintService getPrintService() { if (myService == null) { String printerName = getNativePrintService(); if (printerName != null) { myService = PrintServiceLookupProvider.getWin32PrintLUS(). getPrintServiceByName(printerName); // no need to call setNativePrintService as this name is // currently set in native if (myService != null) { return myService; } } myService = PrintServiceLookup.lookupDefaultPrintService(); if (myService instanceof Win32PrintService) { try { setNativePrintServiceIfNeeded(myService.getName()); } catch (Exception e) { myService = null; } } } return myService; } private native String getNativePrintService(); private void initAttributeMembers() { mAttSides = 0; mAttChromaticity = 0; mAttXRes = 0; mAttYRes = 0; mAttQuality = 0; mAttCollate = -1; mAttCopies = 0; mAttMediaTray = 0; mAttMediaSizeName = 0; mDestination = null; } /** * copy the attributes to the native print job * Note that this method, and hence the re-initialisation * of the GDI values is done on each entry to the print dialog since * an app could redisplay the print dialog for the same job and * 1) the application may have changed attribute settings * 2) the application may have changed the printer. * In the event that the user changes the printer using the dialog, then it is up to GDI to report back all changed values. */ @Override protected void setAttributes(PrintRequestAttributeSet attributes) throws PrinterException { // initialize attribute values initAttributeMembers(); super.setAttributes(attributes); mAttCopies = getCopiesInt(); mDestination = destinationAttr; if (attributes == null) { return; // now always use attributes, so this shouldn't happen. } Attribute[] attrs = attributes.toArray(); for (int i=0; i< attrs.length; i++) { Attribute attr = attrs[i]; try { if (attr.getCategory() == Sides.class) { setSidesAttrib(attr); } else if (attr.getCategory() == Chromaticity.class) { setColorAttrib(attr); } else if (attr.getCategory() == PrinterResolution.class) { if (myService.isAttributeValueSupported(attr, null, null)) { setResolutionAttrib(attr); } } else if (attr.getCategory() == PrintQuality.class) { setQualityAttrib(attr); } else if (attr.getCategory() == SheetCollate.class) { setCollateAttrib(attr); } else if (attr.getCategory() == Media.class || attr.getCategory() == SunAlternateMedia.class) { /* SunAlternateMedia is used if its a tray, and * any Media that is specified is not a tray. */ if (attr.getCategory() == SunAlternateMedia.class) { Media media = (Media)attributes.get(Media.class); if (media == null || !(media instanceof MediaTray)) { attr = ((SunAlternateMedia)attr).getMedia(); } } if (attr instanceof MediaSizeName) { setWin32MediaAttrib(attr); } if (attr instanceof MediaTray) { setMediaTrayAttrib(attr); } } } catch (ClassCastException e) { } } } /** * Alters the orientation and Paper to match defaults obtained * from a printer. */ private native void getDefaultPage(PageFormat page); /** * The passed in PageFormat will be copied and altered to describe * the default page size and orientation of the PrinterJob's * current printer. * Note: PageFormat.getPaper() returns a clone and getDefaultPage() * gets that clone so it won't overwrite the original paper. */ @Override public PageFormat defaultPage(PageFormat page) { PageFormat newPage = (PageFormat)page.clone(); getDefaultPage(newPage); return newPage; } /** * validate the paper size against the current printer. */ @Override protected native void validatePaper(Paper origPaper, Paper newPaper ); /** * 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. */ @Override protected Graphics2D createPathGraphics(PeekGraphics peekGraphics, PrinterJob printerJob, Printable painter, PageFormat pageFormat, int pageIndex) { WPathGraphics pathGraphics; PeekMetrics metrics = peekGraphics.getMetrics(); /* If the application has drawn anything that * out PathGraphics class can not handle then * return a null PathGraphics. If the property * to force the raster pipeline has been set then * we also want to avoid the path (pdl) pipeline * and return null. */ if (forcePDL == false && (forceRaster == true || metrics.hasNonSolidColors() || metrics.hasCompositing() )) { pathGraphics = null; } else { BufferedImage bufferedImage = new BufferedImage(8, 8, BufferedImage.TYPE_INT_RGB); Graphics2D bufferedGraphics = bufferedImage.createGraphics(); boolean canRedraw = peekGraphics.getAWTDrawingOnly() == false; pathGraphics = new WPathGraphics(bufferedGraphics, printerJob, painter, pageFormat, pageIndex, canRedraw); } return pathGraphics; } @Override protected double getXRes() { if (mAttXRes != 0) { return mAttXRes; } else { return mPrintXRes; } } @Override protected double getYRes() { if (mAttYRes != 0) { return mAttYRes; } else { return mPrintYRes; } } @Override protected double getPhysicalPrintableX(Paper p) { return mPrintPhysX; } @Override protected double getPhysicalPrintableY(Paper p) { return mPrintPhysY; } @Override protected double getPhysicalPrintableWidth(Paper p) { return mPrintWidth; } @Override protected double getPhysicalPrintableHeight(Paper p) { return mPrintHeight; } @Override protected double getPhysicalPageWidth(Paper p) { return mPageWidth; } @Override protected double getPhysicalPageHeight(Paper p) { return mPageHeight; } /** * We don't (yet) provide API to support collation, and * when we do the logic here will require adjustment, but * this method is currently necessary to honour user-originated * collation requests - which can only originate from the print dialog. * REMIND: check if this can be deleted already. */ @Override protected boolean isCollated() { return userRequestedCollation; } /** * 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. */ @Override protected int getCollatedCopies() { debug_println("driverDoesMultipleCopies="+driverDoesMultipleCopies +" driverDoesCollation="+driverDoesCollation); if (super.isCollated() && !driverDoesCollation) { // we will do our own collation so we need to // tell the printer to not collate mAttCollate = 0; mAttCopies = 1; return getCopies(); } return 1; } /** * Returns how many times each page in the book * should be consecutively printed by PrinterJob. * If the underlying Window's driver will * generate the copies, rather than having RasterPrinterJob * iterate over the number of copies, this method always returns * 1. */ @Override protected int getNoncollatedCopies() { if (driverDoesMultipleCopies || super.isCollated()) { return 1; } else { return getCopies(); } } /* These getter/setters are called from native code */ /** * Return the Window's device context that we are printing * into. */ private long getPrintDC() { return handleRecord.mPrintDC; } private void setPrintDC(long mPrintDC) { handleRecord.mPrintDC = mPrintDC; } private long getDevMode() { return handleRecord.mPrintHDevMode; } private void setDevMode(long mPrintHDevMode) { handleRecord.mPrintHDevMode = mPrintHDevMode; } private long getDevNames() { return handleRecord.mPrintHDevNames; } private void setDevNames(long mPrintHDevNames) { handleRecord.mPrintHDevNames = mPrintHDevNames; } protected void beginPath() { beginPath(getPrintDC()); } protected void endPath() { endPath(getPrintDC()); } protected void closeFigure() { closeFigure(getPrintDC()); } protected void fillPath() { fillPath(getPrintDC()); } protected void moveTo(float x, float y) { moveTo(getPrintDC(), x, y); } protected void lineTo(float x, float y) { lineTo(getPrintDC(), x, y); } protected void polyBezierTo(float control1x, float control1y, float control2x, float control2y, float endX, float endY) { polyBezierTo(getPrintDC(), control1x, control1y, control2x, control2y, endX, endY); } /** * Set the current polgon fill rule into the printer device context. * The {@code fillRule} should * be one of the following Windows constants: * {@code ALTERNATE} or {@code WINDING}. */ protected void setPolyFillMode(int fillRule) { setPolyFillMode(getPrintDC(), fillRule); } /* * Create a Window's solid brush for the color specified * by {@code (red, green, blue)}. Once the brush * is created, select it in the current printing device * context and free the old brush. */ protected void selectSolidBrush(Color color) { /* We only need to select a brush if the color has changed. */ if (color.equals(mLastColor) == false) { mLastColor = color; float[] rgb = color.getRGBColorComponents(null); selectSolidBrush(getPrintDC(), (int) (rgb[0] * MAX_WCOLOR), (int) (rgb[1] * MAX_WCOLOR), (int) (rgb[2] * MAX_WCOLOR)); } } /** * Return the x coordinate of the current pen * position in the print device context. */ protected int getPenX() { return getPenX(getPrintDC()); } /** * Return the y coordinate of the current pen * position in the print device context. */ protected int getPenY() { return getPenY(getPrintDC()); } /** * Set the current path in the printer device's * context to be clipping path. */ protected void selectClipPath() { selectClipPath(getPrintDC()); } protected void frameRect(float x, float y, float width, float height) { frameRect(getPrintDC(), x, y, width, height); } protected void fillRect(float x, float y, float width, float height, Color color) { float[] rgb = color.getRGBColorComponents(null); fillRect(getPrintDC(), x, y, width, height, (int) (rgb[0] * MAX_WCOLOR), (int) (rgb[1] * MAX_WCOLOR), (int) (rgb[2] * MAX_WCOLOR)); } protected void selectPen(float width, Color color) { float[] rgb = color.getRGBColorComponents(null); selectPen(getPrintDC(), width, (int) (rgb[0] * MAX_WCOLOR), (int) (rgb[1] * MAX_WCOLOR), (int) (rgb[2] * MAX_WCOLOR)); } protected boolean selectStylePen(int cap, int join, float width, Color color) { long endCap; long lineJoin; float[] rgb = color.getRGBColorComponents(null); switch(cap) { case BasicStroke.CAP_BUTT: endCap = PS_ENDCAP_FLAT; break; case BasicStroke.CAP_ROUND: endCap = PS_ENDCAP_ROUND; break; default: case BasicStroke.CAP_SQUARE: endCap = PS_ENDCAP_SQUARE; break; } switch(join) { case BasicStroke.JOIN_BEVEL:lineJoin = PS_JOIN_BEVEL; break; default: case BasicStroke.JOIN_MITER:lineJoin = PS_JOIN_MITER; break; case BasicStroke.JOIN_ROUND:lineJoin = PS_JOIN_ROUND; break; } return (selectStylePen(getPrintDC(), endCap, lineJoin, width, (int) (rgb[0] * MAX_WCOLOR), (int) (rgb[1] * MAX_WCOLOR), (int) (rgb[2] * MAX_WCOLOR))); } /** * Set a GDI font capable of drawing the java Font * passed in. */ protected boolean setFont(String family, float size, int style, int rotation, float awScale) { boolean didSetFont = true; if (!family.equals(mLastFontFamily) || size != mLastFontSize || style != mLastFontStyle || rotation != mLastRotation || awScale != mLastAwScale) { didSetFont = setFont(getPrintDC(), family, size, (style & Font.BOLD) != 0, (style & Font.ITALIC) != 0, rotation, awScale); if (didSetFont) { mLastFontFamily = family; mLastFontSize = size; mLastFontStyle = style; mLastRotation = rotation; mLastAwScale = awScale; } } return didSetFont; } /** * Set the GDI color for text drawing. */ protected void setTextColor(Color color) { /* We only need to select a brush if the color has changed. */ if (color.equals(mLastTextColor) == false) { mLastTextColor = color; float[] rgb = color.getRGBColorComponents(null); setTextColor(getPrintDC(), (int) (rgb[0] * MAX_WCOLOR), (int) (rgb[1] * MAX_WCOLOR), (int) (rgb[2] * MAX_WCOLOR)); } } /** * Remove control characters. */ @Override protected String removeControlChars(String str) { return super.removeControlChars(str); } /** * Draw the string {@code text} to the printer's * device context at the specified position. */ protected void textOut(String str, float x, float y, float[] positions) { /* Don't leave handling of control chars to GDI. * If control chars are removed, 'positions' isn't valid. * This means the caller needs to be aware of this and remove * control chars up front if supplying positions. Since the * caller is tightly integrated here, that's acceptable. */ String text = removeControlChars(str); assert (positions == null) || (text.length() == str.length()); if (text.length() == 0) { return; } textOut(getPrintDC(), text, text.length(), false, x, y, positions); } /** * Draw the glyphs {@code glyphs} to the printer's * device context at the specified position. */ protected void glyphsOut(int []glyphs, float x, float y, float[] positions) { /* TrueType glyph codes are 16 bit values, so can be packed * in a unicode string, and that's how GDI expects them. * A flag bit is set to indicate to GDI that these are glyphs, * not characters. The positions array must always be non-null * here for our purposes, although if not supplied, GDI should * just use the default advances for the glyphs. * Mask out upper 16 bits to remove any slot from a composite. */ char[] glyphCharArray = new char[glyphs.length]; for (int i=0;i>", "read,write"); try { security.checkPermission(printToFilePermission); } catch (SecurityException e) { return false; } } return true; } private void setNativeAttributes(int flags, int fields, int values) { if (attributes == null) { return; } if ((flags & PD_PRINTTOFILE) != 0) { Destination destPrn = (Destination)attributes.get( Destination.class); if (destPrn == null) { try { attributes.add(new Destination( new File("./out.prn").toURI())); } catch (SecurityException se) { try { attributes.add(new Destination( new URI("file:out.prn"))); } catch (URISyntaxException e) { } } } } else { attributes.remove(Destination.class); } if ((flags & PD_COLLATE) != 0) { setCollateAttrib(SheetCollate.COLLATED, attributes); } else { setCollateAttrib(SheetCollate.UNCOLLATED, attributes); } if ((flags & PD_NOSELECTION) != PD_NOSELECTION) { if ((flags & PD_PAGENUMS) != 0) { attributes.add(SunPageSelection.RANGE); } else if ((flags & PD_SELECTION) != 0) { attributes.add(SunPageSelection.SELECTION); } else { attributes.add(SunPageSelection.ALL); } } if ((fields & DM_ORIENTATION) != 0) { if ((values & SET_ORIENTATION) != 0) { setOrientAttrib(OrientationRequested.LANDSCAPE, attributes); } else { setOrientAttrib(OrientationRequested.PORTRAIT, attributes); } } if ((fields & DM_COLOR) != 0) { if ((values & SET_COLOR) != 0) { setColorAttrib(Chromaticity.COLOR, attributes); } else { setColorAttrib(Chromaticity.MONOCHROME, attributes); } } if ((fields & DM_PRINTQUALITY) != 0) { PrintQuality quality; if ((values & SET_RES_LOW) != 0) { quality = PrintQuality.DRAFT; } else if ((fields & SET_RES_HIGH) != 0) { quality = PrintQuality.HIGH; } else { quality = PrintQuality.NORMAL; } setQualityAttrib(quality, attributes); } if ((fields & DM_DUPLEX) != 0) { Sides sides; if ((values & SET_DUP_VERTICAL) != 0) { sides = Sides.TWO_SIDED_LONG_EDGE; } else if ((values & SET_DUP_HORIZONTAL) != 0) { sides = Sides.TWO_SIDED_SHORT_EDGE; } else { sides = Sides.ONE_SIDED; } setSidesAttrib(sides, attributes); } } private static final class DevModeValues { int dmFields; short copies; short collate; short color; short duplex; short orient; short paper; short bin; short xres_quality; short yres; } private void getDevModeValues(PrintRequestAttributeSet aset, DevModeValues info) { Copies c = (Copies)aset.get(Copies.class); if (c != null) { info.dmFields |= DM_COPIES; info.copies = (short)c.getValue(); } SheetCollate sc = (SheetCollate)aset.get(SheetCollate.class); if (sc != null) { info.dmFields |= DM_COLLATE; info.collate = (sc == SheetCollate.COLLATED) ? DMCOLLATE_TRUE : DMCOLLATE_FALSE; } Chromaticity ch = (Chromaticity)aset.get(Chromaticity.class); if (ch != null) { info.dmFields |= DM_COLOR; if (ch == Chromaticity.COLOR) { info.color = DMCOLOR_COLOR; } else { info.color = DMCOLOR_MONOCHROME; } } Sides s = (Sides)aset.get(Sides.class); if (s != null) { info.dmFields |= DM_DUPLEX; if (s == Sides.TWO_SIDED_LONG_EDGE) { info.duplex = DMDUP_VERTICAL; } else if (s == Sides.TWO_SIDED_SHORT_EDGE) { info.duplex = DMDUP_HORIZONTAL; } else { // Sides.ONE_SIDED info.duplex = DMDUP_SIMPLEX; } } OrientationRequested or = (OrientationRequested)aset.get(OrientationRequested.class); if (or != null) { info.dmFields |= DM_ORIENTATION; info.orient = (or == OrientationRequested.LANDSCAPE) ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT; } Media m = (Media)aset.get(Media.class); if (m instanceof MediaSizeName) { info.dmFields |= DM_PAPERSIZE; MediaSizeName msn = (MediaSizeName)m; info.paper = (short)((Win32PrintService)myService).findPaperID(msn); } MediaTray mt = null; if (m instanceof MediaTray) { mt = (MediaTray)m; } if (mt == null) { SunAlternateMedia sam = (SunAlternateMedia)aset.get(SunAlternateMedia.class); if (sam != null && (sam.getMedia() instanceof MediaTray)) { mt = (MediaTray)sam.getMedia(); } } if (mt != null) { info.dmFields |= DM_DEFAULTSOURCE; info.bin = (short)(((Win32PrintService)myService).findTrayID(mt)); } PrintQuality q = (PrintQuality)aset.get(PrintQuality.class); if (q != null) { info.dmFields |= DM_PRINTQUALITY; if (q == PrintQuality.DRAFT) { info.xres_quality = DMRES_DRAFT; } else if (q == PrintQuality.HIGH) { info.xres_quality = DMRES_HIGH; } else { info.xres_quality = DMRES_MEDIUM; } } PrinterResolution r = (PrinterResolution)aset.get(PrinterResolution.class); if (r != null) { info.dmFields |= DM_PRINTQUALITY | DM_YRESOLUTION; info.xres_quality = (short)r.getCrossFeedResolution(PrinterResolution.DPI); info.yres = (short)r.getFeedResolution(PrinterResolution.DPI); } } /* This method is called from native to update the values in the * attribute set which originates from the cross-platform dialog, * but updated by the native DocumentPropertiesUI which updates the * devmode. This syncs the devmode back in to the attributes so that * we can update the cross-platform dialog. * The attribute set here is a temporary one installed whilst this * happens, */ private void setJobAttributes(PrintRequestAttributeSet attributes, int fields, int values, short copies, short dmPaperSize, short dmPaperWidth, short dmPaperLength, short dmDefaultSource, short xRes, short yRes) { if (attributes == null) { return; } if ((fields & DM_COPIES) != 0) { attributes.add(new Copies(copies)); } if ((fields & DM_COLLATE) != 0) { if ((values & SET_COLLATED) != 0) { attributes.add(SheetCollate.COLLATED); } else { attributes.add(SheetCollate.UNCOLLATED); } } if ((fields & DM_ORIENTATION) != 0) { if ((values & SET_ORIENTATION) != 0) { attributes.add(OrientationRequested.LANDSCAPE); } else { attributes.add(OrientationRequested.PORTRAIT); } } if ((fields & DM_COLOR) != 0) { if ((values & SET_COLOR) != 0) { attributes.add(Chromaticity.COLOR); } else { attributes.add(Chromaticity.MONOCHROME); } } if ((fields & DM_PRINTQUALITY) != 0) { /* value < 0 indicates quality setting. * value > 0 indicates X resolution. In that case * hopefully we will also find y-resolution specified. * If its not, assume its the same as x-res. * Maybe Java code should try to reconcile this against * the printers claimed set of supported resolutions. */ if (xRes < 0) { PrintQuality quality; if ((values & SET_RES_LOW) != 0) { quality = PrintQuality.DRAFT; } else if ((fields & SET_RES_HIGH) != 0) { quality = PrintQuality.HIGH; } else { quality = PrintQuality.NORMAL; } attributes.add(quality); } else if (xRes > 0 && yRes > 0) { attributes.add( new PrinterResolution(xRes, yRes, PrinterResolution.DPI)); } } if ((fields & DM_DUPLEX) != 0) { Sides sides; if ((values & SET_DUP_VERTICAL) != 0) { sides = Sides.TWO_SIDED_LONG_EDGE; } else if ((values & SET_DUP_HORIZONTAL) != 0) { sides = Sides.TWO_SIDED_SHORT_EDGE; } else { sides = Sides.ONE_SIDED; } attributes.add(sides); } if ((fields & DM_PAPERSIZE) != 0) { addPaperSize(attributes, dmPaperSize, dmPaperWidth, dmPaperLength); } if ((fields & DM_DEFAULTSOURCE) != 0) { MediaTray tray = ((Win32PrintService)myService).findMediaTray(dmDefaultSource); attributes.add(new SunAlternateMedia(tray)); } } private native boolean showDocProperties(long hWnd, PrintRequestAttributeSet aset, int dmFields, short copies, short collate, short color, short duplex, short orient, short paper, short bin, short xres_quality, short yres); public PrintRequestAttributeSet showDocumentProperties(Window owner, PrintService service, PrintRequestAttributeSet aset) { try { setNativePrintServiceIfNeeded(service.getName()); } catch (PrinterException e) { } final ComponentAccessor acc = AWTAccessor.getComponentAccessor(); long hWnd = acc.getPeer(owner).getHWnd(); DevModeValues info = new DevModeValues(); getDevModeValues(aset, info); boolean ok = showDocProperties(hWnd, aset, info.dmFields, info.copies, info.collate, info.color, info.duplex, info.orient, info.paper, info.bin, info.xres_quality, info.yres); if (ok) { return aset; } else { return null; } } /* Printer Resolution. See also getXRes() and getYRes() */ private void setResolutionDPI(int xres, int yres) { if (attributes != null) { PrinterResolution res = new PrinterResolution(xres, yres, PrinterResolution.DPI); attributes.add(res); } mAttXRes = xres; mAttYRes = yres; } private void setResolutionAttrib(Attribute attr) { PrinterResolution pr = (PrinterResolution)attr; mAttXRes = pr.getCrossFeedResolution(PrinterResolution.DPI); mAttYRes = pr.getFeedResolution(PrinterResolution.DPI); } private void setPrinterNameAttrib(String printerName) { PrintService service = this.getPrintService(); if (printerName == null) { return; } if (service != null && printerName.equals(service.getName())) { return; } else { PrintService []services = PrinterJob.lookupPrintServices(); for (int i=0; i