[sane.ds 3/4] Add support for ICAP_SUPPORTEDSIZES, enabling rational sizing for scans.

Jeremy White jwhite at codeweavers.com
Thu Mar 5 17:14:08 CST 2009


---
 dlls/sane.ds/capability.c |  238 ++++++++++++++++++++++++++++++++++++++++++++-
 dlls/sane.ds/options.c    |   13 +++
 dlls/sane.ds/sane_i.h     |    1 +
 dlls/twain_32/tests/dsm.c |  113 +++++++++++++++++++++
 4 files changed, 364 insertions(+), 1 deletions(-)

diff --git a/dlls/sane.ds/capability.c b/dlls/sane.ds/capability.c
index 07c8899..6ec75c8 100644
--- a/dlls/sane.ds/capability.c
+++ b/dlls/sane.ds/capability.c
@@ -23,6 +23,7 @@
 
 #include <stdarg.h>
 #include <stdio.h>
+#include <stdlib.h>
 #define __USE_ISOC9X 1
 #define __USE_ISOC99 1
 #include <math.h>
@@ -31,6 +32,7 @@
 #include "winbase.h"
 #include "twain.h"
 #include "sane_i.h"
+#include "winnls.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(twain);
@@ -170,7 +172,7 @@ static TW_UINT16 TWAIN_GetSupportedCaps(pTW_CAPABILITY pCapability)
     static const UINT16 supported_caps[] = { CAP_SUPPORTEDCAPS, CAP_XFERCOUNT, CAP_UICONTROLLABLE,
                     CAP_AUTOFEED, CAP_FEEDERENABLED,
                     ICAP_XFERMECH, ICAP_PIXELTYPE, ICAP_UNITS, ICAP_BITDEPTH, ICAP_COMPRESSION, ICAP_PIXELFLAVOR,
-                    ICAP_XRESOLUTION, ICAP_YRESOLUTION, ICAP_PHYSICALHEIGHT, ICAP_PHYSICALWIDTH };
+                    ICAP_XRESOLUTION, ICAP_YRESOLUTION, ICAP_PHYSICALHEIGHT, ICAP_PHYSICALWIDTH, ICAP_SUPPORTEDSIZES };
 
     pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ARRAY, ItemList[sizeof(supported_caps)] ));
     pCapability->ConType = TWON_ARRAY;
@@ -817,6 +819,229 @@ static TW_UINT16 SANE_ICAPPixelFlavor (pTW_CAPABILITY pCapability, TW_UINT16 act
     return twCC;
 }
 
