changeset 6570:7e5de40af11a

4673406: RFE: Java Printing: Provide a way to display win32 printer driver's dialog Summary: Displays the Windows DocumentProperties UI from the JDK's Swing print dialog. Reviewed-by: prr, bae
author dbuck
date Thu, 12 Sep 2013 01:50:51 -0700
parents 2f890a506334
children 1dfc719e1525
files src/share/classes/sun/print/DocumentPropertiesUI.java src/share/classes/sun/print/PrinterJobWrapper.java src/share/classes/sun/print/RasterPrinterJob.java src/share/classes/sun/print/ServiceDialog.java src/windows/classes/sun/awt/windows/WPrinterJob.java src/windows/classes/sun/print/Win32MediaTray.java src/windows/classes/sun/print/Win32PrintService.java src/windows/native/sun/windows/awt_PrintControl.cpp src/windows/native/sun/windows/awt_PrintControl.h src/windows/native/sun/windows/awt_PrintJob.cpp
diffstat 10 files changed, 721 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/print/DocumentPropertiesUI.java	Thu Sep 12 01:50:51 2013 -0700
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2013, 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.awt.Window;
+import java.awt.print.PrinterJob;
+import javax.print.PrintService;
+import javax.print.ServiceUIFactory;
+import javax.print.attribute.PrintRequestAttributeSet;
+
+public abstract class DocumentPropertiesUI {
+
+    /**
+     * For Win32 doc properties sheet.
+     */
+    public static final int
+        DOCUMENTPROPERTIES_ROLE = ServiceUIFactory.RESERVED_UIROLE +100;
+
+    /**
+     * Name of (this) abstract class for Document Properties.
+     */
+    public static final String
+        DOCPROPERTIESCLASSNAME = DocumentPropertiesUI.class.getName();
+
+    /**
+     * Invokes whatever code is needed to display a native dialog
+     * with the specified owner. The owner should be the cross-platform
+     * dialog. If the user cancels the dialog the return value is null.
+     * A non-null return value is always a new attribute set (or is it?)
+     * The cross-platform dialog may need to be updated to reflect the
+     * updated properties.
+     */
+    public abstract PrintRequestAttributeSet
+        showDocumentProperties(PrinterJob job,
+                               Window owner,
+                               PrintService service,
+                               PrintRequestAttributeSet aset);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/print/PrinterJobWrapper.java	Thu Sep 12 01:50:51 2013 -0700
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013, 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.awt.print.PrinterJob;
+import javax.print.attribute.PrintRequestAttribute;
+
+public class PrinterJobWrapper implements PrintRequestAttribute {
+
+    private static final long serialVersionUID = -8792124426995707237L;
+
+    private PrinterJob job;
+
+    public PrinterJobWrapper(PrinterJob job) {
+        this.job = job;
+    }
+
+    public PrinterJob getPrinterJob() {
+        return job;
+    }
+
+    public final Class getCategory() {
+        return PrinterJobWrapper.class;
+    }
+
+    public final String getName() {
+        return "printerjob-wrapper";
+    }
+
+    public String toString() {
+       return "printerjob-wrapper: " + job.toString();
+    }
+
+    public int hashCode() {
+        return job.hashCode();
+    }
+}
--- a/src/share/classes/sun/print/RasterPrinterJob.java	Tue Aug 27 17:50:55 2013 +0800
+++ b/src/share/classes/sun/print/RasterPrinterJob.java	Thu Sep 12 01:50:51 2013 -0700
@@ -879,6 +879,9 @@
         int x = bounds.x+bounds.width/3;
         int y = bounds.y+bounds.height/3;
         PrintService newService;
+        // temporarily add an attribute pointing back to this job.
+        PrinterJobWrapper jobWrapper = new PrinterJobWrapper(this);
+        attributes.add(jobWrapper);
         try {
             newService =
             ServiceUI.printDialog(gc, x, y,
@@ -891,6 +894,7 @@
                                   DocFlavor.SERVICE_FORMATTED.PAGEABLE,
                                   attributes);
         }
+        attributes.remove(PrinterJobWrapper.class);
 
         if (newService == null) {
             return false;
--- a/src/share/classes/sun/print/ServiceDialog.java	Tue Aug 27 17:50:55 2013 +0800
+++ b/src/share/classes/sun/print/ServiceDialog.java	Thu Sep 12 01:50:51 2013 -0700
@@ -46,6 +46,7 @@
 import java.awt.event.ItemListener;
 import java.awt.event.WindowEvent;
 import java.awt.event.WindowAdapter;
+import java.awt.print.PrinterJob;
 import java.io.File;
 import java.io.FilePermission;
 import java.io.IOException;
@@ -119,8 +120,6 @@
     private AppearancePanel pnlAppearance;
 
     private boolean isAWT = false;
-
-
     static {
         initResource();
     }
@@ -801,9 +800,32 @@
                     if (dialog != null) {
                         dialog.show();
                     } else {
-                        // REMIND: may want to notify the user why we're
-                        //         disabling the button
-                        btnProperties.setEnabled(false);
+                        DocumentPropertiesUI docPropertiesUI = null;
+                        try {
+                            docPropertiesUI =
+                                (DocumentPropertiesUI)uiFactory.getUI
+                                (DocumentPropertiesUI.DOCUMENTPROPERTIES_ROLE,
+                                 DocumentPropertiesUI.DOCPROPERTIESCLASSNAME);
+                        } catch (Exception ex) {
+                        }
+                        if (docPropertiesUI != null) {
+                            PrinterJobWrapper wrapper = (PrinterJobWrapper)
+                                asCurrent.get(PrinterJobWrapper.class);
+                            if (wrapper == null) {
+                                return; // should not happen, defensive only.
+                            }
+                            PrinterJob job = wrapper.getPrinterJob();
+                            if (job == null) {
+                                return;  // should not happen, defensive only.
+                            }
+                            PrintRequestAttributeSet newAttrs =
+                               docPropertiesUI.showDocumentProperties
+                               (job, ServiceDialog.this, psCurrent, asCurrent);
+                            if (newAttrs != null) {
+                                asCurrent.addAll(newAttrs);
+                                updatePanels();
+                            }
+                        }
                     }
                 }
             }
