8048328: CUPS Printing does not report supported printer resolutions.
authorprr
Fri, 11 Jul 2014 11:12:59 -0700
changeset 25774 21b78da4b2df
parent 25773 07cae3ef5f57
child 25775 6a6fd62bc6d5
8048328: CUPS Printing does not report supported printer resolutions. Reviewed-by: bae, jgodinez
jdk/make/mapfiles/libawt/mapfile-mawt-vers
jdk/make/mapfiles/libawt_headless/mapfile-vers
jdk/make/mapfiles/libawt_xawt/mapfile-vers
jdk/src/share/classes/sun/print/PSPrinterJob.java
jdk/src/share/classes/sun/print/RasterPrinterJob.java
jdk/src/solaris/classes/sun/print/CUPSPrinter.java
jdk/src/solaris/classes/sun/print/IPPPrintService.java
jdk/src/solaris/native/sun/awt/CUPSfuncs.c
jdk/test/javax/print/attribute/PrintResAttr.java
--- a/jdk/make/mapfiles/libawt/mapfile-mawt-vers	Fri Jul 11 18:46:47 2014 +0400
+++ b/jdk/make/mapfiles/libawt/mapfile-mawt-vers	Fri Jul 11 11:12:59 2014 -0700
@@ -204,6 +204,7 @@
                 Java_sun_print_CUPSPrinter_canConnect;
                 Java_sun_print_CUPSPrinter_getMedia;
                 Java_sun_print_CUPSPrinter_getPageSizes;
+                Java_sun_print_CUPSPrinter_getResolutions;
 
                 Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1arrow;
                 Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1box;
--- a/jdk/make/mapfiles/libawt_headless/mapfile-vers	Fri Jul 11 18:46:47 2014 +0400
+++ b/jdk/make/mapfiles/libawt_headless/mapfile-vers	Fri Jul 11 11:12:59 2014 -0700
@@ -76,6 +76,7 @@
 		Java_sun_print_CUPSPrinter_canConnect;
 		Java_sun_print_CUPSPrinter_getMedia;
 		Java_sun_print_CUPSPrinter_getPageSizes;
+		Java_sun_print_CUPSPrinter_getResolutions;
 
 		# libfontmanager entry points
 		AWTIsHeadless;
--- a/jdk/make/mapfiles/libawt_xawt/mapfile-vers	Fri Jul 11 18:46:47 2014 +0400
+++ b/jdk/make/mapfiles/libawt_xawt/mapfile-vers	Fri Jul 11 11:12:59 2014 -0700
@@ -442,6 +442,7 @@
 	Java_sun_print_CUPSPrinter_canConnect;
 	Java_sun_print_CUPSPrinter_getMedia;
 	Java_sun_print_CUPSPrinter_getPageSizes;
+	Java_sun_print_CUPSPrinter_getResolutions;
 
         awt_GetDrawingSurface;
         awt_FreeDrawingSurface;
--- a/jdk/src/share/classes/sun/print/PSPrinterJob.java	Fri Jul 11 18:46:47 2014 +0400
+++ b/jdk/src/share/classes/sun/print/PSPrinterJob.java	Fri Jul 11 11:12:59 2014 -0700
@@ -168,13 +168,6 @@
     private static final String IMAGE_STR =     " string /imStr exch def";
     private static final String IMAGE_RESTORE = "imSave restore";
 
-    private static final String COORD_PREP =    " 0 exch translate "
-                                              + "1 -1 scale"
-                                              + "[72 " + PS_XRES + " div "
-                                              + "0 0 "
-                                              + "72 " + PS_YRES + " div "
-                                              + "0 0]concat";
-
     private static final String SetFontName = "F";
 
     private static final String DrawStringName = "S";
@@ -275,6 +268,9 @@
 
    private AffineTransform mLastTransform;
 
+   private double xres = PS_XRES;
+   private double yres = PS_XRES;
+
    /* non-null if printing EPS for Java Plugin */
    private EPSPrinter epsPrinter = null;
 
@@ -796,6 +792,15 @@
         }
     }
 