+#ifdef SONAME_LIBSANE
+static TW_UINT16 get_width_height(double *width, double *height, BOOL max)
+{
+    SANE_Status status;
+
+    SANE_Fixed tlx_current, tlx_min, tlx_max;
+    SANE_Fixed tly_current, tly_min, tly_max;
+    SANE_Fixed brx_current, brx_min, brx_max;
+    SANE_Fixed bry_current, bry_min, bry_max;
+
+    status = sane_option_probe_scan_area(activeDS.deviceHandle, "tl-x", &tlx_current, NULL, &tlx_min, &tlx_max, NULL);
+    if (status != SANE_STATUS_GOOD)
+        return sane_status_to_twcc(status);
+
+    status = sane_option_probe_scan_area(activeDS.deviceHandle, "tl-y", &tly_current, NULL, &tly_min, &tly_max, NULL);
+    if (status != SANE_STATUS_GOOD)
+        return sane_status_to_twcc(status);
+
+    status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-x", &brx_current, NULL, &brx_min, &brx_max, NULL);
+    if (status != SANE_STATUS_GOOD)
+        return sane_status_to_twcc(status);
+
+    status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-y", &bry_current, NULL, &bry_min, &bry_max, NULL);
+    if (status != SANE_STATUS_GOOD)
+        return sane_status_to_twcc(status);
+
+    if (max)
+        *width = SANE_UNFIX(brx_max) - SANE_UNFIX(tlx_min);
+    else
+        *width = SANE_UNFIX(brx_current) - SANE_UNFIX(tlx_current);
+
+    if (max)
+        *height = SANE_UNFIX(bry_max) - SANE_UNFIX(tly_min);
+    else
+        *height = SANE_UNFIX(bry_current) - SANE_UNFIX(tly_current);
+
+    return(TWCC_SUCCESS);
+}
+
+static TW_UINT16 set_one_coord(const char *name, double coord)
+{
+    SANE_Status status;
+    status = sane_option_set_fixed(activeDS.deviceHandle, name, SANE_FIX(coord), NULL);
+    if (status != SANE_STATUS_GOOD)
+        return sane_status_to_twcc(status);
+    return TWCC_SUCCESS;
+}
+
+static TW_UINT16 set_width_height(double width, double height)
+{
+    TW_UINT16 rc = TWCC_SUCCESS;
+    rc = set_one_coord("tl-x", 0);
+    if (rc != TWCC_SUCCESS)
+        return rc;
+    rc = set_one_coord("br-x", width);
+    if (rc != TWCC_SUCCESS)
+        return rc;
+    rc = set_one_coord("tl-y", 0);
+    if (rc != TWCC_SUCCESS)
+        return rc;
+    rc = set_one_coord("br-y", height);
+
+    return rc;
+}
+
+typedef struct
+{
+    TW_UINT32 size;
+    double x;
+    double y;
+} supported_size_t;
+
+static const supported_size_t supported_sizes[] =
+{
+    { TWSS_NONE,        0,      0       },
+    { TWSS_A4,          210,    297     },
+    { TWSS_JISB5,       182,    257     },
+    { TWSS_USLETTER,    215.9,  279.4   },
+    { TWSS_USLEGAL,     215.9,  355.6   },
+    { TWSS_A5,          148,    210     },
+    { TWSS_B4,          250,    353     },
+    { TWSS_B6,          125,    176     },
+    { TWSS_USLEDGER,    215.9,  431.8   },
+    { TWSS_USEXECUTIVE, 184.15, 266.7   },
+    { TWSS_A3,          297,    420     },
+};
+#define SUPPORTED_SIZE_COUNT (sizeof(supported_sizes) / sizeof(supported_sizes[0]))
+
+static TW_UINT16 get_default_paper_size(const supported_size_t *s, int n)
+{
+    char paper[64];
+    int rc;
+    int defsize = -1;
+    double width, height;
+    int i;
+    rc = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IPAPERSIZE, paper, sizeof(paper));
+    if (rc > 0)
+        switch (atoi(paper))
+        {
+            case 1:
+                defsize = TWSS_USLETTER;
+                break;
+            case 5:
+                defsize = TWSS_USLEGAL;
+                break;
+            case 8:
+                defsize = TWSS_A3;
+                break;
+            case 9:
+                defsize = TWSS_A4;
+                break;
+        }
+
+    if (defsize == -1)
+        return TWSS_NONE;
+
+    if (get_width_height(&width, &height, TRUE) != TWCC_SUCCESS)
+        return TWSS_NONE;
+
+    for (i = 0; i < n; i++)
+        if (s[i].size == defsize)
+        {
+            /* Sane's use of integers to store floats is a hair lossy; deal with it */
+            if (s[i].x > (width + .01) || s[i].y > (height + 0.01))
+                return TWSS_NONE;
+            else
+                return s[i].size;
+        }
+
+    return TWSS_NONE;
+}
+
+static TW_UINT16 get_current_paper_size(const supported_size_t *s, int n)
+{
+    int i;
+    double width, height;
+    double xdelta, ydelta;
+
+    if (get_width_height(&width, &height, FALSE) != TWCC_SUCCESS)
+        return TWSS_NONE;
+
+    for (i = 0; i < n; i++)
+    {
+        /* Sane's use of integers to store floats results
+         * in a very small error; cope with that */
+        xdelta = s[i].x - width;
+        ydelta = s[i].y - height;
+        if (xdelta < 0.01 && xdelta > -0.01 &&
+            ydelta < 0.01 && ydelta > -0.01)
+            return s[i].size;
+    }
+
+    return TWSS_NONE;
+}
+#endif
+
+/* ICAP_SUPPORTEDSIZES */
+static TW_UINT16 SANE_ICAPSupportedSizes (pTW_CAPABILITY pCapability, TW_UINT16 action)
+{
+    TW_UINT16 twCC = TWCC_BADCAP;
+#ifdef SONAME_LIBSANE
+
+    static TW_UINT32 possible_values[SUPPORTED_SIZE_COUNT];
+    int i;
+    TW_UINT32 val;
+    TW_UINT16 default_size = get_default_paper_size(supported_sizes, SUPPORTED_SIZE_COUNT);
+    TW_UINT16 current_size = get_current_paper_size(supported_sizes, SUPPORTED_SIZE_COUNT);
+
+    TRACE("ICAP_SUPPORTEDSIZES\n");
+
+    switch (action)
+    {
+        case MSG_QUERYSUPPORT:
+            twCC = set_onevalue(pCapability, TWTY_INT32,
+                    TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
+            break;
+
+        case MSG_GET:
+            for (i = 0; i < sizeof(supported_sizes) / sizeof(supported_sizes[0]); i++)
+                possible_values[i] = supported_sizes[i].size;
+            twCC = msg_get_enum(pCapability, possible_values, sizeof(possible_values) / sizeof(possible_values[0]),
+                    TWTY_UINT16, current_size, default_size);
+            WARN("Partial Stub:  our supported size selection is a bit thin.\n");
+            break;
+
+        case MSG_SET:
+            twCC = msg_set(pCapability, &val);
+            if (twCC == TWCC_SUCCESS)
+                for (i = 1; i < SUPPORTED_SIZE_COUNT; i++)
+                    if (supported_sizes[i].size == val)
+                        return set_width_height(supported_sizes[i].x, supported_sizes[i].y);
+
+            ERR("Unsupported size %d\n", val);
+            twCC = TWCC_BADCAP;
+            break;
+
+        case MSG_GETDEFAULT:
+            twCC = set_onevalue(pCapability, TWTY_UINT16, default_size);
+            break;
+
+        case MSG_RESET:
+            twCC = TWCC_BADCAP;
+            for (i = 1; i < SUPPORTED_SIZE_COUNT; i++)
+                if (supported_sizes[i].size == default_size)
+                {
+                    twCC = set_width_height(supported_sizes[i].x, supported_sizes[i].y);
+                    break;
+                }
+            if (twCC != TWCC_SUCCESS)
+                return twCC;
+
+            /* .. fall through intentional .. */
+
+        case MSG_GETCURRENT:
+            twCC = set_onevalue(pCapability, TWTY_UINT16, current_size);
+            break;
+    }
+
+#undef SUPPORTED_SIZE_COUNT
+#endif
+    return twCC;
+}
+
 /* CAP_AUTOFEED */
 static TW_UINT16 SANE_CAPAutofeed (pTW_CAPABILITY pCapability, TW_UINT16 action)
 {
@@ -1029,6 +1254,10 @@ TW_UINT16 SANE_SaneCapability (pTW_CAPABILITY pCapability, TW_UINT16 action)
             twCC = SANE_ICAPPhysical(pCapability, action, pCapability->Cap);
             break;
 
+        case ICAP_SUPPORTEDSIZES:
+            twCC = SANE_ICAPSupportedSizes (pCapability, action);
+            break;
+
     }
 
     /* Twain specifies that you should return a 0 in response to QUERYSUPPORT,
@@ -1060,5 +1289,12 @@ TW_UINT16 SANE_SaneSetDefaults (void)
     if (SANE_SaneCapability(&cap, MSG_RESET) == TWCC_SUCCESS)
         GlobalFree(cap.hContainer);
 
+    memset(&cap, 0, sizeof(cap));
+    cap.Cap = ICAP_SUPPORTEDSIZES;
+    cap.ConType = TWON_DONTCARE16;
+
+    if (SANE_SaneCapability(&cap, MSG_RESET) == TWCC_SUCCESS)
+        GlobalFree(cap.hContainer);
+
    return TWCC_SUCCESS;
 }
diff --git a/dlls/sane.ds/options.c b/dlls/sane.ds/options.c
index 3fccb45..44cd34e 100644
--- a/dlls/sane.ds/options.c
+++ b/dlls/sane.ds/options.c
@@ -111,6 +111,19 @@ SANE_Status sane_option_set_bool(SANE_Handle h, const char *option_name, SANE_Bo
     return psane_control_option(h, optno, SANE_ACTION_SET_VALUE, (void *) &val, status);
 }
 
+SANE_Status sane_option_set_fixed(SANE_Handle h, const char *option_name, SANE_Fixed val, SANE_Int *status)
+{
+    SANE_Status rc;
+    int optno;
+    const SANE_Option_Descriptor *opt;
+
+    rc = sane_find_option(h, option_name, &opt, &optno, SANE_TYPE_FIXED);
+    if (rc != SANE_STATUS_GOOD)
+        return rc;
+
+    return psane_control_option(h, optno, SANE_ACTION_SET_VALUE, (void *) &val, status);
+}
+
 SANE_Status sane_option_get_str(SANE_Handle h, const char *option_name, SANE_String val, size_t len, SANE_Int *status)
 {
     SANE_Status rc;
diff --git a/dlls/sane.ds/sane_i.h b/dlls/sane.ds/sane_i.h
index be4bd8f..72b4b79 100644
--- a/dlls/sane.ds/sane_i.h
+++ b/dlls/sane.ds/sane_i.h
@@ -233,6 +233,7 @@ SANE_Status sane_option_probe_scan_area(SANE_Handle h, const char *option_name,
                                         SANE_Unit *unit, SANE_Fixed *min, SANE_Fixed *max, SANE_Fixed *quant);
 SANE_Status sane_option_get_bool(SANE_Handle h, const char *option_name, SANE_Bool *val, SANE_Int *status);
 SANE_Status sane_option_set_bool(SANE_Handle h, const char *option_name, SANE_Bool val, SANE_Int *status);
+SANE_Status sane_option_set_fixed(SANE_Handle h, const char *option_name, SANE_Fixed val, SANE_Int *status);
 #endif
 
 
diff --git a/dlls/twain_32/tests/dsm.c b/dlls/twain_32/tests/dsm.c
index 17c106b..7c6cc4e 100644
--- a/dlls/twain_32/tests/dsm.c
+++ b/dlls/twain_32/tests/dsm.c
@@ -473,6 +473,116 @@ static void test_physical(TW_IDENTITY *appid, TW_IDENTITY *source, TW_UINT16 cap
 
 }
 
+static void test_supported_sizes(TW_IDENTITY *appid, TW_IDENTITY *source, TW_INT32 minimum_support)
+{
+    TW_UINT16 rc;
+    TW_STATUS status;
+    TW_CAPABILITY cap;
+    TW_UINT32 val;
+    TW_UINT16 type;
+    TW_INT32 actual_support;
+    TW_UINT32 orig_value;
+    TW_UINT32 default_value;
+    TW_UINT32 new_value = TWSS_NONE;
+
+
+    memset(&cap, 0, sizeof(cap));
+    cap.Cap = ICAP_SUPPORTEDSIZES;
+    cap.ConType = TWON_DONTCARE16;
+
+    rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_QUERYSUPPORT, &cap);
+    get_condition_code(appid, source, &status);
+    ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
+            "Error [rc %d|cc %d] doing MSG_QUERYSUPPORT for ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode);
+    if (rc != TWRC_SUCCESS)
+        return;
+    ok(get_onevalue(cap.hContainer, (TW_UINT32 *) &actual_support, NULL), "Returned cap.hContainer invalid for QuerySupport on ICAP_SUPPORTEDSIZES\n");
+    ok((actual_support & minimum_support) == minimum_support,
+            "Error:  minimum support 0x%x for ICAP_SUPPORTEDSIZES, got 0x%x\n", minimum_support, actual_support);
+
+    if (actual_support & TWQC_GETCURRENT)
+    {
+        memset(&cap, 0, sizeof(cap));
+        cap.Cap = ICAP_SUPPORTEDSIZES;
+        cap.ConType = TWON_DONTCARE16;
+
+        rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETCURRENT, &cap);
+        get_condition_code(appid, source, &status);
+        ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
+                "Error [rc %d|cc %d] doing MSG_GETCURRENT for ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode);
+        if (rc == TWRC_SUCCESS)
+        {
+            get_onevalue(cap.hContainer, &val, &type);
+            ok(type == TWTY_UINT16, "GETCURRENT for ICAP_SUPPORTEDSIZES is not type UINT16, is type %d\n", type);
+            trace("Current size is %d\n", val);
+            GlobalFree(cap.hContainer);
+            orig_value = val;
+        }
+    }
+
+    if (actual_support & TWQC_GETDEFAULT)
+    {
+        memset(&cap, 0, sizeof(cap));
+        cap.Cap = ICAP_SUPPORTEDSIZES;
+        cap.ConType = TWON_DONTCARE16;
+
+        rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETDEFAULT, &cap);
+        get_condition_code(appid, source, &status);
+        ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
+                "Error [rc %d|cc %d] doing MSG_GETDEFAULT for ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode);
+        if (rc == TWRC_SUCCESS)
+        {
+            get_onevalue(cap.hContainer, &val, &type);
+            ok(type == TWTY_UINT16, "GETDEFAULT for PHYSICALXXX is not type TWTY_UINT16, is type %d\n", type);
+            trace("Default size is %d\n", val);
+            GlobalFree(cap.hContainer);
+            default_value = val;
+        }
+    }
+
+    if (actual_support & TWQC_GET)
+    {
+        memset(&cap, 0, sizeof(cap));
+        cap.Cap = ICAP_SUPPORTEDSIZES;
+        cap.ConType = TWON_DONTCARE16;
+
+        rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GET, &cap);
+        get_condition_code(appid, source, &status);
+        ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
+                "Error [rc %d|cc %d] doing MSG_GET for ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode);
+        check_get(&cap, actual_support, orig_value, default_value, &new_value);
+    }
+
+    if (actual_support & TWQC_SET)
+    {
+        memset(&cap, 0, sizeof(cap));
+        cap.Cap = ICAP_SUPPORTEDSIZES;
+        cap.ConType = TWON_ONEVALUE;
+        cap.hContainer = alloc_and_set_onevalue(new_value, TWTY_UINT16);
+
+        rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_SET, &cap);
+        get_condition_code(appid, source, &status);
+        ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
+                "Error [rc %d|cc %d] doing MSG_SET for ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode);
+        GlobalFree(cap.hContainer);
+
+    }
+
+    if (actual_support & TWQC_RESET)
+    {
+        memset(&cap, 0, sizeof(cap));
+        cap.Cap = ICAP_SUPPORTEDSIZES;
+        cap.ConType = TWON_DONTCARE16;
+
+        rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_RESET, &cap);
+        get_condition_code(appid, source, &status);
+        ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
+                "Error [rc %d|cc %d] doing MSG_RESET for ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode);
+        if (rc == TWRC_SUCCESS)
+            GlobalFree(cap.hContainer);
+    }
+}
+
 
 static void test_single_source(TW_IDENTITY *appid, TW_IDENTITY *source)
 {
@@ -586,6 +696,9 @@ static void test_single_source(TW_IDENTITY *appid, TW_IDENTITY *source)
         if (capabilities[CAP_FEEDERENABLED])
             test_onevalue_cap(appid, source, CAP_FEEDERENABLED, TWTY_BOOL,
                 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
+        if (capabilities[ICAP_SUPPORTEDSIZES])
+            test_supported_sizes(appid, source,
+                TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
 
     }
 }



More information about the wine-patches mailing list