--- a/src/windows/classes/sun/awt/windows/WPrinterJob.java	Tue Aug 27 17:50:55 2013 +0800
+++ b/src/windows/classes/sun/awt/windows/WPrinterJob.java	Thu Sep 12 01:50:51 2013 -0700
@@ -179,6 +179,7 @@
     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
@@ -189,10 +190,33 @@
     private static final int PD_NOSELECTION = 0x00000004;
     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_PRINTQUALITY = 0x00000400;
-    private static final int DM_COLOR = 0x00000800;
-    private static final int DM_DUPLEX = 0x00001000;
+    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
@@ -592,13 +616,23 @@
         }
         driverDoesMultipleCopies = false;
         driverDoesCollation = false;
-        setNativePrintService(service.getName());
+        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;
+        }
+    }
+
     public PrintService getPrintService() {
         if (myService == null) {
             String printerName = getNativePrintService();
@@ -616,7 +650,7 @@
             myService = PrintServiceLookup.lookupDefaultPrintService();
             if (myService != null) {
                 try {
-                    setNativePrintService(myService.getName());
+                    setNativePrintServiceIfNeeded(myService.getName());
                 } catch (Exception e) {
                     myService = null;
                 }
@@ -1742,8 +1776,13 @@
         mAttMediaSizeName = ((Win32PrintService)myService).findPaperID(msn);
     }
 
-    private void setWin32MediaAttrib(int dmIndex, int width, int length) {
-       MediaSizeName msn =
+    private void addPaperSize(PrintRequestAttributeSet aset,
+                              int dmIndex, int width, int length) {
+
+        if (aset == null) {
+            return;
+        }
+        MediaSizeName msn =
            ((Win32PrintService)myService).findWin32Media(dmIndex);
         if (msn == null) {
             msn = ((Win32PrintService)myService).
@@ -1751,10 +1790,12 @@
         }
 
         if (msn != null) {
-            if (attributes != null) {
-                attributes.add(msn);
-            }
+            aset.add(msn);
         }
+    }
+
+    private void setWin32MediaAttrib(int dmIndex, int width, int length) {
+        addPaperSize(attributes, dmIndex, width, length);
         mAttMediaSizeName = dmIndex;
     }
 
@@ -1776,7 +1817,7 @@
             // no equivalent predefined value
             mAttMediaTray = 7;              // DMBIN_AUTO
         } else if (attr == MediaTray.TOP) {
-            mAttMediaTray =1;               // DMBIN_UPPER
+            mAttMediaTray = 1;              // DMBIN_UPPER
         } else {
             if (attr instanceof Win32MediaTray) {
                 mAttMediaTray = ((Win32MediaTray)attr).winID;
@@ -1902,6 +1943,254 @@
         }
     }
 
+    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 final 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);
+
+    @SuppressWarnings("deprecation")
+    public PrintRequestAttributeSet
+        showDocumentProperties(Window owner,
+                               PrintService service,
+                               PrintRequestAttributeSet aset)
+    {
+        try {
+            setNativePrintServiceIfNeeded(service.getName());
+        } catch (PrinterException e) {
+        }
+        long hWnd = ((WWindowPeer)(owner.getPeer())).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 final void setResolutionDPI(int xres, int yres) {
@@ -1944,7 +2233,7 @@
         }
     //** END Functions called by native code for querying/updating attributes
 
-   }
+    }
 
 class PrintToFileErrorDialog extends Dialog implements ActionListener{
     public PrintToFileErrorDialog(Frame parent, String title, String message,
--- a/src/windows/classes/sun/print/Win32MediaTray.java	Tue Aug 27 17:50:55 2013 +0800
+++ b/src/windows/classes/sun/print/Win32MediaTray.java	Thu Sep 12 01:50:51 2013 -0700
@@ -70,6 +70,10 @@
         winEnumTable.add(this);
     }
 
+    public int getDMBinID() {
+        return winID;
+    }
+
     private static final String[] myStringTable ={
         "Manual-Envelope",
         "Automatic-Feeder",
--- a/src/windows/classes/sun/print/Win32PrintService.java	Tue Aug 27 17:50:55 2013 +0800
+++ b/src/windows/classes/sun/print/Win32PrintService.java	Thu Sep 12 01:50:51 2013 -0700
@@ -25,6 +25,8 @@
 
 package sun.print;
 
+import java.awt.Window;
+import java.awt.print.PrinterJob;
 import java.io.File;
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -39,6 +41,7 @@
 import javax.print.attribute.AttributeSetUtilities;
 import javax.print.attribute.EnumSyntax;
 import javax.print.attribute.HashAttributeSet;
+import javax.print.attribute.PrintRequestAttributeSet;
 import javax.print.attribute.PrintServiceAttribute;
 import javax.print.attribute.PrintServiceAttributeSet;
 import javax.print.attribute.HashPrintServiceAttributeSet;
@@ -69,6 +72,7 @@
 import javax.print.attribute.standard.PrinterResolution;
 import javax.print.attribute.standard.SheetCollate;
 import javax.print.event.PrintServiceAttributeListener;
+import sun.awt.windows.WPrinterJob;
 
 public class Win32PrintService implements PrintService, AttributeUpdater,
                                           SunPrinterJobService {
@@ -279,6 +283,22 @@
         return 0;
     }
 
+    public int findTrayID(MediaTray tray) {
+
+        getMediaTrays(); // make sure they are initialised.
+
+        if (tray instanceof Win32MediaTray) {
+            Win32MediaTray winTray = (Win32MediaTray)tray;
+            return winTray.getDMBinID();
+        }
+        for (int id=0; id<dmPaperBinToPrintService.length; id++) {
+            if (tray.equals(dmPaperBinToPrintService[id])) {
+                return id+1; // DMBIN_FIRST = 1;
+            }
+        }
+        return 0; // didn't find the tray
+    }
+
     public MediaTray findMediaTray(int dmBin) {
         if (dmBin >= 1 && dmBin <= dmPaperBinToPrintService.length) {
             return dmPaperBinToPrintService[dmBin-1];
@@ -670,7 +690,6 @@
         return arr2;
     }
 
-
     private PrinterIsAcceptingJobs getPrinterIsAcceptingJobs() {
         if (getJobStatus(printer, 2) != 1) {
             return PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS;
@@ -1593,8 +1612,76 @@
         }
     }
 
-    public ServiceUIFactory getServiceUIFactory() {
-        return null;
+    private Win32DocumentPropertiesUI docPropertiesUI = null;
+
+    private static class Win32DocumentPropertiesUI
+        extends DocumentPropertiesUI {
+
+        Win32PrintService service;
+
+        private Win32DocumentPropertiesUI(Win32PrintService s) {
+            service = s;
+        }
+
+        public PrintRequestAttributeSet
+            showDocumentProperties(PrinterJob job,
+                                   Window owner,
+                                   PrintService service,
+                                   PrintRequestAttributeSet aset) {
+
+            if (!(job instanceof WPrinterJob)) {
+                return null;
+            }
+            WPrinterJob wJob = (WPrinterJob)job;
+            return wJob.showDocumentProperties(owner, service, aset);
+        }
+    }
+
+    private synchronized DocumentPropertiesUI getDocumentPropertiesUI() {
+        return new Win32DocumentPropertiesUI(this);
+    }
+
+    private static class Win32ServiceUIFactory extends ServiceUIFactory {
+
+        Win32PrintService service;
+
+        Win32ServiceUIFactory(Win32PrintService s) {
+            service = s;
+        }
+
+        public Object getUI(int role, String ui) {
+            if (role <= ServiceUIFactory.MAIN_UIROLE) {
+                return null;
+            }
+            if (role == DocumentPropertiesUI.DOCUMENTPROPERTIES_ROLE &&
+                DocumentPropertiesUI.DOCPROPERTIESCLASSNAME.equals(ui))
+            {
+                return service.getDocumentPropertiesUI();
+            }
+            throw new IllegalArgumentException("Unsupported role");
+        }
+
+        public String[] getUIClassNamesForRole(int role) {
+
+            if (role <= ServiceUIFactory.MAIN_UIROLE) {
+                return null;
+            }
+            if (role == DocumentPropertiesUI.DOCUMENTPROPERTIES_ROLE) {
+                String[] names = new String[0];
+                names[0] = DocumentPropertiesUI.DOCPROPERTIESCLASSNAME;
+                return names;
+            }
+            throw new IllegalArgumentException("Unsupported role");
+        }
+    }
+
+    private Win32ServiceUIFactory uiFactory = null;
+
+    public synchronized ServiceUIFactory getServiceUIFactory() {
+        if (uiFactory == null) {
+            uiFactory = new Win32ServiceUIFactory(this);
+        }
+        return uiFactory;
     }
 
     public String toString() {
--- a/src/windows/native/sun/windows/awt_PrintControl.cpp	Tue Aug 27 17:50:55 2013 +0800
+++ b/src/windows/native/sun/windows/awt_PrintControl.cpp	Thu Sep 12 01:50:51 2013 -0700
@@ -81,6 +81,7 @@
 jmethodID AwtPrintControl::setNativeAttID;
 jmethodID AwtPrintControl::setRangeCopiesID;
 jmethodID AwtPrintControl::setResID;
+jmethodID AwtPrintControl::setJobAttributesID;
 
 
 BOOL AwtPrintControl::IsSupportedLevel(HANDLE hPrinter, DWORD dwLevel) {
@@ -297,6 +298,10 @@
     AwtPrintControl::setPrinterID =
       env->GetMethodID(cls, "setPrinterNameAttrib", "(Ljava/lang/String;)V");
 
+    AwtPrintControl::setJobAttributesID =
+        env->GetMethodID(cls, "setJobAttributes",
+        "(Ljavax/print/attribute/PrintRequestAttributeSet;IISSSSSSS)V");
+
     DASSERT(AwtPrintControl::driverDoesMultipleCopiesID != NULL);
     DASSERT(AwtPrintControl::getPrintDCID != NULL);
     DASSERT(AwtPrintControl::setPrintDCID != NULL);
@@ -327,6 +332,7 @@
     DASSERT(AwtPrintControl::getSidesID != NULL);
     DASSERT(AwtPrintControl::getSelectID != NULL);
     DASSERT(AwtPrintControl::getPrintToFileEnabledID != NULL);
+    DASSERT(AwtPrintControl::setJobAttributesID != NULL);
 
 
     CATCH_BAD_ALLOC;
--- a/src/windows/native/sun/windows/awt_PrintControl.h	Tue Aug 27 17:50:55 2013 +0800
+++ b/src/windows/native/sun/windows/awt_PrintControl.h	Thu Sep 12 01:50:51 2013 -0700
@@ -47,7 +47,6 @@
     static jmethodID setDevmodeID;
     static jmethodID getDevnamesID;
     static jmethodID setDevnamesID;
-
     static jmethodID getWin32MediaID;
     static jmethodID setWin32MediaID;
     static jmethodID getWin32MediaTrayID;
@@ -73,6 +72,7 @@
     static jmethodID setNativeAttID;
     static jmethodID setRangeCopiesID;
     static jmethodID setResID;
+    static jmethodID setJobAttributesID;
 
     static void initIDs(JNIEnv *env, jclass cls);
     static BOOL FindPrinter(jstring printerName, LPBYTE pPrinterEnum,
--- a/src/windows/native/sun/windows/awt_PrintJob.cpp	Tue Aug 27 17:50:55 2013 +0800
+++ b/src/windows/native/sun/windows/awt_PrintJob.cpp	Thu Sep 12 01:50:51 2013 -0700
@@ -329,6 +329,156 @@
 static int embolden(int currentWeight);
 static BOOL getPrintableArea(HDC pdc, HANDLE hDevMode, RectDouble *margin);
 
+
+
+/************************************************************************
+ * DocumentProperties native support
+ */
+
+/* Values must match those defined in WPrinterJob.java */
+static const DWORD SET_COLOR = 0x00000200;
+static const DWORD SET_ORIENTATION = 0x00004000;
+static const DWORD SET_COLLATED    = 0x00008000;
+static const DWORD SET_DUP_VERTICAL = 0x00000010;
+static const DWORD SET_DUP_HORIZONTAL = 0x00000020;
+static const DWORD SET_RES_HIGH = 0x00000040;
+static const DWORD SET_RES_LOW = 0x00000080;
+
+/*
+ * Copy DEVMODE state back into JobAttributes.
+ */
+
+static void UpdateJobAttributes(JNIEnv *env,
+                                jobject wJob,
+                                jobject attrSet,
+                                DEVMODE *devmode) {
+
+    DWORD dmValues = 0;
+    int xRes = 0, yRes = 0;
+
+    if (devmode->dmFields & DM_COLOR) {
+        if (devmode->dmColor == DMCOLOR_COLOR) {
+            dmValues |= SET_COLOR;
+        }
+    }
+
+    if (devmode->dmFields & DM_ORIENTATION) {
+        if (devmode->dmOrientation == DMORIENT_LANDSCAPE) {
+            dmValues |= SET_ORIENTATION;
+        }
+    }
+
+    if (devmode->dmFields & DM_COLLATE &&
+        devmode->dmCollate == DMCOLLATE_TRUE) {
+            dmValues |= SET_COLLATED;
+    }
+
+    if (devmode->dmFields & DM_PRINTQUALITY) {
+        /* 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 (devmode->dmPrintQuality < 0) {
+            if (devmode->dmPrintQuality == DMRES_HIGH) {
+                dmValues |= SET_RES_HIGH;
+            } else if ((devmode->dmPrintQuality == DMRES_LOW) ||
+                       (devmode->dmPrintQuality == DMRES_DRAFT)) {
+                dmValues |= SET_RES_LOW;
+            }
+            /* else if (devmode->dmPrintQuality == DMRES_MEDIUM)
+             * will set to NORMAL.
+             */
+        } else {
+            xRes = devmode->dmPrintQuality;
+            yRes = (devmode->dmFields & DM_YRESOLUTION) ?
+                devmode->dmYResolution : devmode->dmPrintQuality;
+        }
+    }
+
+    if (devmode->dmFields & DM_DUPLEX) {
+        if (devmode->dmDuplex == DMDUP_HORIZONTAL) {
+            dmValues |= SET_DUP_HORIZONTAL;
+        } else if (devmode->dmDuplex == DMDUP_VERTICAL) {
+            dmValues |= SET_DUP_VERTICAL;
+        }
+    }
+
+    env->CallVoidMethod(wJob, AwtPrintControl::setJobAttributesID, attrSet,
+                        devmode->dmFields, dmValues, devmode->dmCopies,
+                        devmode->dmPaperSize, devmode->dmPaperWidth,
+                        devmode->dmPaperLength, devmode->dmDefaultSource,
+                        xRes, yRes);
+
+}
+
+JNIEXPORT jboolean JNICALL
+Java_sun_awt_windows_WPrinterJob_showDocProperties(JNIEnv *env,
+                                                   jobject wJob,
+                                                   jlong hWndParent,
+                                                   jobject attrSet,
+                                                   jint dmFields,
+                                                   jshort copies,
+                                                   jshort collate,
+                                                   jshort color,
+                                                   jshort duplex,
+                                                   jshort orient,
+                                                   jshort paper,
+                                                   jshort bin,
+                                                   jshort xres_quality,
+                                                   jshort yres)
+{
+    TRY;
+
+    HGLOBAL hDevMode = AwtPrintControl::getPrintHDMode(env, wJob);
+    HGLOBAL hDevNames = AwtPrintControl::getPrintHDName(env, wJob);
+    DEVMODE *devmode = NULL;
+    DEVNAMES *devnames = NULL;
+    LONG rval = IDCANCEL;
+    jboolean ret = JNI_FALSE;
+
+    if (hDevMode != NULL && hDevNames != NULL) {
+        devmode = (DEVMODE *)::GlobalLock(hDevMode);
+        devnames = (DEVNAMES *)::GlobalLock(hDevNames);
+
+        LPTSTR lpdevnames = (LPTSTR)devnames;
+        // No need to call _tcsdup as we won't unlock until we are done.
+        LPTSTR printerName = lpdevnames+devnames->wDeviceOffset;
+        LPTSTR portName = lpdevnames+devnames->wOutputOffset;
+
+        HANDLE hPrinter;
+        if (::OpenPrinter(printerName, &hPrinter, NULL) == TRUE) {
+            devmode->dmFields |= dmFields;
+            devmode->dmCopies = copies;
+            devmode->dmCollate = collate;
+            devmode->dmColor = color;
+            devmode->dmDuplex = duplex;
+            devmode->dmOrientation = orient;
+            devmode->dmPrintQuality = xres_quality;
+            devmode->dmYResolution = yres;
+            devmode->dmPaperSize = paper;
+            devmode->dmDefaultSource = bin;
+
+            rval = ::DocumentProperties((HWND)hWndParent,
+                           hPrinter, printerName, devmode, devmode,
+                           DM_IN_BUFFER | DM_OUT_BUFFER | DM_IN_PROMPT);
+            if (rval == IDOK) {
+                UpdateJobAttributes(env, wJob, attrSet, devmode);
+                ret = JNI_TRUE;
+            }
+            VERIFY(::ClosePrinter(hPrinter));
+        }
+        ::GlobalUnlock(hDevNames);
+        ::GlobalUnlock(hDevMode);
+    }
+
+    return ret;
+
+    CATCH_BAD_ALLOC_RET(0);
+}
+
 /************************************************************************
  * WPageDialog native methods
  */
@@ -732,7 +882,6 @@
         memset(&pd, 0, sizeof(PRINTDLG));
         pd.lStructSize = sizeof(PRINTDLG);
         pd.Flags = PD_RETURNDEFAULT | PD_RETURNDC;
-
         if (::PrintDlg(&pd)) {
             printDC = pd.hDC;
             hDevMode = pd.hDevMode;
@@ -792,8 +941,19 @@
     jint imgPixelWid = GetDeviceCaps(printDC, HORZRES);
     jint imgPixelHgt = GetDeviceCaps(printDC, VERTRES);
 
+    // The DC may be obtained when we first selected the printer as a
+    // result of a call to setNativePrintService.
+    // If the Devmode was obtained later on from the DocumentProperties dialog
+    // the DC won't have been updated and its settings may be for PORTRAIT.
+    // This may happen in other cases too, but was observed for the above.
+    // To get a DC compatible with this devmode we should really call
+    // CreateDC() again to get a DC for the devmode we are using.
+    // The changes for that are a lot more risk, so to minimise that
+    // risk, assume its not LANDSCAPE unless width > height, even if the
+    // devmode says its LANDSCAPE.
     // if the values were obtained from a rotated device, swap.
-    if (getOrientationFromDevMode2(hDevMode) == DMORIENT_LANDSCAPE) {
+    if ((getOrientationFromDevMode2(hDevMode) == DMORIENT_LANDSCAPE) &&
+        (imgPixelWid > imgPixelHgt)) {
       jint tmp;
       tmp = xPixelRes;
       xPixelRes = yPixelRes;
@@ -941,6 +1101,9 @@
             setBooleanField(env, self, DRIVER_COLLATE_STR, JNI_FALSE);
         }
 
+        if (dmFields & DM_COPIES) {
+            setBooleanField(env, self, DRIVER_COPIES_STR, JNI_TRUE);
+        }
     }
 
     CATCH_BAD_ALLOC;