+    private String getCoordPrep() {
+        return " 0 exch translate "
+             + "1 -1 scale"
+             + "[72 " + getXRes() + " div "
+             + "0 0 "
+             + "72 " + getYRes() + " div "
+             + "0 0]concat";
+    }
+
     /**
      * The RasterPrintJob super class calls this method
      * at the start of each page.
@@ -852,7 +857,7 @@
             mPSStream.println(" >> setpagedevice");
         }
         mPSStream.println(PAGE_SAVE);
-        mPSStream.println(paperHeight + COORD_PREP);
+        mPSStream.println(paperHeight + getCoordPrep());
     }
 
     /**
@@ -1493,14 +1498,22 @@
      * to be rendered.
      */
     protected double getXRes() {
-        return PS_XRES;
+        return xres;
     }
     /**
      * Return the y resolution of the coordinates
      * to be rendered.
      */
     protected double getYRes() {
-        return PS_YRES;
+        return yres;
+    }
+
+    /**
+     * Set the resolution at which to print.
+     */
+    protected void setXYRes(double x, double y) {
+        xres = x;
+        yres = y;
     }
 
     /**
--- a/jdk/src/share/classes/sun/print/RasterPrinterJob.java	Fri Jul 11 18:46:47 2014 +0400
+++ b/jdk/src/share/classes/sun/print/RasterPrinterJob.java	Fri Jul 11 11:12:59 2014 -0700
@@ -72,6 +72,7 @@
 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.Chromaticity;
 import javax.print.attribute.standard.Copies;
@@ -86,6 +87,7 @@
 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;
@@ -283,6 +285,7 @@
     private String jobNameAttr;
     private String userNameAttr;
     private PageRanges pageRangesAttr;
+    protected PrinterResolution printerResAttr;
     protected Sides sidesAttr;
     protected String destinationAttr;
     protected boolean noJobSheet = false;
@@ -1064,6 +1067,14 @@
                                           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()
      */
@@ -1072,6 +1083,7 @@
         /*  reset all values to defaults */
         setCollated(false);
         sidesAttr = null;
+        printerResAttr = null;
         pageRangesAttr = null;
         copiesAttr = 0;
         jobNameAttr = null;
@@ -1117,6 +1129,18 @@
             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;
--- a/jdk/src/solaris/classes/sun/print/CUPSPrinter.java	Fri Jul 11 18:46:47 2014 +0400
+++ b/jdk/src/solaris/classes/sun/print/CUPSPrinter.java	Fri Jul 11 11:12:59 2014 -0700
@@ -39,6 +39,7 @@
 import javax.print.attribute.standard.MediaSize;
 import javax.print.attribute.standard.MediaTray;
 import javax.print.attribute.standard.MediaPrintableArea;
+import javax.print.attribute.standard.PrinterResolution;
 import javax.print.attribute.Size2DSyntax;
 import javax.print.attribute.Attribute;
 import javax.print.attribute.EnumSyntax;
@@ -57,6 +58,8 @@
     // CUPS does not support multi-threading.
     private static synchronized native String[] getMedia(String printer);
     private static synchronized native float[] getPageSizes(String printer);
+    private static synchronized native void
+        getResolutions(String printer, ArrayList<Integer> resolutionList);
     //public static boolean useIPPMedia = false; will be used later
 
     private MediaPrintableArea[] cupsMediaPrintables;
@@ -68,6 +71,7 @@
     public  int nTrays = 0;
     private  String[] media;
     private  float[] pageSizes;
+    int[]   resolutionsArray;
     private String printer;
 
     private static boolean libFound;
@@ -119,6 +123,12 @@
                 nTrays = media.length/2-nPageSizes;
                 assert (nTrays >= 0);
             }
+            ArrayList<Integer> resolutionList = new ArrayList<>();
+            getResolutions(printer, resolutionList);
+            resolutionsArray = new int[resolutionList.size()];
+            for (int i=0; i < resolutionList.size(); i++) {
+                resolutionsArray[i] = resolutionList.get(i);
+            }
         }
     }
 
@@ -160,6 +170,12 @@
         return cupsMediaTrays;
     }
 
+    /**
+     * return the raw packed array of supported printer resolutions.
+     */
+    int[] getRawResolutions() {
+        return resolutionsArray;
+    }
 
     /**
      * Initialize media by translating PPD info to PrintService attributes.
--- a/jdk/src/solaris/classes/sun/print/IPPPrintService.java	Fri Jul 11 18:46:47 2014 +0400
+++ b/jdk/src/solaris/classes/sun/print/IPPPrintService.java	Fri Jul 11 11:12:59 2014 -0700
@@ -96,6 +96,8 @@
     private MediaSizeName[] mediaSizeNames;
     private CustomMediaSizeName[] customMediaSizeNames;
     private int defaultMediaIndex;
+    private int[] rawResolutions = null;
+    private PrinterResolution[] printerResolutions = null;
     private boolean isCupsPrinter;
     private boolean init;
     private Boolean isPS;
@@ -414,6 +416,7 @@
                     mediaTrays = cps.getMediaTrays();
                     customMediaSizeNames = cps.getCustomMediaSizeNames();
                     defaultMediaIndex = cps.getDefaultMediaIndex();
+                    rawResolutions = cps.getRawResolutions();
                     urlConnection.disconnect();
                     init = true;
                     return;
@@ -765,6 +768,15 @@
                     return sidesSup;
                 }
             }
+        } else if (category == PrinterResolution.class) {
+            PrinterResolution[] supportedRes = getPrintResolutions();
+            if (supportedRes == null) {
+                return null;
+            }
+            PrinterResolution []arr =
+                new PrinterResolution[supportedRes.length];
+            System.arraycopy(supportedRes, 0, arr, 0, supportedRes.length);
+            return arr;
         }
 
         return null;
@@ -1043,6 +1055,14 @@
         if (getAttMap != null && getAttMap.containsKey("color-supported")) {
             catList.add(Chromaticity.class);
         }
+
+        // CUPS does not report printer resolution via IPP but it
+        // may be gleaned from the PPD.
+        PrinterResolution[] supportedRes = getPrintResolutions();
+        if (supportedRes != null && (supportedRes.length > 0)) {
+            catList.add(PrinterResolution.class);
+        }
+
         supportedCats = new Class<?>[catList.size()];
         catList.toArray(supportedCats);
         return supportedCats;
@@ -1362,6 +1382,10 @@
                 }
             }
             return false;
+        } if (attr.getCategory() == PrinterResolution.class) {
+            if (attr instanceof PrinterResolution) {
+                return isSupportedResolution((PrinterResolution)attr);
+            }
         }
         return true;
     }
@@ -1523,11 +1547,48 @@
                 }
             }
             return Sides.ONE_SIDED;
+        } else if (category == PrinterResolution.class) {
+             PrinterResolution[] supportedRes = getPrintResolutions();
+             if ((supportedRes != null) && (supportedRes.length > 0)) {
+                return supportedRes[0];
+             } else {
+                 return new PrinterResolution(300, 300, PrinterResolution.DPI);
+             }
         }
 
         return null;
     }
 
+    private PrinterResolution[] getPrintResolutions() {
+        if (printerResolutions == null) {
+            if (rawResolutions == null) {
+              printerResolutions = new PrinterResolution[0];
+            } else {
+                int numRes = rawResolutions.length / 2;
+                PrinterResolution[] pres = new PrinterResolution[numRes];
+                for (int i=0; i < numRes; i++) {
+                    pres[i] =  new PrinterResolution(rawResolutions[i*2],
+                                                     rawResolutions[i*2+1],
+                                                     PrinterResolution.DPI);
+                }
+                printerResolutions = pres;
+            }
+        }
+        return printerResolutions;
+    }
+
+    private boolean isSupportedResolution(PrinterResolution res) {
+        PrinterResolution[] supportedRes = getPrintResolutions();
+        if (supportedRes != null) {
+            for (int i=0; i<supportedRes.length; i++) {
+                if (res.equals(supportedRes[i])) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     public ServiceUIFactory getServiceUIFactory() {
         return null;
     }
--- a/jdk/src/solaris/native/sun/awt/CUPSfuncs.c	Fri Jul 11 18:46:47 2014 +0400
+++ b/jdk/src/solaris/native/sun/awt/CUPSfuncs.c	Fri Jul 11 11:12:59 2014 -0700
@@ -394,3 +394,112 @@
     unlink(filename);
     return sizeArray;
 }
+
+/*
+ * Populates the supplied ArrayList<Integer> with resolutions.
+ * The first pair of elements will be the default resolution.
+ * If resolution isn't supported the list will be empty.
+ * If needed we can add a 2nd ArrayList<String> which would
+ * be populated with the corresponding UI name.
+ * PPD specifies the syntax for resolution as either "Ndpi" or "MxNdpi",
+ * eg 300dpi or 600x600dpi. The former is a shorthand where xres==yres.
+ * We will always expand to the latter as we use a single array list.
+ * Note: getMedia() and getPageSizes() both open the ppd file
+ * This is not going to scale forever so if we add anymore we
+ * should look to consolidate this.
+ */
+JNIEXPORT void JNICALL
+Java_sun_print_CUPSPrinter_getResolutions(JNIEnv *env,
+                                          jobject printObj,
+                                          jstring printer,
+                                          jobject arrayList)
+{
+    ppd_file_t *ppd = NULL;
+    ppd_option_t *resolution;
+    int defx = 0, defy = 0;
+    int resx = 0, resy = 0;
+    jclass intCls, cls;
+    jmethodID intCtr, arrListAddMID;
+    int i;
+
+    intCls = (*env)->FindClass(env, "java/lang/Integer");
+    CHECK_NULL(intCls);
+    intCtr = (*env)->GetMethodID(env, intCls, "<init>", "(I)V");
+    CHECK_NULL(intCtr);
+    cls = (*env)->FindClass(env, "java/util/ArrayList");
+    CHECK_NULL(cls);
+    arrListAddMID =
+        (*env)->GetMethodID(env, cls, "add", "(Ljava/lang/Object;)Z");
+    CHECK_NULL(arrListAddMID);
+
+    const char *name = (*env)->GetStringUTFChars(env, printer, NULL);
+    if (name == NULL) {
+        (*env)->ExceptionClear(env);
+        JNU_ThrowOutOfMemoryError(env, "Could not create printer name");
+    }
+    const char *filename;
+
+    // NOTE: cupsGetPPD returns a pointer to a filename of a temporary file.
+    // unlink() must be called to remove the file after using it.
+    filename = j2d_cupsGetPPD(name);
+    (*env)->ReleaseStringUTFChars(env, printer, name);
+    CHECK_NULL(filename);
+    if ((ppd = j2d_ppdOpenFile(filename)) == NULL) {
+        unlink(filename);
+        DPRINTF("unable to open PPD  %s\n", filename)
+    }
+    resolution = j2d_ppdFindOption(ppd, "Resolution");
+    if (resolution != NULL) {
+        int matches = sscanf(resolution->defchoice, "%dx%ddpi", &defx, &defy);
+        if (matches == 2) {
+           if (defx <= 0 || defy <= 0) {
+              defx = 0;
+              defy = 0;
+           }
+        } else {
+            matches = sscanf(resolution->defchoice, "%ddpi", &defx);
+            if (matches == 1) {
+                if (defx <= 0) {
+                   defx = 0;
+                } else {
+                   defy = defx;
+                }
+            }
+        }
+        if (defx > 0) {
+          jobject rxObj = (*env)->NewObject(env, intCls, intCtr, defx);
+          jobject ryObj = (*env)->NewObject(env, intCls, intCtr, defy);
+          (*env)->CallBooleanMethod(env, arrayList, arrListAddMID, rxObj);
+          (*env)->CallBooleanMethod(env, arrayList, arrListAddMID, ryObj);
+        }
+
+        for (i = 0; i < resolution->num_choices; i++) {
+            char *resStr = resolution->choices[i].choice;
+            int matches = sscanf(resStr, "%dx%ddpi", &resx, &resy);
+            if (matches == 2) {
+               if (resx <= 0 || resy <= 0) {
+                  resx = 0;
+                  resy = 0;
+               }
+            } else {
+                matches = sscanf(resStr, "%ddpi", &resx);
+                if (matches == 1) {
+                    if (resx <= 0) {
+                       resx = 0;
+                    } else {
+                       resy = resx;
+                    }
+                }
+            }
+            if (resx > 0 && (resx != defx || resy != defy )) {
+              jobject rxObj = (*env)->NewObject(env, intCls, intCtr, resx);
+              jobject ryObj = (*env)->NewObject(env, intCls, intCtr, resy);
+              (*env)->CallBooleanMethod(env, arrayList, arrListAddMID, rxObj);
+              (*env)->CallBooleanMethod(env, arrayList, arrListAddMID, ryObj);
+            }
+        }
+    }
+
+    j2d_ppdClose(ppd);
+    unlink(filename);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/print/attribute/PrintResAttr.java	Fri Jul 11 11:12:59 2014 -0700
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+/**
+ * @test
+ * @bug 8048328
+ * @summary CUPS Printing does not report supported printer resolutions.
+ * @run main PrintResAttr
+ */
+
+/*
+ * Since there is no guarantee you have any printers that support
+ * resolution this test can't verify that resolution is being
+ * reported when supported. But when they are it should test that
+ * the code behaves reasonably.
+ */
+import javax.print.*;
+import javax.print.attribute.*;
+import javax.print.attribute.standard.*;
+
+public class PrintResAttr {
+
+   public static void main(String args[]) throws Exception {
+
+      PrintService[] services =
+            PrintServiceLookup.lookupPrintServices(null,null);
+      for (int i=0; i<services.length; i++) {
+          if (services[i].isAttributeCategorySupported(PrinterResolution.class)) {
+              System.out.println("Testing " + services[i]);
+              PrinterResolution[] res = (PrinterResolution[])
+            services[i].getSupportedAttributeValues(PrinterResolution.class,
+                                                      null,null);
+              System.out.println("# supp res= " + res.length);
+              for (int r=0;r<res.length;r++) System.out.println(res[r]);
+          }
+      }
+   }
+}