summaryrefslogtreecommitdiff
path: root/backend/escl
diff options
context:
space:
mode:
Diffstat (limited to 'backend/escl')
-rw-r--r--backend/escl/escl.c893
-rw-r--r--backend/escl/escl.h65
-rw-r--r--backend/escl/escl_capabilities.c194
-rw-r--r--backend/escl/escl_crop.c102
-rw-r--r--backend/escl/escl_jpeg.c69
-rw-r--r--backend/escl/escl_mupdf.c256
-rw-r--r--backend/escl/escl_newjob.c115
-rw-r--r--backend/escl/escl_pdf.c223
-rw-r--r--backend/escl/escl_png.c157
-rw-r--r--backend/escl/escl_reset.c42
-rw-r--r--backend/escl/escl_scan.c48
-rw-r--r--backend/escl/escl_status.c159
-rw-r--r--backend/escl/escl_tiff.c71
13 files changed, 1877 insertions, 517 deletions
diff --git a/backend/escl/escl.c b/backend/escl/escl.c
index 8df6c5c..c40fd98 100644
--- a/backend/escl/escl.c
+++ b/backend/escl/escl.c
@@ -46,14 +46,16 @@ static int num_devices = 0;
typedef struct Handled {
struct Handled *next;
- SANE_String_Const name;
+ ESCL_Device *device;
char *result;
ESCL_ScanParam param;
SANE_Option_Descriptor opt[NUM_OPTIONS];
Option_Value val[NUM_OPTIONS];
capabilities_t *scanner;
- SANE_Range x_range;
- SANE_Range y_range;
+ SANE_Range x_range1;
+ SANE_Range x_range2;
+ SANE_Range y_range1;
+ SANE_Range y_range2;
SANE_Bool cancel;
SANE_Bool write_scan_data;
SANE_Bool decompress_scan_data;
@@ -61,6 +63,59 @@ typedef struct Handled {
SANE_Parameters ps;
} escl_sane_t;
+static ESCL_Device *
+escl_free_device(ESCL_Device *current)
+{
+ if (!current) return NULL;
+ free((void*)current->ip_address);
+ free((void*)current->model_name);
+ free((void*)current->type);
+ free(current->unix_socket);
+ free(current);
+ return NULL;
+}
+
+void
+escl_free_handler(escl_sane_t *handler)
+{
+ if (handler == NULL)
+ return;
+
+ escl_free_device(handler->device);
+ free(handler);
+}
+
+SANE_Status escl_parse_name(SANE_String_Const name, ESCL_Device *device);
+
+static SANE_Status
+escl_check_and_add_device(ESCL_Device *current)
+{
+ if(!current) {
+ DBG (10, "ESCL_Device *current us null.\n");
+ return (SANE_STATUS_NO_MEM);
+ }
+ if (!current->ip_address) {
+ DBG (10, "Ip Address allocation failure.\n");
+ return (SANE_STATUS_NO_MEM);
+ }
+ if (current->port_nb == 0) {
+ DBG (10, "No port defined.\n");
+ return (SANE_STATUS_NO_MEM);
+ }
+ if (!current->model_name) {
+ DBG (10, "Modele Name allocation failure.\n");
+ return (SANE_STATUS_NO_MEM);
+ }
+ if (!current->type) {
+ DBG (10, "Scanner Type allocation failure.\n");
+ return (SANE_STATUS_NO_MEM);
+ }
+ ++num_devices;
+ current->next = list_devices_primary;
+ list_devices_primary = current;
+ return (SANE_STATUS_GOOD);
+}
+
/**
* \fn static SANE_Status escl_add_in_list(ESCL_Device *current)
* \brief Function that adds all the element needed to my list :
@@ -72,10 +127,18 @@ typedef struct Handled {
static SANE_Status
escl_add_in_list(ESCL_Device *current)
{
- ++num_devices;
- current->next = list_devices_primary;
- list_devices_primary = current;
- return (SANE_STATUS_GOOD);
+ if(!current) {
+ DBG (10, "ESCL_Device *current us null.\n");
+ return (SANE_STATUS_NO_MEM);
+ }
+
+ if (SANE_STATUS_GOOD ==
+ escl_check_and_add_device(current)) {
+ list_devices_primary = current;
+ return (SANE_STATUS_GOOD);
+ }
+ current = escl_free_device(current);
+ return (SANE_STATUS_NO_MEM);
}
/**
@@ -89,19 +152,44 @@ escl_add_in_list(ESCL_Device *current)
SANE_Status
escl_device_add(int port_nb, const char *model_name, char *ip_address, char *type)
{
+ char tmp[PATH_MAX] = { 0 };
+ char *model = NULL;
ESCL_Device *current = NULL;
DBG (10, "escl_device_add\n");
for (current = list_devices_primary; current; current = current->next) {
- if (strcmp(current->ip_address, ip_address) == 0 && current->port_nb == port_nb
- && strcmp(current->type, type) == 0)
- return (SANE_STATUS_GOOD);
+ if (strcmp(current->ip_address, ip_address) == 0)
+ {
+ if (strcmp(current->type, type))
+ {
+ if(!strcmp(type, "_uscans._tcp") ||
+ !strcmp(type, "https"))
+ {
+ free (current->type);
+ current->type = strdup(type);
+ current->port_nb = port_nb;
+ current->https = SANE_TRUE;
+ }
+ return (SANE_STATUS_GOOD);
+ }
+ else if (current->port_nb == port_nb)
+ return (SANE_STATUS_GOOD);
+ }
+ }
+ current = (ESCL_Device*)calloc(1, sizeof(*current));
+ if (current == NULL) {
+ DBG (10, "New device allocation failure.\n");
+ return (SANE_STATUS_NO_MEM);
}
- current = malloc(sizeof(*current));
- if (current == NULL)
- return (SANE_STATUS_NO_MEM);
- memset(current, 0, sizeof(*current));
current->port_nb = port_nb;
- current->model_name = strdup(model_name);
+
+ if (strcmp(type, "_uscan._tcp") != 0 && strcmp(type, "http") != 0) {
+ snprintf(tmp, sizeof(tmp), "%s SSL", model_name);
+ current->https = SANE_TRUE;
+ } else {
+ current->https = SANE_FALSE;
+ }
+ model = (char*)(tmp[0] != 0 ? tmp : model_name);
+ current->model_name = strdup(model);
current->ip_address = strdup(ip_address);
current->type = strdup(type);
return escl_add_in_list(current);
@@ -121,13 +209,51 @@ max_string_size(const SANE_String_Const strings[])
int i = 0;
for (i = 0; strings[i]; ++i) {
- size_t size = strlen (strings[i]);
- if (size > max_size)
- max_size = size;
+ size_t size = strlen (strings[i]);
+ if (size > max_size)
+ max_size = size;
}
return (max_size + 1);
}
+static char *
+get_vendor(char *search)
+{
+ if(strcasestr(search, "Epson"))
+ return strdup("Epson");
+ else if(strcasestr(search, "Fujitsu"))
+ return strdup("Fujitsu");
+ else if(strcasestr(search, "HP"))
+ return strdup("HP");
+ else if(strcasestr(search, "Canon"))
+ return strdup("Canon");
+ else if(strcasestr(search, "Lexmark"))
+ return strdup("Lexmark");
+ else if(strcasestr(search, "Samsung"))
+ return strdup("Samsung");
+ else if(strcasestr(search, "Xerox"))
+ return strdup("Xerox");
+ else if(strcasestr(search, "OKI"))
+ return strdup("OKI");
+ else if(strcasestr(search, "Hewlett Packard"))
+ return strdup("Hewlett Packard");
+ else if(strcasestr(search, "IBM"))
+ return strdup("IBM");
+ else if(strcasestr(search, "Mustek"))
+ return strdup("Mustek");
+ else if(strcasestr(search, "Ricoh"))
+ return strdup("Ricoh");
+ else if(strcasestr(search, "Sharp"))
+ return strdup("Sharp");
+ else if(strcasestr(search, "UMAX"))
+ return strdup("UMAX");
+ else if(strcasestr(search, "PINT"))
+ return strdup("PINT");
+ else if(strcasestr(search, "Brother"))
+ return strdup("Brother");
+ return NULL;
+}
+
/**
* \fn static SANE_Device *convertFromESCLDev(ESCL_Device *cdev)
* \brief Function that checks if the url of the received scanner is secured or not (http / https).
@@ -142,19 +268,61 @@ max_string_size(const SANE_String_Const strings[])
static SANE_Device *
convertFromESCLDev(ESCL_Device *cdev)
{
+ char *tmp;
+ int len, lv = 0;
+ char unix_path[PATH_MAX+7] = { 0 };
SANE_Device *sdev = (SANE_Device*) calloc(1, sizeof(SANE_Device));
- char tmp[PATH_MAX] = { 0 };
+ if (!sdev) {
+ DBG (10, "Sane_Device allocation failure.\n");
+ return NULL;
+ }
+
+ if (cdev->unix_socket && strlen(cdev->unix_socket)) {
+ snprintf(unix_path, sizeof(unix_path), "unix:%s:", cdev->unix_socket);
+ }
+ len = snprintf(NULL, 0, "%shttp%s://%s:%d",
+ unix_path, cdev->https ? "s" : "", cdev->ip_address, cdev->port_nb);
+ len++;
+ tmp = (char *)malloc(len);
+ if (!tmp) {
+ DBG (10, "Name allocation failure.\n");
+ goto freedev;
+ }
+ snprintf(tmp, len, "%shttp%s://%s:%d",
+ unix_path, cdev->https ? "s" : "", cdev->ip_address, cdev->port_nb);
+ sdev->name = tmp;
- if (strcmp(cdev->type, "_uscan._tcp") == 0 || strcmp(cdev->type, "http") == 0)
- snprintf(tmp, sizeof(tmp), "http://%s:%d", cdev->ip_address, cdev->port_nb);
- else
- snprintf(tmp, sizeof(tmp), "https://%s:%d", cdev->ip_address, cdev->port_nb);
DBG( 1, "Escl add device : %s\n", tmp);
- sdev->name = strdup(tmp);
- sdev->model = strdup(cdev->model_name);
- sdev->vendor = strdup("ESCL");
+ sdev->vendor = get_vendor(cdev->model_name);
+
+ if (!sdev->vendor)
+ sdev->vendor = strdup("ESCL");
+ else
+ lv = strlen(sdev->vendor) + 1;
+ if (!sdev->vendor) {
+ DBG (10, "Vendor allocation failure.\n");
+ goto freemodel;
+ }
+ sdev->model = strdup(lv + cdev->model_name);
+ if (!sdev->model) {
+ DBG (10, "Model allocation failure.\n");
+ goto freename;
+ }
sdev->type = strdup("flatbed scanner");
+ if (!sdev->type) {
+ DBG (10, "Scanner Type allocation failure.\n");
+ goto freevendor;
+ }
return (sdev);
+freevendor:
+ free((void*)sdev->vendor);
+freemodel:
+ free((void*)sdev->model);
+freename:
+ free((void*)sdev->name);
+freedev:
+ free((void*)sdev);
+ return NULL;
}
/**
@@ -174,9 +342,9 @@ sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize)
SANE_Status status = SANE_STATUS_GOOD;
curl_global_init(CURL_GLOBAL_ALL);
if (version_code != NULL)
- *version_code = SANE_VERSION_CODE(1, 0, 0);
+ *version_code = SANE_VERSION_CODE(1, 0, 0);
if (status != SANE_STATUS_GOOD)
- return (status);
+ return (status);
return (SANE_STATUS_GOOD);
}
@@ -194,12 +362,12 @@ sane_exit(void)
ESCL_Device *next = NULL;
while (list_devices_primary != NULL) {
- next = list_devices_primary->next;
- free(list_devices_primary);
- list_devices_primary = next;
+ next = list_devices_primary->next;
+ free(list_devices_primary);
+ list_devices_primary = next;
}
if (devlist)
- free (devlist);
+ free (devlist);
list_devices_primary = NULL;
devlist = NULL;
curl_global_cleanup();
@@ -218,44 +386,85 @@ static SANE_Status
attach_one_config(SANEI_Config __sane_unused__ *config, const char *line)
{
int port = 0;
- static int count = 0;
+ SANE_Status status;
static ESCL_Device *escl_device = NULL;
- if (strncmp(line, "[device]", 8) == 0) {
- count = 0;
+ if (strncmp(line, "device", 6) == 0) {
+ char *name_str = NULL;
+ char *opt_model = NULL;
+
+ line = sanei_config_get_string(line + 6, &name_str);
+ DBG (10, "New Escl_Device URL [%s].\n", (name_str ? name_str : "VIDE"));
+ if (!name_str || !*name_str) {
+ DBG (1, "Escl_Device URL missing.\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (*line) {
+ line = sanei_config_get_string(line, &opt_model);
+ DBG (10, "New Escl_Device model [%s].\n", opt_model);
+ }
+
+ escl_free_device(escl_device);
escl_device = (ESCL_Device*)calloc(1, sizeof(ESCL_Device));
+ if (!escl_device) {
+ DBG (10, "New Escl_Device allocation failure.\n");
+ free(name_str);
+ return (SANE_STATUS_NO_MEM);
+ }
+ status = escl_parse_name(name_str, escl_device);
+ free(name_str);
+ if (status != SANE_STATUS_GOOD) {
+ escl_free_device(escl_device);
+ escl_device = NULL;
+ return status;
+ }
+ escl_device->model_name = opt_model ? opt_model : strdup("Unknown model");
+ escl_device->type = strdup("flatbed scanner");
+ }
+
+ if (strncmp(line, "[device]", 8) == 0) {
+ escl_device = escl_free_device(escl_device);
+ escl_device = (ESCL_Device*)calloc(1, sizeof(ESCL_Device));
+ if (!escl_device) {
+ DBG (10, "New Escl_Device allocation failure.");
+ return (SANE_STATUS_NO_MEM);
+ }
}
if (strncmp(line, "ip", 2) == 0) {
- const char *ip_space = sanei_config_skip_whitespace(line + 2);
- if (escl_device != NULL && ip_space != NULL) {
- count++;
- escl_device->ip_address = strdup(ip_space);
- }
+ const char *ip_space = sanei_config_skip_whitespace(line + 2);
+ DBG (10, "New Escl_Device IP [%s].", (ip_space ? ip_space : "VIDE"));
+ if (escl_device != NULL && ip_space != NULL) {
+ DBG (10, "New Escl_Device IP Affected.");
+ escl_device->ip_address = strdup(ip_space);
+ }
}
if (sscanf(line, "port %i", &port) == 1 && port != 0) {
- const char *port_space = sanei_config_skip_whitespace(line + 4);
- if (escl_device != NULL && port_space != NULL) {
- count++;
- escl_device->port_nb = port;
- }
+ DBG (10, "New Escl_Device PORT [%d].", port);
+ if (escl_device != NULL) {
+ DBG (10, "New Escl_Device PORT Affected.");
+ escl_device->port_nb = port;
+ }
}
if (strncmp(line, "model", 5) == 0) {
- const char *model_space = sanei_config_skip_whitespace(line + 5);
- if (escl_device != NULL && model_space != NULL) {
- count++;
- escl_device->model_name = strdup(model_space);
- }
+ const char *model_space = sanei_config_skip_whitespace(line + 5);
+ DBG (10, "New Escl_Device MODEL [%s].", (model_space ? model_space : "VIDE"));
+ if (escl_device != NULL && model_space != NULL) {
+ DBG (10, "New Escl_Device MODEL Affected.");
+ escl_device->model_name = strdup(model_space);
+ }
}
if (strncmp(line, "type", 4) == 0) {
- const char *type_space = sanei_config_skip_whitespace(line + 4);
- if (escl_device != NULL && type_space != NULL) {
- count++;
- escl_device->type = strdup(type_space);
- }
+ const char *type_space = sanei_config_skip_whitespace(line + 4);
+ DBG (10, "New Escl_Device TYPE [%s].", (type_space ? type_space : "VIDE"));
+ if (escl_device != NULL && type_space != NULL) {
+ DBG (10, "New Escl_Device TYPE Affected.");
+ escl_device->type = strdup(type_space);
+ }
}
- if (count == 4)
- return (escl_add_in_list(escl_device));
- return (SANE_STATUS_GOOD);
+ status = escl_check_and_add_device(escl_device);
+ if (status == SANE_STATUS_GOOD)
+ escl_device = NULL;
+ return status;
}
/**
@@ -269,7 +478,7 @@ SANE_Status
sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
{
if (local_only) /* eSCL is a network-only protocol */
- return (device_list ? SANE_STATUS_GOOD : SANE_STATUS_INVAL);
+ return (device_list ? SANE_STATUS_GOOD : SANE_STATUS_INVAL);
DBG (10, "escl sane_get_devices\n");
ESCL_Device *dev = NULL;
@@ -277,29 +486,46 @@ sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
SANE_Status status;
if (device_list == NULL)
- return (SANE_STATUS_INVAL);
+ return (SANE_STATUS_INVAL);
status = sanei_configure_attach(ESCL_CONFIG_FILE, NULL, attach_one_config);
if (status != SANE_STATUS_GOOD)
- return (status);
+ return (status);
escl_devices(&status);
if (status != SANE_STATUS_GOOD)
- return (status);
+ return (status);
if (devlist)
- free(devlist);
+ free(devlist);
devlist = (const SANE_Device **) calloc (num_devices + 1, sizeof (devlist[0]));
if (devlist == NULL)
- return (SANE_STATUS_NO_MEM);
+ return (SANE_STATUS_NO_MEM);
int i = 0;
for (dev = list_devices_primary; i < num_devices; dev = dev->next) {
- SANE_Device *s_dev = convertFromESCLDev(dev);
- devlist[i] = s_dev;
- i++;
+ SANE_Device *s_dev = convertFromESCLDev(dev);
+ devlist[i] = s_dev;
+ i++;
}
devlist[i] = 0;
*device_list = devlist;
return (devlist) ? SANE_STATUS_GOOD : SANE_STATUS_NO_MEM;
}
+/* Returns the length of the longest string, including the terminating
+ * character. */
+static size_t
+_source_size_max (SANE_String_Const * sources)
+{
+ size_t size = 0;
+
+ while(*sources)
+ {
+ size_t t = strlen (*sources) + 1;
+ if (t > size)
+ size = t;
+ sources++;
+ }
+ return size;
+}
+
/**
* \fn static SANE_Status init_options(SANE_String_Const name, escl_sane_t *s)
* \brief Function thzt initializes all the needed options of the received scanner
@@ -309,26 +535,51 @@ sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
* \return status (if everything is OK, status = SANE_STATUS_GOOD)
*/
static SANE_Status
-init_options(SANE_String_Const name, escl_sane_t *s)
+init_options(SANE_String_Const name_source, escl_sane_t *s)
{
DBG (10, "escl init_options\n");
+
SANE_Status status = SANE_STATUS_GOOD;
int i = 0;
-
- if (name == NULL)
- return (SANE_STATUS_INVAL);
+ if (!s->scanner) return SANE_STATUS_INVAL;
+ if (name_source) {
+ int source = s->scanner->source;
+ DBG (10, "escl init_options name [%s]\n", name_source);
+ if (!strcmp(name_source, SANE_I18N ("ADF Duplex")))
+ s->scanner->source = ADFDUPLEX;
+ else if (!strncmp(name_source, "A", 1) ||
+ !strcmp(name_source, SANE_I18N ("ADF")))
+ s->scanner->source = ADFSIMPLEX;
+ else
+ s->scanner->source = PLATEN;
+ if (source == s->scanner->source) return status;
+ }
+ else
+ s->scanner->source = PLATEN;
memset (s->opt, 0, sizeof (s->opt));
memset (s->val, 0, sizeof (s->val));
for (i = 0; i < NUM_OPTIONS; ++i) {
- s->opt[i].size = sizeof (SANE_Word);
- s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
- }
- s->x_range.min = 0;
- s->x_range.max = s->scanner->MaxWidth - s->scanner->MinWidth;
- s->x_range.quant = 1;
- s->y_range.min = 0;
- s->y_range.max = s->scanner->MaxHeight - s->scanner->MinHeight;
- s->y_range.quant = 1;
+ s->opt[i].size = sizeof (SANE_Word);
+ s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ s->x_range1.min = 0;
+ s->x_range1.max =
+ PIXEL_TO_MM((s->scanner->caps[s->scanner->source].MaxWidth -
+ s->scanner->caps[s->scanner->source].MinWidth),
+ 300.0);
+ s->x_range1.quant = 0;
+ s->x_range2.min = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MinWidth, 300.0);
+ s->x_range2.max = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MaxWidth, 300.0);
+ s->x_range2.quant = 0;
+ s->y_range1.min = 0;
+ s->y_range1.max =
+ PIXEL_TO_MM((s->scanner->caps[s->scanner->source].MaxHeight -
+ s->scanner->caps[s->scanner->source].MinHeight),
+ 300.0);
+ s->y_range1.quant = 0;
+ s->y_range2.min = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MinHeight, 300.0);
+ s->y_range2.max = PIXEL_TO_MM(s->scanner->caps[s->scanner->source].MaxHeight, 300.0);
+ s->y_range2.quant = 0;
s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
@@ -347,10 +598,18 @@ init_options(SANE_String_Const name, escl_sane_t *s)
s->opt[OPT_MODE].type = SANE_TYPE_STRING;
s->opt[OPT_MODE].unit = SANE_UNIT_NONE;
s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
- s->opt[OPT_MODE].constraint.string_list = s->scanner->ColorModes;
- s->val[OPT_MODE].s = (char *)strdup(s->scanner->ColorModes[0]);
- s->opt[OPT_MODE].size = max_string_size(s->scanner->ColorModes);
- s->scanner->default_color = (char *)strdup(s->scanner->ColorModes[0]);
+ s->opt[OPT_MODE].constraint.string_list = s->scanner->caps[s->scanner->source].ColorModes;
+ s->val[OPT_MODE].s = (char *)strdup(s->scanner->caps[s->scanner->source].ColorModes[0]);
+ if (!s->val[OPT_MODE].s) {
+ DBG (10, "Color Mode Default allocation failure.\n");
+ return (SANE_STATUS_NO_MEM);
+ }
+ s->opt[OPT_MODE].size = max_string_size(s->scanner->caps[s->scanner->source].ColorModes);
+ s->scanner->caps[s->scanner->source].default_color = (char *)strdup(s->scanner->caps[s->scanner->source].ColorModes[0]);
+ if (!s->scanner->caps[s->scanner->source].default_color) {
+ DBG (10, "Color Mode Default allocation failure.\n");
+ return (SANE_STATUS_NO_MEM);
+ }
s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
@@ -358,9 +617,9 @@ init_options(SANE_String_Const name, escl_sane_t *s)
s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
- s->opt[OPT_RESOLUTION].constraint.word_list = s->scanner->SupportedResolutions;
- s->val[OPT_RESOLUTION].w = s->scanner->SupportedResolutions[1];
- s->scanner->default_resolution = s->scanner->SupportedResolutions[1];
+ s->opt[OPT_RESOLUTION].constraint.word_list = s->scanner->caps[s->scanner->source].SupportedResolutions;
+ s->val[OPT_RESOLUTION].w = s->scanner->caps[s->scanner->source].SupportedResolutions[1];
+ s->scanner->caps[s->scanner->source].default_resolution = s->scanner->caps[s->scanner->source].SupportedResolutions[1];
s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
@@ -376,7 +635,7 @@ init_options(SANE_String_Const name, escl_sane_t *s)
s->val[OPT_GRAY_PREVIEW].w = SANE_FALSE;
s->opt[OPT_GEOMETRY_GROUP].title = SANE_TITLE_GEOMETRY;
- s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].desc = SANE_DESC_GEOMETRY;
s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
@@ -385,40 +644,107 @@ init_options(SANE_String_Const name, escl_sane_t *s)
s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
- s->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_TL_X].size = sizeof(SANE_Fixed);
+ s->opt[OPT_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
- s->opt[OPT_TL_X].constraint.range = &s->x_range;
- s->val[OPT_TL_X].w = s->scanner->RiskyLeftMargin;
+ s->opt[OPT_TL_X].constraint.range = &s->x_range1;
+ s->val[OPT_TL_X].w = s->x_range1.min;
s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
- s->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_TL_Y].size = sizeof(SANE_Fixed);
+ s->opt[OPT_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
- s->opt[OPT_TL_Y].constraint.range = &s->y_range;
- s->val[OPT_TL_Y].w = s->scanner->RiskyTopMargin;
+ s->opt[OPT_TL_Y].constraint.range = &s->y_range1;
+ s->val[OPT_TL_Y].w = s->y_range1.min;
s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
- s->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_BR_X].size = sizeof(SANE_Fixed);
+ s->opt[OPT_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
- s->opt[OPT_BR_X].constraint.range = &s->x_range;
- s->val[OPT_BR_X].w = s->scanner->MaxWidth;
+ s->opt[OPT_BR_X].constraint.range = &s->x_range2;
+ s->val[OPT_BR_X].w = s->x_range2.max;
s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
- s->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
+ s->opt[OPT_BR_Y].size = sizeof(SANE_Fixed);
+ s->opt[OPT_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
- s->opt[OPT_BR_Y].constraint.range = &s->y_range;
- s->val[OPT_BR_Y].w = s->scanner->MaxHeight;
+ s->opt[OPT_BR_Y].constraint.range = &s->y_range2;
+ s->val[OPT_BR_Y].w = s->y_range2.max;
+
+ /* OPT_SCAN_SOURCE */
+ s->opt[OPT_SCAN_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SCAN_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SCAN_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SCAN_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SCAN_SOURCE].size = _source_size_max(s->scanner->Sources);
+ s->opt[OPT_SCAN_SOURCE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ s->opt[OPT_SCAN_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SCAN_SOURCE].constraint.string_list = s->scanner->Sources;
+ if (s->val[OPT_SCAN_SOURCE].s)
+ free (s->val[OPT_SCAN_SOURCE].s);
+ s->val[OPT_SCAN_SOURCE].s = strdup (s->scanner->Sources[s->scanner->source]);
return (status);
}
+SANE_Status
+escl_parse_name(SANE_String_Const name, ESCL_Device *device)
+{
+ SANE_String_Const host = NULL;
+ SANE_String_Const port_str = NULL;
+ DBG(10, "escl_parse_name\n");
+ if (name == NULL || device == NULL) {
+ return SANE_STATUS_INVAL;
+ }
+
+ if (strncmp(name, "unix:", 5) == 0) {
+ SANE_String_Const socket = name + 5;
+ name = strchr(socket, ':');
+ if (name == NULL)
+ return SANE_STATUS_INVAL;
+ device->unix_socket = strndup(socket, name - socket);
+ name++;
+ }
+
+ if (strncmp(name, "https://", 8) == 0) {
+ device->https = SANE_TRUE;
+ host = name + 8;
+ } else if (strncmp(name, "http://", 7) == 0) {
+ device->https = SANE_FALSE;
+ host = name + 7;
+ } else {
+ DBG(1, "Unknown URL scheme in %s", name);
+ return SANE_STATUS_INVAL;
+ }
+
+ port_str = strchr(host, ':');
+ if (port_str == NULL) {
+ DBG(1, "Port missing from URL: %s", name);
+ return SANE_STATUS_INVAL;
+ }
+ port_str++;
+ device->port_nb = atoi(port_str);
+ if (device->port_nb < 1 || device->port_nb > 65535) {
+ DBG(1, "Invalid port number in URL: %s", name);
+ return SANE_STATUS_INVAL;
+ }
+
+ device->ip_address = strndup(host, port_str - host - 1);
+ return SANE_STATUS_GOOD;
+}
+
/**
* \fn SANE_Status sane_open(SANE_String_Const name, SANE_Handle *h)
* \brief Function that establishes a connection with the device named by 'name',
@@ -437,28 +763,45 @@ sane_open(SANE_String_Const name, SANE_Handle *h)
if (name == NULL)
return (SANE_STATUS_INVAL);
- status = escl_status(name);
- if (status != SANE_STATUS_GOOD)
- return (status);
+
+ ESCL_Device *device = calloc(1, sizeof(ESCL_Device));
+ if (device == NULL) {
+ DBG (10, "Handle device allocation failure.\n");
+ return SANE_STATUS_NO_MEM;
+ }
+ status = escl_parse_name(name, device);
+ if (status != SANE_STATUS_GOOD) {
+ escl_free_device(device);
+ return status;
+ }
+
handler = (escl_sane_t *)calloc(1, sizeof(escl_sane_t));
- if (handler == NULL)
+ if (handler == NULL) {
+ escl_free_device(device);
return (SANE_STATUS_NO_MEM);
- handler->name = strdup(name);
- handler->scanner = escl_capabilities(name, &status);
- if (status != SANE_STATUS_GOOD)
+ }
+ handler->device = device; // Handler owns device now.
+ handler->scanner = escl_capabilities(device, &status);
+ if (status != SANE_STATUS_GOOD) {
+ escl_free_handler(handler);
return (status);
- status = init_options(name, handler);
- if (status != SANE_STATUS_GOOD)
+ }
+ status = init_options(NULL, handler);
+ if (status != SANE_STATUS_GOOD) {
+ escl_free_handler(handler);
return (status);
+ }
handler->ps.depth = 8;
handler->ps.last_frame = SANE_TRUE;
handler->ps.format = SANE_FRAME_RGB;
- handler->ps.pixels_per_line = handler->val[OPT_BR_X].w;
- handler->ps.lines = handler->val[OPT_BR_Y].w;
+ handler->ps.pixels_per_line = MM_TO_PIXEL(handler->val[OPT_BR_X].w, 300.0);
+ handler->ps.lines = MM_TO_PIXEL(handler->val[OPT_BR_Y].w, 300.0);
handler->ps.bytes_per_line = handler->ps.pixels_per_line * 3;
status = sane_get_parameters(handler, 0);
- if (status != SANE_STATUS_GOOD)
+ if (status != SANE_STATUS_GOOD) {
+ escl_free_handler(handler);
return (status);
+ }
handler->cancel = SANE_FALSE;
handler->write_scan_data = SANE_FALSE;
handler->decompress_scan_data = SANE_FALSE;
@@ -483,8 +826,11 @@ sane_cancel(SANE_Handle h)
fclose(handler->scanner->tmp);
handler->scanner->tmp = NULL;
}
+ handler->scanner->work = SANE_FALSE;
handler->cancel = SANE_TRUE;
- escl_scanner(handler->name, handler->result);
+ escl_scanner(handler->device, handler->result);
+ free(handler->result);
+ handler->result = NULL;
}
/**
@@ -497,7 +843,7 @@ sane_close(SANE_Handle h)
{
DBG (10, "escl sane_close\n");
if (h != NULL) {
- free(h);
+ escl_free_handler(h);
h = NULL;
}
}
@@ -517,8 +863,8 @@ sane_get_option_descriptor(SANE_Handle h, SANE_Int n)
escl_sane_t *s = h;
if ((unsigned) n >= NUM_OPTIONS || n < 0)
- return (0);
- return (s->opt + n);
+ return (0);
+ return (&s->opt[n]);
}
/**
@@ -541,62 +887,92 @@ sane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v, SANE_Int
escl_sane_t *handler = h;
if (i)
- *i = 0;
+ *i = 0;
if (n >= NUM_OPTIONS || n < 0)
- return (SANE_STATUS_INVAL);
+ return (SANE_STATUS_INVAL);
if (a == SANE_ACTION_GET_VALUE) {
- switch (n) {
- case OPT_NUM_OPTS:
- case OPT_RESOLUTION:
- case OPT_TL_X:
- case OPT_TL_Y:
- case OPT_BR_X:
- case OPT_BR_Y:
- case OPT_PREVIEW:
- case OPT_GRAY_PREVIEW:
- *(SANE_Word *) v = handler->val[n].w;
- break;
- case OPT_MODE:
- strcpy (v, handler->val[n].s);
- break;
- case OPT_MODE_GROUP:
- default:
- break;
- }
- return (SANE_STATUS_GOOD);
+ switch (n) {
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_GRAY_PREVIEW:
+ *(SANE_Word *) v = handler->val[n].w;
+ break;
+ case OPT_SCAN_SOURCE:
+ case OPT_MODE:
+ strcpy (v, handler->val[n].s);
+ break;
+ case OPT_MODE_GROUP:
+ default:
+ break;
+ }
+ return (SANE_STATUS_GOOD);
}
if (a == SANE_ACTION_SET_VALUE) {
- switch (n) {
- case OPT_TL_X:
- case OPT_TL_Y:
- case OPT_BR_X:
- case OPT_BR_Y:
- case OPT_PREVIEW:
- case OPT_GRAY_PREVIEW:
- handler->val[n].w = *(SANE_Word *) v;
- if (i && handler->val[n].w != *(SANE_Word *) v)
- *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
- handler->val[n].w = *(SANE_Word *) v;
- break;
- case OPT_RESOLUTION:
- handler->val[n].w = *(SANE_Word *) v;
- if (i)
- *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
- break;
- case OPT_MODE:
- if (handler->val[n].s)
- free (handler->val[n].s);
- handler->val[n].s = strdup (v);
- if (i)
- *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
- break;
- default:
- break;
- }
+ switch (n) {
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_GRAY_PREVIEW:
+ handler->val[n].w = *(SANE_Word *) v;
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
+ break;
+ case OPT_SCAN_SOURCE:
+ DBG(10, "SET OPT_SCAN_SOURCE(%s)\n", (SANE_String_Const)v);
+ init_options((SANE_String_Const)v, handler);
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
+ break;
+ case OPT_MODE:
+ if (handler->val[n].s)
+ free (handler->val[n].s);
+ handler->val[n].s = strdup (v);
+ if (!handler->val[n].s) {
+ DBG (10, "OPT_MODE allocation failure.\n");
+ return (SANE_STATUS_NO_MEM);
+ }
+ if (i)
+ *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
+ break;
+ default:
+ break;
+ }
}
return (SANE_STATUS_GOOD);
}
+static SANE_Bool
+_go_next_page(SANE_Status status,
+ SANE_Status job)
+{
+ // Thank's Alexander Pevzner (pzz@apevzner.com)
+ SANE_Status st = SANE_STATUS_NO_DOCS;
+ switch (status) {
+ case SANE_STATUS_GOOD:
+ case SANE_STATUS_UNSUPPORTED:
+ case SANE_STATUS_DEVICE_BUSY: {
+ DBG(10, "eSCL : Test next page\n");
+ if (job != SANE_STATUS_GOOD) {
+ DBG(10, "eSCL : Go next page\n");
+ st = SANE_STATUS_GOOD;
+ }
+ break;
+ }
+ default:
+ DBG(10, "eSCL : No next page\n");
+ }
+ return st;
+}
+
/**
* \fn SANE_Status sane_start(SANE_Handle h)
* \brief Function that initiates aquisition of an image from the device represented by handle 'h'.
@@ -614,70 +990,137 @@ sane_start(SANE_Handle h)
int he = 0;
int bps = 0;
- if (handler->name == NULL)
+ if (handler->device == NULL) {
+ DBG(1, "Missing handler device.\n");
return (SANE_STATUS_INVAL);
+ }
handler->cancel = SANE_FALSE;
handler->write_scan_data = SANE_FALSE;
handler->decompress_scan_data = SANE_FALSE;
handler->end_read = SANE_FALSE;
- handler->scanner->height = handler->val[OPT_BR_Y].w;
- handler->scanner->width = handler->val[OPT_BR_X].w;
- handler->scanner->pos_x = handler->val[OPT_TL_X].w;
- handler->scanner->pos_y = handler->val[OPT_TL_Y].w;
- if(handler->scanner->default_color)
- free(handler->scanner->default_color);
- if (handler->val[OPT_PREVIEW].w == SANE_TRUE)
- {
- int i = 0, val = 9999;;
- if (handler->val[OPT_GRAY_PREVIEW].w == SANE_TRUE ||
- !strncasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY, 3))
- handler->scanner->default_color = strdup("Grayscale8");
+ if (handler->scanner->work == SANE_FALSE) {
+ SANE_Status st = escl_status(handler->device,
+ handler->scanner->source,
+ NULL,
+ NULL);
+ if (st != SANE_STATUS_GOOD)
+ return st;
+ if(handler->scanner->caps[handler->scanner->source].default_color)
+ free(handler->scanner->caps[handler->scanner->source].default_color);
+ if (handler->val[OPT_PREVIEW].w == SANE_TRUE)
+ {
+ int i = 0, val = 9999;;
+ if (handler->val[OPT_GRAY_PREVIEW].w == SANE_TRUE ||
+ !strcasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY))
+ handler->scanner->caps[handler->scanner->source].default_color =
+ strdup("Grayscale8");
+ else
+ handler->scanner->caps[handler->scanner->source].default_color =
+ strdup("RGB24");
+ if (!handler->scanner->caps[handler->scanner->source].default_color) {
+ DBG (10, "Default Color allocation failure.\n");
+ return (SANE_STATUS_NO_MEM);
+ }
+ for (i = 1; i < handler->scanner->caps[handler->scanner->source].SupportedResolutionsSize; i++)
+ {
+ if (val > handler->scanner->caps[handler->scanner->source].SupportedResolutions[i])
+ val = handler->scanner->caps[handler->scanner->source].SupportedResolutions[i];
+ }
+ handler->scanner->caps[handler->scanner->source].default_resolution = val;
+ }
else
- handler->scanner->default_color = strdup("RGB24");
- for (i = 1; i < handler->scanner->SupportedResolutionsSize; i++)
{
- if (val > handler->scanner->SupportedResolutions[i])
- val = handler->scanner->SupportedResolutions[i];
+ handler->scanner->caps[handler->scanner->source].default_resolution =
+ handler->val[OPT_RESOLUTION].w;
+ if (!strcasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY))
+ handler->scanner->caps[handler->scanner->source].default_color = strdup("Grayscale8");
+ else if (!strcasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_LINEART))
+ handler->scanner->caps[handler->scanner->source].default_color =
+ strdup("BlackAndWhite1");
+ else
+ handler->scanner->caps[handler->scanner->source].default_color =
+ strdup("RGB24");
}
- handler->scanner->default_resolution = val;
+ handler->scanner->caps[handler->scanner->source].height =
+ MM_TO_PIXEL(handler->val[OPT_BR_Y].w, 300.0);
+ handler->scanner->caps[handler->scanner->source].width =
+ MM_TO_PIXEL(handler->val[OPT_BR_X].w, 300.0);;
+ if (handler->x_range1.min == handler->val[OPT_TL_X].w)
+ handler->scanner->caps[handler->scanner->source].pos_x = 0;
+ else
+ handler->scanner->caps[handler->scanner->source].pos_x =
+ MM_TO_PIXEL((handler->val[OPT_TL_X].w - handler->x_range1.min),
+ 300.0);
+ if (handler->y_range1.min == handler->val[OPT_TL_X].w)
+ handler->scanner->caps[handler->scanner->source].pos_y = 0;
+ else
+ handler->scanner->caps[handler->scanner->source].pos_y =
+ MM_TO_PIXEL((handler->val[OPT_TL_Y].w - handler->y_range1.min),
+ 300.0);
+ DBG(10, "Calculate Size Image [%dx%d|%dx%d]\n",
+ handler->scanner->caps[handler->scanner->source].pos_x,
+ handler->scanner->caps[handler->scanner->source].pos_y,
+ handler->scanner->caps[handler->scanner->source].width,
+ handler->scanner->caps[handler->scanner->source].height);
+ if (!handler->scanner->caps[handler->scanner->source].default_color) {
+ DBG (10, "Default Color allocation failure.\n");
+ return (SANE_STATUS_NO_MEM);
+ }
+ handler->result = escl_newjob(handler->scanner, handler->device, &status);
+ if (status != SANE_STATUS_GOOD)
+ return (status);
}
else
{
- handler->scanner->default_resolution = handler->val[OPT_RESOLUTION].w;
- if (!strncasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY, 3))
- handler->scanner->default_color = strdup("Grayscale8");
- else
- handler->scanner->default_color = strdup("RGB24");
+ SANE_Status job = SANE_STATUS_UNSUPPORTED;
+ SANE_Status st = escl_status(handler->device,
+ handler->scanner->source,
+ handler->result,
+ &job);
+ DBG(10, "eSCL : command returned status %s\n", sane_strstatus(st));
+ if (_go_next_page(st, job) != SANE_STATUS_GOOD)
+ {
+ handler->scanner->work = SANE_FALSE;
+ return SANE_STATUS_NO_DOCS;
+ }
}
- handler->result = escl_newjob(handler->scanner, handler->name, &status);
+ status = escl_scan(handler->scanner, handler->device, handler->result);
if (status != SANE_STATUS_GOOD)
- return (status);
- status = escl_scan(handler->scanner, handler->name, handler->result);
- if (status != SANE_STATUS_GOOD)
- return (status);
- if (!strcmp(handler->scanner->default_format, "image/jpeg"))
+ return (status);
+ if (!strcmp(handler->scanner->caps[handler->scanner->source].default_format, "image/jpeg"))
{
status = get_JPEG_data(handler->scanner, &w, &he, &bps);
}
- else if (!strcmp(handler->scanner->default_format, "image/png"))
+ else if (!strcmp(handler->scanner->caps[handler->scanner->source].default_format, "image/png"))
{
status = get_PNG_data(handler->scanner, &w, &he, &bps);
}
- else if (!strcmp(handler->scanner->default_format, "image/tiff"))
+ else if (!strcmp(handler->scanner->caps[handler->scanner->source].default_format, "image/tiff"))
{
status = get_TIFF_data(handler->scanner, &w, &he, &bps);
}
- else
- return SANE_STATUS_INVAL;
+ else if (!strcmp(handler->scanner->caps[handler->scanner->source].default_format, "application/pdf"))
+ {
+ status = get_PDF_data(handler->scanner, &w, &he, &bps);
+ }
+ else {
+ DBG(10, "Unknow image format\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG(10, "2-Size Image (%ld)[%dx%d|%dx%d]\n", handler->scanner->img_size, 0, 0, w, he);
if (status != SANE_STATUS_GOOD)
- return (status);
+ return (status);
handler->ps.depth = 8;
handler->ps.pixels_per_line = w;
handler->ps.lines = he;
handler->ps.bytes_per_line = w * bps;
handler->ps.last_frame = SANE_TRUE;
handler->ps.format = SANE_FRAME_RGB;
+ handler->scanner->work = SANE_FALSE;
+// DBG(10, "NEXT Frame [%s]\n", (handler->ps.last_frame ? "Non" : "Oui"));
+ DBG(10, "Real Size Image [%dx%d|%dx%d]\n", 0, 0, w, he);
return (status);
}
@@ -700,7 +1143,7 @@ sane_get_parameters(SANE_Handle h, SANE_Parameters *p)
return (status);
if (p != NULL) {
p->depth = 8;
- p->last_frame = SANE_TRUE;
+ p->last_frame = handler->ps.last_frame;
p->format = SANE_FRAME_RGB;
p->pixels_per_line = handler->ps.pixels_per_line;
p->lines = handler->ps.lines;
@@ -729,6 +1172,7 @@ sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len)
if (!handler | !buf | !len)
return (SANE_STATUS_INVAL);
+
if (handler->cancel)
return (SANE_STATUS_CANCELLED);
if (!handler->write_scan_data)
@@ -756,10 +1200,23 @@ sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len)
}
}
else {
+ SANE_Status job = SANE_STATUS_UNSUPPORTED;
*len = 0;
free(handler->scanner->img_data);
handler->scanner->img_data = NULL;
- return (SANE_STATUS_EOF);
+ if (handler->scanner->source != PLATEN) {
+ SANE_Bool next_page = SANE_FALSE;
+ SANE_Status st = escl_status(handler->device,
+ handler->scanner->source,
+ handler->result,
+ &job);
+ DBG(10, "eSCL : command returned status %s\n", sane_strstatus(st));
+ if (_go_next_page(st, job) == SANE_STATUS_GOOD)
+ next_page = SANE_TRUE;
+ handler->scanner->work = SANE_TRUE;
+ handler->ps.last_frame = !next_page;
+ }
+ return SANE_STATUS_EOF;
}
return (SANE_STATUS_GOOD);
}
@@ -775,3 +1232,39 @@ sane_set_io_mode(SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ n
{
return (SANE_STATUS_UNSUPPORTED);
}
+
+/**
+ * \fn void escl_curl_url(CURL *handle, const ESCL_Device *device, SANE_String_Const path)
+ * \brief Uses the device info in 'device' and the path from 'path' to construct
+ * a full URL. Sets this URL and any necessary connection options into
+ * 'handle'.
+ */
+void
+escl_curl_url(CURL *handle, const ESCL_Device *device, SANE_String_Const path)
+{
+ int url_len;
+ char *url;
+
+ url_len = snprintf(NULL, 0, "%s://%s:%d%s",
+ (device->https ? "https" : "http"), device->ip_address,
+ device->port_nb, path);
+ url_len++;
+ url = (char *)malloc(url_len);
+ snprintf(url, url_len, "%s://%s:%d%s",
+ (device->https ? "https" : "http"), device->ip_address,
+ device->port_nb, path);
+
+ DBG( 1, "escl_curl_url: URL: %s\n", url );
+ curl_easy_setopt(handle, CURLOPT_URL, url);
+ free(url);
+ if (device->https) {
+ DBG( 1, "Ignoring safety certificates, use https\n");
+ curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L);
+ curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L);
+ }
+ if (device->unix_socket != NULL) {
+ DBG( 1, "Using local socket %s\n", device->unix_socket );
+ curl_easy_setopt(handle, CURLOPT_UNIX_SOCKET_PATH,
+ device->unix_socket);
+ }
+}
diff --git a/backend/escl/escl.h b/backend/escl/escl.h
index 82910bd..53ce7c7 100644
--- a/backend/escl/escl.h
+++ b/backend/escl/escl.h
@@ -43,6 +43,7 @@
#include "../include/sane/sane.h"
#include <stdio.h>
+#include <math.h>
#ifndef BACKEND_NAME
#define BACKEND_NAME escl
@@ -63,6 +64,14 @@
#define ESCL_CONFIG_FILE "escl.conf"
+
+enum {
+ PLATEN = 0,
+ ADFSIMPLEX,
+ ADFDUPLEX
+};
+
+
typedef struct {
int p1_0;
int p2_0;
@@ -82,9 +91,11 @@ typedef struct ESCL_Device {
int port_nb;
char *ip_address;
char *type;
+ SANE_Bool https;
+ char *unix_socket;
} ESCL_Device;
-typedef struct capabilities
+typedef struct capst
{
int height;
int width;
@@ -104,6 +115,7 @@ typedef struct capabilities
int ContentTypesSize;
SANE_String_Const *DocumentFormats;
int DocumentFormatsSize;
+ int format_ext;
SANE_Int *SupportedResolutions;
int SupportedResolutionsSize;
SANE_String_Const *SupportedIntents;
@@ -114,11 +126,21 @@ typedef struct capabilities
int RiskyRightMargin;
int RiskyTopMargin;
int RiskyBottomMargin;
+ int duplex;
+} caps_t;
+
+typedef struct capabilities
+{
+ caps_t caps[3];
+ int source;
+ SANE_String_Const *Sources;
+ int SourcesSize;
FILE *tmp;
unsigned char *img_data;
long img_size;
long img_read;
- int format_ext;
+ size_t real_read;
+ SANE_Bool work;
} capabilities_t;
typedef struct {
@@ -148,24 +170,45 @@ enum
OPT_TL_Y,
OPT_BR_X,
OPT_BR_Y,
+
+ OPT_SCAN_SOURCE,
+
NUM_OPTIONS
};
+#define PIXEL_TO_MM(pixels, dpi) SANE_FIX((double)pixels * 25.4 / (dpi))
+#define MM_TO_PIXEL(millimeters, dpi) (SANE_Word)round(SANE_UNFIX(millimeters) * (dpi) / 25.4)
+
ESCL_Device *escl_devices(SANE_Status *status);
-SANE_Status escl_device_add(int port_nb, const char *model_name, char *ip_address, char *type);
-SANE_Status escl_status(SANE_String_Const name);
-capabilities_t *escl_capabilities(SANE_String_Const name, SANE_Status *status);
-char *escl_newjob(capabilities_t *scanner, SANE_String_Const name, SANE_Status *status);
-SANE_Status escl_scan(capabilities_t *scanner, SANE_String_Const name, char *result);
-void escl_scanner(SANE_String_Const name, char *result);
+SANE_Status escl_device_add(int port_nb, const char *model_name,
+ char *ip_address, char *type);
+SANE_Status escl_status(const ESCL_Device *device,
+ int source,
+ const char* jobId,
+ SANE_Status *job);
+capabilities_t *escl_capabilities(const ESCL_Device *device, SANE_Status *status);
+char *escl_newjob(capabilities_t *scanner, const ESCL_Device *device,
+ SANE_Status *status);
+SANE_Status escl_scan(capabilities_t *scanner, const ESCL_Device *device,
+ char *result);
+void escl_scanner(const ESCL_Device *device, char *result);
+
+typedef void CURL;
+void escl_curl_url(CURL *handle, const ESCL_Device *device, SANE_String_Const path);
+
+unsigned char *escl_crop_surface(capabilities_t *scanner, unsigned char *surface,
+ int w, int h, int bps, int *width, int *height);
// JPEG
-SANE_Status get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps);
+SANE_Status get_JPEG_data(capabilities_t *scanner, int *width, int *height, int *bps);
// PNG
-SANE_Status get_PNG_data(capabilities_t *scanner, int *w, int *h, int *bps);
+SANE_Status get_PNG_data(capabilities_t *scanner, int *width, int *height, int *bps);
// TIFF
-SANE_Status get_TIFF_data(capabilities_t *scanner, int *w, int *h, int *bps);
+SANE_Status get_TIFF_data(capabilities_t *scanner, int *width, int *height, int *bps);
+
+// PDF
+SANE_Status get_PDF_data(capabilities_t *scanner, int *width, int *height, int *bps);
#endif
diff --git a/backend/escl/escl_capabilities.c b/backend/escl/escl_capabilities.c
index 690ff1e..fdd5cfe 100644
--- a/backend/escl/escl_capabilities.c
+++ b/backend/escl/escl_capabilities.c
@@ -45,7 +45,7 @@ struct cap
* \fn static SANE_String_Const convert_elements(SANE_String_Const str)
* \brief Function that converts the 'color modes' of the scanner (color/gray) to be understood by SANE.
*
- * \return SANE_VALUE_SCAN_MODE_GRAY / SANE_VALUE_SCAN_MODE_COLOR ; NULL otherwise
+ * \return SANE_VALUE_SCAN_MODE_GRAY / SANE_VALUE_SCAN_MODE_COLOR / SANE_VALUE_SCAN_MODE_LINEART; NULL otherwise
*/
static SANE_String_Const
convert_elements(SANE_String_Const str)
@@ -54,6 +54,10 @@ convert_elements(SANE_String_Const str)
return (SANE_VALUE_SCAN_MODE_GRAY);
else if (strcmp(str, "RGB24") == 0)
return (SANE_VALUE_SCAN_MODE_COLOR);
+#if(defined HAVE_POPPLER_GLIB)
+ else if (strcmp(str, "BlackAndWhite1") == 0)
+ return (SANE_VALUE_SCAN_MODE_LINEART);
+#endif
return (NULL);
}
@@ -137,7 +141,7 @@ memory_callback_c(void *contents, size_t size, size_t nmemb, void *userp)
char *str = realloc(mem->memory, mem->size + realsize + 1);
if (str == NULL) {
- fprintf(stderr, "not enough memory (realloc returned NULL)\n");
+ DBG(10, "not enough memory (realloc returned NULL)\n");
return (0);
}
mem->memory = str;
@@ -175,48 +179,61 @@ find_nodes_c(xmlNode *node)
* \return 0
*/
static int
-find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner)
+find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner, int type)
{
const char *name = (const char *)node->name;
- if (strcmp(name, "ColorMode") == 0)
- scanner->ColorModes = char_to_array(scanner->ColorModes, &scanner->ColorModesSize, (SANE_String_Const)xmlNodeGetContent(node), 1);
+ if (strcmp(name, "ColorMode") == 0) {
+ const char *color = (SANE_String_Const)xmlNodeGetContent(node);
+ if (type == PLATEN || strcmp(color, "BlackAndWhite1"))
+ scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes, &scanner->caps[type].ColorModesSize, (SANE_String_Const)xmlNodeGetContent(node), 1);
+ }
else if (strcmp(name, "ContentType") == 0)
- scanner->ContentTypes = char_to_array(scanner->ContentTypes, &scanner->ContentTypesSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
+ scanner->caps[type].ContentTypes = char_to_array(scanner->caps[type].ContentTypes, &scanner->caps[type].ContentTypesSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
else if (strcmp(name, "DocumentFormat") == 0)
{
int i = 0;
- scanner->DocumentFormats = char_to_array(scanner->DocumentFormats, &scanner->DocumentFormatsSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
- for(; i < scanner->DocumentFormatsSize; i++)
+ SANE_Bool have_jpeg = SANE_FALSE, have_png = SANE_FALSE, have_tiff = SANE_FALSE, have_pdf = SANE_FALSE;
+ scanner->caps[type].DocumentFormats = char_to_array(scanner->caps[type].DocumentFormats, &scanner->caps[type].DocumentFormatsSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
+ for(; i < scanner->caps[type].DocumentFormatsSize; i++)
{
- if (scanner->default_format == NULL && !strcmp(scanner->DocumentFormats[i], "image/jpeg"))
+ if (!strcmp(scanner->caps[type].DocumentFormats[i], "image/jpeg"))
{
- scanner->default_format = strdup("image/jpeg");
+ have_jpeg = SANE_TRUE;
}
#if(defined HAVE_LIBPNG)
- else if(!strcmp(scanner->DocumentFormats[i], "image/png") && (scanner->default_format == NULL || strcmp(scanner->default_format, "image/tiff")))
+ else if(!strcmp(scanner->caps[type].DocumentFormats[i], "image/png"))
{
- if (scanner->default_format)
- free(scanner->default_format);
- scanner->default_format = strdup("image/png");
+ have_png = SANE_TRUE;
}
#endif
#if(defined HAVE_TIFFIO_H)
- else if(!strcmp(scanner->DocumentFormats[i], "image/tiff"))
+ else if(type == PLATEN && !strcmp(scanner->caps[type].DocumentFormats[i], "image/tiff"))
+ {
+ have_tiff = SANE_TRUE;
+ }
+#endif
+#if(defined HAVE_POPPLER_GLIB)
+ else if(type == PLATEN && !strcmp(scanner->caps[type].DocumentFormats[i], "application/pdf"))
{
- if (scanner->default_format)
- free(scanner->default_format);
- scanner->default_format = strdup("image/tiff");
+ have_pdf = SANE_TRUE;
}
#endif
}
- fprintf(stderr, "Capability : [%s]\n", scanner->default_format);
+ if (have_pdf)
+ scanner->caps[type].default_format = strdup("application/pdf");
+ else if (have_tiff)
+ scanner->caps[type].default_format = strdup("image/tiff");
+ else if (have_png)
+ scanner->caps[type].default_format = strdup("image/png");
+ else if (have_jpeg)
+ scanner->caps[type].default_format = strdup("image/jpeg");
}
else if (strcmp(name, "DocumentFormatExt") == 0)
- scanner->format_ext = 1;
+ scanner->caps[type].format_ext = 1;
else if (strcmp(name, "Intent") == 0)
- scanner->SupportedIntents = char_to_array(scanner->SupportedIntents, &scanner->SupportedIntentsSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
+ scanner->caps[type].SupportedIntents = char_to_array(scanner->caps[type].SupportedIntents, &scanner->caps[type].SupportedIntentsSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
else if (strcmp(name, "XResolution") == 0)
- scanner->SupportedResolutions = int_to_array(scanner->SupportedResolutions, &scanner->SupportedResolutionsSize, atoi((const char *)xmlNodeGetContent(node)));
+ scanner->caps[type].SupportedResolutions = int_to_array(scanner->caps[type].SupportedResolutions, &scanner->caps[type].SupportedResolutionsSize, atoi((const char *)xmlNodeGetContent(node)));
return (0);
}
@@ -230,39 +247,39 @@ find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner)
* \return 0
*/
static int
-find_value_of_int_variables(xmlNode *node, capabilities_t *scanner)
+find_value_of_int_variables(xmlNode *node, capabilities_t *scanner, int type)
{
int MaxWidth = 0;
int MaxHeight = 0;
const char *name = (const char *)node->name;
if (strcmp(name, "MinWidth") == 0)
- scanner->MinWidth = atoi((const char*)xmlNodeGetContent(node));
+ scanner->caps[type].MinWidth = atoi((const char*)xmlNodeGetContent(node));
else if (strcmp(name, "MaxWidth") == 0) {
MaxWidth = atoi((const char*)xmlNodeGetContent(node));
- if (scanner->MaxWidth == 0 || MaxWidth < scanner->MaxWidth)
- scanner->MaxWidth = atoi((const char *)xmlNodeGetContent(node));
+ if (scanner->caps[type].MaxWidth == 0 || MaxWidth < scanner->caps[type].MaxWidth)
+ scanner->caps[type].MaxWidth = atoi((const char *)xmlNodeGetContent(node));
}
else if (strcmp(name, "MinHeight") == 0)
- scanner->MinHeight = atoi((const char*)xmlNodeGetContent(node));
+ scanner->caps[type].MinHeight = atoi((const char*)xmlNodeGetContent(node));
else if (strcmp(name, "MaxHeight") == 0) {
MaxHeight = atoi((const char*)xmlNodeGetContent(node));
- if (scanner->MaxHeight == 0 || MaxHeight < scanner->MaxHeight)
- scanner->MaxHeight = atoi((const char *)xmlNodeGetContent(node));
+ if (scanner->caps[type].MaxHeight == 0 || MaxHeight < scanner->caps[type].MaxHeight)
+ scanner->caps[type].MaxHeight = atoi((const char *)xmlNodeGetContent(node));
}
else if (strcmp(name, "MaxScanRegions") == 0)
- scanner->MaxScanRegions = atoi((const char *)xmlNodeGetContent(node));
+ scanner->caps[type].MaxScanRegions = atoi((const char *)xmlNodeGetContent(node));
else if (strcmp(name, "MaxOpticalXResolution") == 0)
- scanner->MaxOpticalXResolution = atoi((const char *)xmlNodeGetContent(node));
+ scanner->caps[type].MaxOpticalXResolution = atoi((const char *)xmlNodeGetContent(node));
else if (strcmp(name, "RiskyLeftMargin") == 0)
- scanner->RiskyLeftMargin = atoi((const char *)xmlNodeGetContent(node));
+ scanner->caps[type].RiskyLeftMargin = atoi((const char *)xmlNodeGetContent(node));
else if (strcmp(name, "RiskyRightMargin") == 0)
- scanner->RiskyRightMargin = atoi((const char *)xmlNodeGetContent(node));
+ scanner->caps[type].RiskyRightMargin = atoi((const char *)xmlNodeGetContent(node));
else if (strcmp(name, "RiskyTopMargin") == 0)
- scanner->RiskyTopMargin = atoi((const char *)xmlNodeGetContent(node));
+ scanner->caps[type].RiskyTopMargin = atoi((const char *)xmlNodeGetContent(node));
else if (strcmp(name, "RiskyBottomMargin") == 0)
- scanner->RiskyBottomMargin = atoi((const char *)xmlNodeGetContent(node));
- find_valor_of_array_variables(node, scanner);
+ scanner->caps[type].RiskyBottomMargin = atoi((const char *)xmlNodeGetContent(node));
+ find_valor_of_array_variables(node, scanner, type);
return (0);
}
@@ -275,7 +292,7 @@ find_value_of_int_variables(xmlNode *node, capabilities_t *scanner)
* \return 0
*/
static int
-find_true_variables(xmlNode *node, capabilities_t *scanner)
+find_true_variables(xmlNode *node, capabilities_t *scanner, int type)
{
const char *name = (const char *)node->name;
if (strcmp(name, "MinWidth") == 0 ||
@@ -294,7 +311,7 @@ find_true_variables(xmlNode *node, capabilities_t *scanner)
strcmp(name, "RiskyTopMargin") == 0 ||
strcmp(name, "RiskyBottomMargin") == 0 ||
strcmp(name, "DocumentFormatExt") == 0)
- find_value_of_int_variables(node, scanner);
+ find_value_of_int_variables(node, scanner, type);
return (0);
}
@@ -305,21 +322,67 @@ find_true_variables(xmlNode *node, capabilities_t *scanner)
* \return 0
*/
static int
-print_xml_c(xmlNode *node, capabilities_t *scanner)
+print_xml_c(xmlNode *node, capabilities_t *scanner, int type)
{
while (node) {
if (node->type == XML_ELEMENT_NODE) {
- if (find_nodes_c(node))
- find_true_variables(node, scanner);
+ if (find_nodes_c(node) && type != -1)
+ find_true_variables(node, scanner, type);
}
- print_xml_c(node->children, scanner);
+ if (!strcmp((const char *)node->name, "PlatenInputCaps")) {
+ scanner->Sources[PLATEN] = (SANE_String_Const)strdup(SANE_I18N ("Flatbed"));
+ scanner->SourcesSize++;
+ scanner->source = PLATEN;
+ print_xml_c(node->children, scanner, PLATEN);
+ scanner->caps[PLATEN].duplex = 0;
+ }
+ else if (!strcmp((const char *)node->name, "AdfSimplexInputCaps")) {
+ scanner->Sources[ADFSIMPLEX] = (SANE_String_Const)strdup(SANE_I18N("ADF"));
+ scanner->SourcesSize++;
+ if (scanner->source == -1) scanner->source = ADFSIMPLEX;
+ print_xml_c(node->children, scanner, ADFSIMPLEX);
+ scanner->caps[ADFSIMPLEX].duplex = 0;
+ }
+ else if (!strcmp((const char *)node->name, "AdfDuplexInputCaps")) {
+ scanner->Sources[ADFDUPLEX] = (SANE_String_Const)strdup(SANE_I18N ("ADF Duplex"));
+ scanner->SourcesSize++;
+ if (scanner->source == -1) scanner->source = ADFDUPLEX;
+ print_xml_c(node->children, scanner, ADFDUPLEX);
+ scanner->caps[ADFDUPLEX].duplex = 1;
+ }
+ else
+ print_xml_c(node->children, scanner, type);
node = node->next;
}
return (0);
}
+static void
+_reduce_color_modes(capabilities_t *scanner)
+{
+ int type = 0;
+ for (type = 0; type < 3; type++) {
+ if (scanner->caps[type].ColorModesSize) {
+ if (scanner->caps[type].default_format &&
+ strcmp(scanner->caps[type].default_format, "application/pdf")) {
+ if (scanner->caps[type].ColorModesSize == 3) {
+ free(scanner->caps[type].ColorModes);
+ scanner->caps[type].ColorModes = NULL;
+ scanner->caps[type].ColorModesSize = 0;
+ scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes,
+ &scanner->caps[type].ColorModesSize,
+ (SANE_String_Const)SANE_VALUE_SCAN_MODE_GRAY, 0);
+ scanner->caps[type].ColorModes = char_to_array(scanner->caps[type].ColorModes,
+ &scanner->caps[type].ColorModesSize,
+ (SANE_String_Const)SANE_VALUE_SCAN_MODE_COLOR, 0);
+ }
+ }
+ }
+ }
+}
+
/**
- * \fn capabilities_t *escl_capabilities(SANE_String_Const name, SANE_Status *status)
+ * \fn capabilities_t *escl_capabilities(const ESCL_Device *device, SANE_Status *status)
* \brief Function that finally recovers all the capabilities of the scanner, using curl.
* This function is called in the 'sane_open' function and it's the equivalent of
* the following curl command : "curl http(s)://'ip':'port'/eSCL/ScannerCapabilities".
@@ -327,18 +390,18 @@ print_xml_c(xmlNode *node, capabilities_t *scanner)
* \return scanner (the structure that stocks all the capabilities elements)
*/
capabilities_t *
-escl_capabilities(SANE_String_Const name, SANE_Status *status)
+escl_capabilities(const ESCL_Device *device, SANE_Status *status)
{
capabilities_t *scanner = (capabilities_t*)calloc(1, sizeof(capabilities_t));
CURL *curl_handle = NULL;
struct cap *var = NULL;
xmlDoc *data = NULL;
xmlNode *node = NULL;
+ int i = 0;
const char *scanner_capabilities = "/eSCL/ScannerCapabilities";
- char tmp[PATH_MAX] = { 0 };
*status = SANE_STATUS_GOOD;
- if (name == NULL)
+ if (device == NULL)
*status = SANE_STATUS_NO_MEM;
var = (struct cap *)calloc(1, sizeof(struct cap));
if (var == NULL)
@@ -346,32 +409,41 @@ escl_capabilities(SANE_String_Const name, SANE_Status *status)
var->memory = malloc(1);
var->size = 0;
curl_handle = curl_easy_init();
- strcpy(tmp, name);
- strcat(tmp, scanner_capabilities);
- DBG( 1, "Get Capabilities : %s\n", tmp);
- curl_easy_setopt(curl_handle, CURLOPT_URL, tmp);
- if (strncmp(name, "https", 5) == 0) {
- DBG( 1, "Ignoring safety certificates, use https\n");
- curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
- }
+ escl_curl_url(curl_handle, device, scanner_capabilities);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, memory_callback_c);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)var);
- if (curl_easy_perform(curl_handle) != CURLE_OK) {
- DBG( 1, "The scanner didn't respond.\n");
+ CURLcode res = curl_easy_perform(curl_handle);
+ if (res != CURLE_OK) {
+ DBG( 1, "The scanner didn't respond: %s\n", curl_easy_strerror(res));
*status = SANE_STATUS_INVAL;
+ goto clean_data;
}
+ DBG( 10, "XML Capabilities[\n%s\n]\n", var->memory);
data = xmlReadMemory(var->memory, var->size, "file.xml", NULL, 0);
- if (data == NULL)
+ if (data == NULL) {
*status = SANE_STATUS_NO_MEM;
+ goto clean_data;
+ }
node = xmlDocGetRootElement(data);
- if (node == NULL)
+ if (node == NULL) {
*status = SANE_STATUS_NO_MEM;
- print_xml_c(node, scanner);
+ goto clean;
+ }
+
+ scanner->source = 0;
+ scanner->Sources = (SANE_String_Const *)malloc(sizeof(SANE_String_Const) * 4);
+ for (i = 0; i < 4; i++)
+ scanner->Sources[i] = NULL;
+ print_xml_c(node, scanner, -1);
+ _reduce_color_modes(scanner);
+clean:
xmlFreeDoc(data);
+clean_data:
xmlCleanupParser();
xmlMemoryDump();
curl_easy_cleanup(curl_handle);
- free(var->memory);
+ if (var)
+ free(var->memory);
+ free(var);
return (scanner);
}
diff --git a/backend/escl/escl_crop.c b/backend/escl/escl_crop.c
new file mode 100644
index 0000000..8740d22
--- /dev/null
+++ b/backend/escl/escl_crop.c
@@ -0,0 +1,102 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2020 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE 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
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include "escl.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+unsigned char *
+escl_crop_surface(capabilities_t *scanner,
+ unsigned char *surface,
+ int w,
+ int h,
+ int bps,
+ int *width,
+ int *height)
+{
+ double ratio = 1.0;
+ int x_off = 0, x = 0;
+ int real_w = 0;
+ int y_off = 0, y = 0;
+ int real_h = 0;
+ unsigned char *surface_crop = NULL;
+
+ DBG( 1, "Escl Image Crop\n");
+ ratio = (double)w / (double)scanner->caps[scanner->source].width;
+ scanner->caps[scanner->source].width = w;
+ if (scanner->caps[scanner->source].pos_x < 0)
+ scanner->caps[scanner->source].pos_x = 0;
+ if (scanner->caps[scanner->source].pos_x &&
+ (scanner->caps[scanner->source].width >
+ scanner->caps[scanner->source].pos_x))
+ x_off = (int)((double)scanner->caps[scanner->source].pos_x * ratio);
+ real_w = scanner->caps[scanner->source].width - x_off;
+
+ scanner->caps[scanner->source].height = h;
+ if (scanner->caps[scanner->source].pos_y &&
+ (scanner->caps[scanner->source].height >
+ scanner->caps[scanner->source].pos_y))
+ y_off = (int)((double)scanner->caps[scanner->source].pos_y * ratio);
+ real_h = scanner->caps[scanner->source].height - y_off;
+
+ DBG( 1, "Escl Image Crop [%dx%d|%dx%d]\n", scanner->caps[scanner->source].pos_x, scanner->caps[scanner->source].pos_y,
+ scanner->caps[scanner->source].width, scanner->caps[scanner->source].height);
+
+ *width = real_w;
+ *height = real_h;
+ DBG( 1, "Escl Image Crop [%dx%d]\n", *width, *height);
+ if (x_off > 0 || real_w < scanner->caps[scanner->source].width ||
+ y_off > 0 || real_h < scanner->caps[scanner->source].height) {
+ surface_crop = (unsigned char *)malloc (sizeof (unsigned char) * real_w
+ * real_h * bps);
+ if(!surface_crop) {
+ DBG( 1, "Escl Crop : Surface_crop Memory allocation problem\n");
+ free(surface);
+ surface = NULL;
+ goto finish;
+ }
+ for (y = 0; y < real_h; y++)
+ {
+ for (x = 0; x < real_w; x++)
+ {
+ surface_crop[(y * real_w * bps) + (x * bps)] =
+ surface[((y + y_off) * w * bps) + ((x + x_off) * bps)];
+ surface_crop[(y * real_w * bps) + (x * bps) + 1] =
+ surface[((y + y_off) * w * bps) + ((x + x_off) * bps) + 1];
+ surface_crop[(y * real_w * bps) + (x * bps) + 2] =
+ surface[((y + y_off) * w * bps) + ((x + x_off) * bps) + 2];
+ }
+ }
+ free(surface);
+ surface = surface_crop;
+ }
+ // we don't need row pointers anymore
+ scanner->img_data = surface;
+ scanner->img_size = (int)(real_w * real_h * bps);
+ scanner->img_read = 0;
+finish:
+ return surface;
+}
diff --git a/backend/escl/escl_jpeg.c b/backend/escl/escl_jpeg.c
index d6287ef..8d6b6b6 100644
--- a/backend/escl/escl_jpeg.c
+++ b/backend/escl/escl_jpeg.c
@@ -120,7 +120,6 @@ jpeg_RW_src(j_decompress_ptr cinfo, FILE *ctx)
if (cinfo->src == NULL) {
cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small)
((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(my_source_mgr));
- src = (my_source_mgr *) cinfo->src;
}
src = (my_source_mgr *) cinfo->src;
src->pub.init_source = init_source;
@@ -154,7 +153,7 @@ output_no_message(j_common_ptr __sane_unused__ cinfo)
* \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
*/
SANE_Status
-get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps)
+get_JPEG_data(capabilities_t *scanner, int *width, int *height, int *bps)
{
int start = 0;
struct jpeg_decompress_struct cinfo;
@@ -162,6 +161,11 @@ get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps)
unsigned char *surface = NULL;
struct my_error_mgr jerr;
int lineSize = 0;
+ JDIMENSION x_off = 0;
+ JDIMENSION y_off = 0;
+ JDIMENSION w = 0;
+ JDIMENSION h = 0;
+ int pos = 0;
if (scanner->tmp == NULL)
return (SANE_STATUS_INVAL);
@@ -174,6 +178,7 @@ get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps)
jpeg_destroy_decompress(&cinfo);
if (surface != NULL)
free(surface);
+ fseek(scanner->tmp, start, SEEK_SET);
DBG( 1, "Escl Jpeg : Error reading jpeg\n");
if (scanner->tmp) {
fclose(scanner->tmp);
@@ -187,10 +192,42 @@ get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps)
cinfo.out_color_space = JCS_RGB;
cinfo.quantize_colors = FALSE;
jpeg_calc_output_dimensions(&cinfo);
- surface = malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components);
+ if (cinfo.output_width < (unsigned int)scanner->caps[scanner->source].width)
+ scanner->caps[scanner->source].width = cinfo.output_width;
+ if (scanner->caps[scanner->source].pos_x < 0)
+ scanner->caps[scanner->source].pos_x = 0;
+
+ if (cinfo.output_height < (unsigned int)scanner->caps[scanner->source].height)
+ scanner->caps[scanner->source].height = cinfo.output_height;
+ if (scanner->caps[scanner->source].pos_y < 0)
+ scanner->caps[scanner->source].pos_y = 0;
+ DBG(10, "1-JPEF Geometry [%dx%d|%dx%d]\n",
+ scanner->caps[scanner->source].pos_x,
+ scanner->caps[scanner->source].pos_y,
+ scanner->caps[scanner->source].width,
+ scanner->caps[scanner->source].height);
+ x_off = scanner->caps[scanner->source].pos_x;
+ if (x_off > (unsigned int)scanner->caps[scanner->source].width) {
+ w = scanner->caps[scanner->source].width;
+ x_off = 0;
+ }
+ else
+ w = scanner->caps[scanner->source].width - x_off;
+ y_off = scanner->caps[scanner->source].pos_y;
+ if(y_off > (unsigned int)scanner->caps[scanner->source].height) {
+ h = scanner->caps[scanner->source].height;
+ y_off = 0;
+ }
+ else
+ h = scanner->caps[scanner->source].height - y_off;
+ DBG(10, "2-JPEF Geometry [%dx%d|%dx%d]\n",
+ x_off,
+ y_off,
+ w,
+ h);
+ surface = malloc(w * h * cinfo.output_components);
if (surface == NULL) {
jpeg_destroy_decompress(&cinfo);
- fseek(scanner->tmp, start, SEEK_SET);
DBG( 1, "Escl Jpeg : Memory allocation problem\n");
if (scanner->tmp) {
fclose(scanner->tmp);
@@ -198,17 +235,23 @@ get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps)
}
return (SANE_STATUS_NO_MEM);
}
- lineSize = cinfo.output_width * cinfo.output_components;
jpeg_start_decompress(&cinfo);
- while (cinfo.output_scanline < cinfo.output_height) {
- rowptr[0] = (JSAMPROW)surface + (lineSize * cinfo.output_scanline);
+ if (x_off > 0 || w < cinfo.output_width)
+ jpeg_crop_scanline(&cinfo, &x_off, &w);
+ lineSize = w * cinfo.output_components;
+ if (y_off > 0)
+ jpeg_skip_scanlines(&cinfo, y_off);
+ pos = 0;
+ while (cinfo.output_scanline < (unsigned int)scanner->caps[scanner->source].height) {
+ rowptr[0] = (JSAMPROW)surface + (lineSize * pos); // ..cinfo.output_scanline);
jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION) 1);
- }
+ pos++;
+ }
scanner->img_data = surface;
- scanner->img_size = lineSize * cinfo.output_height;
+ scanner->img_size = lineSize * h;
scanner->img_read = 0;
- *w = cinfo.output_width;
- *h = cinfo.output_height;
+ *width = w;
+ *height = h;
*bps = cinfo.output_components;
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
@@ -220,8 +263,8 @@ get_JPEG_data(capabilities_t *scanner, int *w, int *h, int *bps)
SANE_Status
get_JPEG_data(capabilities_t __sane_unused__ *scanner,
- int __sane_unused__ *w,
- int __sane_unused__ *h,
+ int __sane_unused__ *width,
+ int __sane_unused__ *height,
int __sane_unused__ *bps)
{
return (SANE_STATUS_INVAL);
diff --git a/backend/escl/escl_mupdf.c b/backend/escl/escl_mupdf.c
new file mode 100644
index 0000000..9399218
--- /dev/null
+++ b/backend/escl/escl_mupdf.c
@@ -0,0 +1,256 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE 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
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include "escl.h"
+
+#include "../include/sane/sanei.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+
+#if(defined HAVE_MUPDF)
+#include <mupdf/fitz.h>
+#endif
+
+#include <setjmp.h>
+
+
+#if(defined HAVE_MUPDF)
+
+// TODO: WIN32: HANDLE CreateFileW(), etc.
+// TODO: POSIX: int creat(), read(), write(), lseeko, etc.
+
+typedef struct fz_file_stream_escl_s
+{
+ FILE *file;
+ unsigned char buffer[4096];
+} fz_file_stream_escl;
+
+static int
+next_file_escl(fz_context *ctx, fz_stream *stm, size_t n)
+{
+ fz_file_stream_escl *state = stm->state;
+
+ /* n is only a hint, that we can safely ignore */
+ n = fread(state->buffer, 1, sizeof(state->buffer), state->file);
+ if (n < sizeof(state->buffer) && ferror(state->file))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "read error: %s", strerror(errno));
+ stm->rp = state->buffer;
+ stm->wp = state->buffer + n;
+ stm->pos += (int64_t)n;
+
+ if (n == 0)
+ return EOF;
+ return *stm->rp++;
+}
+
+static void
+drop_file_escl(fz_context *ctx, void *state_)
+{
+ fz_file_stream_escl *state = state_;
+ int n = fclose(state->file);
+ if (n < 0)
+ fz_warn(ctx, "close error: %s", strerror(errno));
+ fz_free(ctx, state);
+}
+
+static void
+seek_file_escl(fz_context *ctx, fz_stream *stm, int64_t offset, int whence)
+{
+ fz_file_stream_escl *state = stm->state;
+#ifdef _WIN32
+ int64_t n = _fseeki64(state->file, offset, whence);
+#else
+ int64_t n = fseeko(state->file, offset, whence);
+#endif
+ if (n < 0)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot seek: %s", strerror(errno));
+#ifdef _WIN32
+ stm->pos = _ftelli64(state->file);
+#else
+ stm->pos = ftello(state->file);
+#endif
+ stm->rp = state->buffer;
+ stm->wp = state->buffer;
+}
+
+static fz_stream *
+fz_open_file_ptr_escl(fz_context *ctx, FILE *file)
+{
+ fz_stream *stm;
+ fz_file_stream_escl *state = fz_malloc_struct(ctx, fz_file_stream_escl);
+ state->file = file;
+
+ stm = fz_new_stream(ctx, state, next_file_escl, drop_file_escl);
+ stm->seek = seek_file_escl;
+
+ return stm;
+}
+
+/**
+ * \fn SANE_Status escl_sane_decompressor(escl_sane_t *handler)
+ * \brief Function that aims to decompress the pdf image to SANE be able
+ * to read the image.
+ * This function is called in the "sane_read" function.
+ *
+ * \return SANE_STATUS_GOOD (if everything is OK, otherwise,
+ * SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
+ */
+SANE_Status
+get_PDF_data(capabilities_t *scanner, int *width, int *height, int *bps)
+{
+ int page_number = -1, page_count = -2;
+ fz_context *ctx;
+ fz_document *doc;
+ fz_pixmap *pix;
+ fz_matrix ctm;
+ fz_stream *stream;
+ unsigned char *surface = NULL; /* Image data */
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ /* Create a context to hold the exception stack and various caches. */
+ ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
+ if (!ctx)
+ {
+ DBG(1, "cannot create mupdf context\n");
+ status = SANE_STATUS_INVAL;
+ goto close_file;
+ }
+
+ /* Register the default file types to handle. */
+ fz_try(ctx)
+ fz_register_document_handlers(ctx);
+ fz_catch(ctx)
+ {
+ DBG(1, "cannot register document handlers: %s\n", fz_caught_message(ctx));
+ status = SANE_STATUS_INVAL;
+ goto drop_context;
+ }
+
+ /* Open the stream. */
+ fz_try(ctx)
+ stream = fz_open_file_ptr_escl(ctx, scanner->tmp);
+ fz_catch(ctx)
+ {
+ DBG(1, "cannot open stream: %s\n", fz_caught_message(ctx));
+ status = SANE_STATUS_INVAL;
+ goto drop_context;
+ }
+
+ /* Seek stream. */
+ fz_try(ctx)
+ fz_seek(ctx, stream, 0, SEEK_SET);
+ fz_catch(ctx)
+ {
+ DBG(1, "cannot seek stream: %s\n", fz_caught_message(ctx));
+ status = SANE_STATUS_INVAL;
+ goto drop_stream;
+ }
+
+ /* Open the document. */
+ fz_try(ctx)
+ doc = fz_open_document_with_stream(ctx, "filename.pdf", stream);
+ fz_catch(ctx)
+ {
+ DBG(1, "cannot open document: %s\n", fz_caught_message(ctx));
+ status = SANE_STATUS_INVAL;
+ goto drop_stream;
+ }
+
+ /* Count the number of pages. */
+ fz_try(ctx)
+ page_count = fz_count_pages(ctx, doc);
+ fz_catch(ctx)
+ {
+ DBG(1, "cannot count number of pages: %s\n", fz_caught_message(ctx));
+ status = SANE_STATUS_INVAL;
+ goto drop_document;
+ }
+
+ if (page_number < 0 || page_number >= page_count)
+ {
+ DBG(1, "page number out of range: %d (page count %d)\n", page_number + 1, page_count);
+ status = SANE_STATUS_INVAL;
+ goto drop_document;
+ }
+
+ /* Compute a transformation matrix for the zoom and rotation desired. */
+ /* The default resolution without scaling is 72 dpi. */
+ fz_scale(&ctm, (float)1.0, (float)1.0);
+ fz_pre_rotate(&ctm, (float)0.0);
+
+ /* Render page to an RGB pixmap. */
+ fz_try(ctx)
+ pix = fz_new_pixmap_from_page_number(ctx, doc, 0, &ctm, fz_device_rgb(ctx), 0);
+ fz_catch(ctx)
+ {
+ DBG(1, "cannot render page: %s\n", fz_caught_message(ctx));
+ status = SANE_STATUS_INVAL;
+ goto drop_document;
+ }
+
+ surface = malloc(pix->h * pix->stride);
+ memcpy(surface, pix->samples, (pix->h * pix->stride));
+
+ // If necessary, trim the image.
+ surface = escl_crop_surface(scanner, surface, pix->w, pix->h, pix->n, width, height);
+ if (!surface) {
+ DBG( 1, "Escl Pdf : Surface Memory allocation problem\n");
+ status = SANE_STATUS_NO_MEM;
+ goto drop_pix;
+ }
+ *bps = pix->n;
+
+ /* Clean up. */
+drop_pix:
+ fz_drop_pixmap(ctx, pix);
+drop_document:
+ fz_drop_document(ctx, doc);
+drop_stream:
+ fz_drop_stream(ctx, stream);
+drop_context:
+ fz_drop_context(ctx);
+
+close_file:
+ if (scanner->tmp)
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ return status;
+}
+#else
+
+SANE_Status
+get_PDF_data(capabilities_t __sane_unused__ *scanner,
+ int __sane_unused__ *width,
+ int __sane_unused__ *height,
+ int __sane_unused__ *bps)
+{
+ return (SANE_STATUS_INVAL);
+}
+
+#endif
diff --git a/backend/escl/escl_newjob.c b/backend/escl/escl_newjob.c
index 279b9df..ee8c03c 100644
--- a/backend/escl/escl_newjob.c
+++ b/backend/escl/escl_newjob.c
@@ -68,18 +68,11 @@ static const char settings[] =
" <scan:ColorMode>%s</scan:ColorMode>" \
" <scan:XResolution>%d</scan:XResolution>" \
" <scan:YResolution>%d</scan:YResolution>" \
- " <pwg:InputSource>Platen</pwg:InputSource>" \
+ " <pwg:InputSource>%s</pwg:InputSource>" \
+ " <scan:InputSource>%s</scan:InputSource>" \
+ "%s" \
"</scan:ScanSettings>";
-static char formatExtJPEG[] =
- " <scan:DocumentFormatExt>image/jpeg</scan:DocumentFormatExt>";
-
-static char formatExtPNG[] =
- " <scan:DocumentFormatExt>image/png</scan:DocumentFormatExt>";
-
-static char formatExtTIFF[] =
- " <scan:DocumentFormatExt>image/tiff</scan:DocumentFormatExt>";
-
/**
* \fn static size_t download_callback(void *str, size_t size, size_t nmemb, void *userp)
* \brief Callback function that stocks in memory the content of the 'job'. Example below :
@@ -122,7 +115,7 @@ download_callback(void *str, size_t size, size_t nmemb, void *userp)
}
/**
- * \fn char *escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *status)
+ * \fn char *escl_newjob (capabilities_t *scanner, const ESCL_Device *device, SANE_Status *status)
* \brief Function that, using curl, uploads the data (composed by the scanner capabilities) to the
* server to download the 'job' and recover the 'new job' (char *result), in LOCATION.
* This function is called in the 'sane_start' function and it's the equivalent of the
@@ -131,22 +124,23 @@ download_callback(void *str, size_t size, size_t nmemb, void *userp)
* \return result (the 'new job', situated in LOCATION)
*/
char *
-escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *status)
+escl_newjob (capabilities_t *scanner, const ESCL_Device *device, SANE_Status *status)
{
CURL *curl_handle = NULL;
+ int off_x = 0, off_y = 0;
struct uploading *upload = NULL;
struct downloading *download = NULL;
const char *scan_jobs = "/eSCL/ScanJobs";
char cap_data[PATH_MAX] = { 0 };
- char job_cmd[PATH_MAX] = { 0 };
char *location = NULL;
char *result = NULL;
char *temporary = NULL;
char *f_ext = "";
char *format_ext = NULL;
+ char duplex_mode[1024] = { 0 };
*status = SANE_STATUS_GOOD;
- if (name == NULL || scanner == NULL) {
+ if (device == NULL || scanner == NULL) {
*status = SANE_STATUS_NO_MEM;
DBG( 1, "Create NewJob : the name or the scan are invalid.\n");
return (NULL);
@@ -165,64 +159,89 @@ escl_newjob (capabilities_t *scanner, SANE_String_Const name, SANE_Status *statu
return (NULL);
}
curl_handle = curl_easy_init();
- if (scanner->format_ext == 1)
+ if (scanner->caps[scanner->source].format_ext == 1)
{
- if (!strcmp(scanner->default_format, "image/jpeg"))
- format_ext = formatExtJPEG;
- else if (!strcmp(scanner->default_format, "image/png"))
- format_ext = formatExtPNG;
- else if (!strcmp(scanner->default_format, "image/tiff"))
- format_ext = formatExtTIFF;
- else
- format_ext = f_ext;
+ char f_ext_tmp[1024];
+ snprintf(f_ext_tmp, sizeof(f_ext_tmp),
+ " <scan:DocumentFormatExt>%s</scan:DocumentFormatExt>",
+ scanner->caps[scanner->source].default_format);
+ format_ext = f_ext_tmp;
}
else
format_ext = f_ext;
- DBG( 1, "Create NewJob : %s\n", scanner->default_format);
+ if(scanner->source > PLATEN && scanner->Sources[ADFDUPLEX]) {
+ snprintf(duplex_mode, sizeof(duplex_mode),
+ " <scan:Duplex>%s</scan:Duplex>",
+ scanner->source == ADFDUPLEX ? "true" : "false");
+ }
+ DBG( 1, "Create NewJob : %s\n", scanner->caps[scanner->source].default_format);
+ if (scanner->caps[scanner->source].pos_x > scanner->caps[scanner->source].width)
+ off_x = (scanner->caps[scanner->source].pos_x > scanner->caps[scanner->source].width) / 2;
+ if (scanner->caps[scanner->source].pos_y > scanner->caps[scanner->source].height)
+ off_y = (scanner->caps[scanner->source].pos_y > scanner->caps[scanner->source].height) / 2;
if (curl_handle != NULL) {
- snprintf(cap_data, sizeof(cap_data), settings, scanner->height, scanner->width, 0, 0, scanner->default_format,
- format_ext,
- scanner->default_color, scanner->default_resolution, scanner->default_resolution);
+ char *source = (scanner->source == PLATEN ? "Platen" : "Feeder");
+ snprintf(cap_data, sizeof(cap_data), settings,
+ scanner->caps[scanner->source].height,
+ scanner->caps[scanner->source].width,
+ off_x,
+ off_y,
+ scanner->caps[scanner->source].default_format,
+ format_ext,
+ scanner->caps[scanner->source].default_color,
+ scanner->caps[scanner->source].default_resolution,
+ scanner->caps[scanner->source].default_resolution,
+ source,
+ source,
+ duplex_mode[0] == 0 ? "" : duplex_mode);
DBG( 1, "Create NewJob : %s\n", cap_data);
upload->read_data = strdup(cap_data);
upload->size = strlen(cap_data);
download->memory = malloc(1);
download->size = 0;
- strcpy(job_cmd, name);
- strcat(job_cmd, scan_jobs);
- curl_easy_setopt(curl_handle, CURLOPT_URL, job_cmd);
- if (strncmp(name, "https", 5) == 0) {
- curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
- }
+ escl_curl_url(curl_handle, device, scan_jobs);
curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, upload->read_data);
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, upload->size);
curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, download_callback);
curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *)download);
- if (curl_easy_perform(curl_handle) != CURLE_OK) {
- DBG( 1, "Create NewJob : the scanner responded incorrectly.\n");
+ CURLcode res = curl_easy_perform(curl_handle);
+ if (res != CURLE_OK) {
+ DBG( 1, "Create NewJob : the scanner responded incorrectly: %s\n", curl_easy_strerror(res));
*status = SANE_STATUS_INVAL;
}
else {
if (download->memory != NULL) {
- if (strstr(download->memory, "Location:")) {
- temporary = strrchr(download->memory, '/');
+ char *tmp_location = strstr(download->memory, "Location:");
+ if (tmp_location) {
+ temporary = strchr(tmp_location, '\r');
+ if (temporary == NULL)
+ temporary = strchr(tmp_location, '\n');
if (temporary != NULL) {
- location = strchr(temporary, '\r');
- if (location == NULL)
- location = strchr(temporary, '\n');
- else {
- *location = '\0';
- result = strdup(temporary);
- }
- DBG( 1, "Create NewJob : %s\n", result);
+ *temporary = '\0';
+ location = strrchr(tmp_location,'/');
+ if (location) {
+ result = strdup(location);
+ DBG( 1, "Create NewJob : %s\n", result);
+ *temporary = '\n';
+ }
+ }
+ if (result == NULL) {
+ DBG( 1, "Error : Create NewJob, no location: %s\n", download->memory);
+ *status = SANE_STATUS_INVAL;
}
free(download->memory);
}
else {
- DBG( 1, "Create NewJob : The creation of the failed job\n");
- *status = SANE_STATUS_INVAL;
+ DBG( 1, "Create NewJob : The creation of the failed job: %s\n", download->memory);
+ // If "409 Conflict" appear it means that there is no paper in feeder
+ if (strstr(download->memory, "409 Conflict") != NULL)
+ *status = SANE_STATUS_NO_DOCS;
+ // If "503 Service Unavailable" appear, it means that device is busy (scanning in progress)
+ else if (strstr(download->memory, "503 Service Unavailable") != NULL)
+ *status = SANE_STATUS_DEVICE_BUSY;
+ else
+ *status = SANE_STATUS_INVAL;
}
}
else {
diff --git a/backend/escl/escl_pdf.c b/backend/escl/escl_pdf.c
new file mode 100644
index 0000000..ae85a3a
--- /dev/null
+++ b/backend/escl/escl_pdf.c
@@ -0,0 +1,223 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com>
+
+ This file is part of the SANE package.
+
+ SANE is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ SANE 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
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ This file implements a SANE backend for eSCL scanners. */
+
+#define DEBUG_DECLARE_ONLY
+#include "../include/sane/config.h"
+
+#include "escl.h"
+
+#include "../include/sane/sanei.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <math.h>
+
+#include <errno.h>
+
+#if(defined HAVE_POPPLER_GLIB)
+#include <poppler/glib/poppler.h>
+#endif
+
+#include <setjmp.h>
+
+
+#if(defined HAVE_POPPLER_GLIB)
+
+#define INPUT_BUFFER_SIZE 4096
+
+static unsigned char*
+set_file_in_buffer(FILE *fp, int *size)
+{
+ char buffer[1024] = { 0 };
+ unsigned char *data = (unsigned char *)calloc(1, sizeof(char));
+ int nx = 0;
+
+ while(!feof(fp))
+ {
+ int n = fread(buffer,sizeof(char),1024,fp);
+ unsigned char *t = realloc(data, nx + n + 1);
+ if (t == NULL) {
+ DBG(10, "not enough memory (realloc returned NULL)");
+ free(data);
+ return NULL;
+ }
+ data = t;
+ memcpy(&(data[nx]), buffer, n);
+ nx = nx + n;
+ data[nx] = 0;
+ }
+ *size = nx;
+ return data;
+}
+
+static unsigned char *
+cairo_surface_to_pixels (cairo_surface_t *surface, int bps)
+{
+ int cairo_width, cairo_height, cairo_rowstride;
+ unsigned char *data, *dst, *cairo_data;
+ unsigned int *src;
+ int x, y;
+
+ cairo_width = cairo_image_surface_get_width (surface);
+ cairo_height = cairo_image_surface_get_height (surface);
+ cairo_rowstride = cairo_image_surface_get_stride (surface);
+ cairo_data = cairo_image_surface_get_data (surface);
+ data = (unsigned char*)calloc(1, sizeof(unsigned char) * (cairo_height * cairo_width * bps));
+
+ for (y = 0; y < cairo_height; y++)
+ {
+ src = (unsigned int *) (cairo_data + y * cairo_rowstride);
+ dst = data + y * (cairo_width * bps);
+ for (x = 0; x < cairo_width; x++)
+ {
+ dst[0] = (*src >> 16) & 0xff;
+ dst[1] = (*src >> 8) & 0xff;
+ dst[2] = (*src >> 0) & 0xff;
+ dst += bps;
+ src++;
+ }
+ }
+ return data;
+}
+
+SANE_Status
+get_PDF_data(capabilities_t *scanner, int *width, int *height, int *bps)
+{
+ cairo_surface_t *cairo_surface = NULL;
+ cairo_t *cr;
+ PopplerPage *page;
+ PopplerDocument *doc;
+ double dw, dh;
+ int w, h, size = 0;
+ char *data = NULL;
+ unsigned char* surface = NULL;
+ SANE_Status status = SANE_STATUS_GOOD;
+
+
+ data = (char*)set_file_in_buffer(scanner->tmp, &size);
+ if (!data) {
+ DBG(1, "Error : poppler_document_new_from_data");
+ status = SANE_STATUS_INVAL;
+ goto close_file;
+ }
+ doc = poppler_document_new_from_data(data,
+ size,
+ NULL,
+ NULL);
+
+ if (!doc) {
+ DBG(1, "Error : poppler_document_new_from_data");
+ status = SANE_STATUS_INVAL;
+ goto free_file;
+ }
+
+ page = poppler_document_get_page (doc, 0);
+ if (!page) {
+ DBG(1, "Error : poppler_document_get_page");
+ status = SANE_STATUS_INVAL;
+ goto free_doc;
+ }
+
+ poppler_page_get_size (page, &dw, &dh);
+ dw = (double)scanner->caps[scanner->source].default_resolution * dw / 72.0;
+ dh = (double)scanner->caps[scanner->source].default_resolution * dh / 72.0;
+ w = (int)ceil(dw);
+ h = (int)ceil(dh);
+ cairo_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
+ if (!cairo_surface) {
+ DBG(1, "Error : cairo_image_surface_create");
+ status = SANE_STATUS_INVAL;
+ goto free_page;
+ }
+
+ cr = cairo_create (cairo_surface);
+ if (!cairo_surface) {
+ DBG(1, "Error : cairo_create");
+ status = SANE_STATUS_INVAL;
+ goto free_surface;
+ }
+ cairo_scale (cr, (double)scanner->caps[scanner->source].default_resolution / 72.0,
+ (double)scanner->caps[scanner->source].default_resolution / 72.0);
+ cairo_save (cr);
+ poppler_page_render (page, cr);
+ cairo_restore (cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_paint (cr);
+
+ int st = cairo_status(cr);
+ if (st)
+ {
+ DBG(1, "%s", cairo_status_to_string (st));
+ status = SANE_STATUS_INVAL;
+ goto destroy_cr;
+ }
+
+ *bps = 3;
+
+ DBG(1, "Escl Pdf : Image Size [%dx%d]\n", w, h);
+
+ surface = cairo_surface_to_pixels (cairo_surface, *bps);
+ if (!surface) {
+ status = SANE_STATUS_NO_MEM;
+ DBG(1, "Escl Pdf : Surface Memory allocation problem");
+ goto destroy_cr;
+ }
+
+ // If necessary, trim the image.
+ surface = escl_crop_surface(scanner, surface, w, h, *bps, width, height);
+ if (!surface) {
+ DBG(1, "Escl Pdf Crop: Surface Memory allocation problem");
+ status = SANE_STATUS_NO_MEM;
+ }
+
+destroy_cr:
+ cairo_destroy (cr);
+free_surface:
+ cairo_surface_destroy (cairo_surface);
+free_page:
+ g_object_unref (page);
+free_doc:
+ g_object_unref (doc);
+free_file:
+ free(data);
+close_file:
+ if (scanner->tmp)
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ return status;
+}
+#else
+
+SANE_Status
+get_PDF_data(capabilities_t __sane_unused__ *scanner,
+ int __sane_unused__ *width,
+ int __sane_unused__ *height,
+ int __sane_unused__ *bps)
+{
+ return (SANE_STATUS_INVAL);
+}
+
+#endif
diff --git a/backend/escl/escl_png.c b/backend/escl/escl_png.c
index 18f6f35..cf92449 100644
--- a/backend/escl/escl_png.c
+++ b/backend/escl/escl_png.c
@@ -49,14 +49,15 @@
* \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
*/
SANE_Status
-get_PNG_data(capabilities_t *scanner, int *w, int *h, int *components)
+get_PNG_data(capabilities_t *scanner, int *width, int *height, int *bps)
{
- unsigned int width = 0; /* largeur */
- unsigned int height = 0; /* hauteur */
- int bps = 3; /* composantes d'un texel */
- unsigned char *texels = NULL; /* données de l'image */
+ unsigned int w = 0;
+ unsigned int h = 0;
+ int components = 3;
+ unsigned char *surface = NULL; /* Image data */
unsigned int i = 0;
png_byte magic[8];
+ SANE_Status status = SANE_STATUS_GOOD;
// read magic number
fread (magic, 1, sizeof (magic), scanner->tmp);
@@ -64,11 +65,8 @@ get_PNG_data(capabilities_t *scanner, int *w, int *h, int *components)
if (!png_check_sig (magic, sizeof (magic)))
{
DBG( 1, "Escl Png : PNG error is not a valid PNG image!\n");
- if (scanner->tmp) {
- fclose(scanner->tmp);
- scanner->tmp = NULL;
- }
- return (SANE_STATUS_INVAL);
+ status = SANE_STATUS_INVAL;
+ goto close_file;
}
// create a png read struct
png_structp png_ptr = png_create_read_struct
@@ -76,12 +74,8 @@ get_PNG_data(capabilities_t *scanner, int *w, int *h, int *components)
if (!png_ptr)
{
DBG( 1, "Escl Png : PNG error create a png read struct\n");
- if (scanner->tmp)
- if (scanner->tmp) {
- fclose(scanner->tmp);
- scanner->tmp = NULL;
- }
- return (SANE_STATUS_INVAL);
+ status = SANE_STATUS_INVAL;
+ goto close_file;
}
// create a png info struct
png_infop info_ptr = png_create_info_struct (png_ptr);
@@ -89,26 +83,19 @@ get_PNG_data(capabilities_t *scanner, int *w, int *h, int *components)
{
DBG( 1, "Escl Png : PNG error create a png info struct\n");
png_destroy_read_struct (&png_ptr, NULL, NULL);
- if (scanner->tmp) {
- fclose(scanner->tmp);
- scanner->tmp = NULL;
- }
- return (SANE_STATUS_INVAL);
+ status = SANE_STATUS_INVAL;
+ goto close_file;
}
// initialize the setjmp for returning properly after a libpng
// error occured
if (setjmp (png_jmpbuf (png_ptr)))
{
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
- if (texels)
- free (texels);
- fprintf(stderr,"PNG read error.\n");
- if (scanner->tmp) {
- fclose(scanner->tmp);
- scanner->tmp = NULL;
- }
+ if (surface)
+ free (surface);
DBG( 1, "Escl Png : PNG read error.\n");
- return (SANE_STATUS_INVAL);
+ status = SANE_STATUS_INVAL;
+ goto close_file;
}
// setup libpng for using standard C fread() function
// with our FILE pointer
@@ -128,63 +115,79 @@ get_PNG_data(capabilities_t *scanner, int *w, int *h, int *components)
png_set_palette_to_rgb (png_ptr);
else if (color_type != PNG_COLOR_TYPE_RGB && color_type != PNG_COLOR_TYPE_RGB_ALPHA)
{
- fprintf(stderr,"PNG format not supported.\n");
- if (scanner->tmp) {
- fclose(scanner->tmp);
- scanner->tmp = NULL;
- }
- return (SANE_STATUS_INVAL);
+ DBG(1, "PNG format not supported.\n");
+ status = SANE_STATUS_NO_MEM;
+ goto close_file;
}
+
if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- bps = 4;
+ components = 4;
else
- bps = 3;
- if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
- png_set_tRNS_to_alpha (png_ptr);
- if (bit_depth == 16)
- png_set_strip_16 (png_ptr);
- else if (bit_depth < 8)
- png_set_packing (png_ptr);
- // update info structure to apply transformations
- png_read_update_info (png_ptr, info_ptr);
- // retrieve updated information
- png_get_IHDR (png_ptr, info_ptr,
- (png_uint_32*)(&width),
- (png_uint_32*)(&height),
- &bit_depth, &color_type,
- NULL, NULL, NULL);
-
- *w = (int)width;
- *h = (int)height;
- *components = bps;
- // we can now allocate memory for storing pixel data
- texels = (unsigned char *)malloc (sizeof (unsigned char) * width
- * height * bps);
- png_bytep *row_pointers;
- // setup a pointer array. Each one points at the begening of a row.
- row_pointers = (png_bytep *)malloc (sizeof (png_bytep) * height);
- for (i = 0; i < height; ++i)
- {
- row_pointers[i] = (png_bytep)(texels +
- ((height - (i + 1)) * width * bps));
- }
- // read pixel data using row pointers
- png_read_image (png_ptr, row_pointers);
- // we don't need row pointers anymore
- scanner->img_data = texels;
- scanner->img_size = (int)(width * height * bps);
- scanner->img_read = 0;
- free (row_pointers);
- fclose(scanner->tmp);
+ components = 3;
+
+ if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha (png_ptr);
+ if (bit_depth == 16)
+ png_set_strip_16 (png_ptr);
+ else if (bit_depth < 8)
+ png_set_packing (png_ptr);
+ // update info structure to apply transformations
+ png_read_update_info (png_ptr, info_ptr);
+ // retrieve updated information
+ png_get_IHDR (png_ptr, info_ptr,
+ (png_uint_32*)(&w),
+ (png_uint_32*)(&h),
+ &bit_depth, &color_type,
+ NULL, NULL, NULL);
+
+ *bps = components;
+ // we can now allocate memory for storing pixel data
+ surface = (unsigned char *)malloc (sizeof (unsigned char) * w
+ * h * components);
+ if (!surface) {
+ DBG( 1, "Escl Png : texels Memory allocation problem\n");
+ status = SANE_STATUS_NO_MEM;
+ goto close_file;
+ }
+ png_bytep *row_pointers;
+ // setup a pointer array. Each one points at the begening of a row.
+ row_pointers = (png_bytep *)malloc (sizeof (png_bytep) * h);
+ if (!row_pointers) {
+ DBG( 1, "Escl Png : row_pointers Memory allocation problem\n");
+ free(surface);
+ status = SANE_STATUS_NO_MEM;
+ goto close_file;
+ }
+ for (i = 0; i < h; ++i)
+ {
+ row_pointers[i] = (png_bytep)(surface +
+ ((h - (i + 1)) * w * components));
+ }
+ // read pixel data using row pointers
+ png_read_image (png_ptr, row_pointers);
+
+ // If necessary, trim the image.
+ surface = escl_crop_surface(scanner, surface, w, h, components, width, height);
+ if (!surface) {
+ DBG( 1, "Escl Png : Surface Memory allocation problem\n");
+ status = SANE_STATUS_NO_MEM;
+ goto close_file;
+ }
+
+ free (row_pointers);
+
+close_file:
+ if (scanner->tmp)
+ fclose(scanner->tmp);
scanner->tmp = NULL;
- return (SANE_STATUS_GOOD);
+ return (status);
}
#else
SANE_Status
get_PNG_data(capabilities_t __sane_unused__ *scanner,
- int __sane_unused__ *w,
- int __sane_unused__ *h,
+ int __sane_unused__ *width,
+ int __sane_unused__ *height,
int __sane_unused__ *bps)
{
return (SANE_STATUS_INVAL);
diff --git a/backend/escl/escl_reset.c b/backend/escl/escl_reset.c
index 7722d89..64d779a 100644
--- a/backend/escl/escl_reset.c
+++ b/backend/escl/escl_reset.c
@@ -31,13 +31,22 @@
#include <curl/curl.h>
+static size_t
+write_callback(void __sane_unused__*str,
+ size_t __sane_unused__ size,
+ size_t nmemb,
+ void __sane_unused__ *userp)
+{
+ return nmemb;
+}
+
/**
- * \fn void escl_scanner(SANE_String_Const name, char *result)
+ * \fn void escl_scanner(const ESCL_Device *device, char *result)
* \brief Function that resets the scanner after each scan, using curl.
* This function is called in the 'sane_cancel' function.
*/
void
-escl_scanner(SANE_String_Const name, char *result)
+escl_scanner(const ESCL_Device *device, char *result)
{
CURL *curl_handle = NULL;
const char *scan_jobs = "/eSCL/ScanJobs";
@@ -46,30 +55,25 @@ escl_scanner(SANE_String_Const name, char *result)
int i = 0;
long answer = 0;
- if (name == NULL || result == NULL)
+ if (device == NULL || result == NULL)
return;
CURL_CALL:
curl_handle = curl_easy_init();
if (curl_handle != NULL) {
- strcpy(scan_cmd, name);
- strcat(scan_cmd, scan_jobs);
- strcat(scan_cmd, result);
- strcat(scan_cmd, scanner_start);
- curl_easy_setopt(curl_handle, CURLOPT_URL, scan_cmd);
- DBG( 1, "Reset Job : %s.\n", scan_cmd);
- if (strncmp(name, "https", 5) == 0) {
- DBG( 1, "Ignoring safety certificates, use https\n");
- curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
- }
+ snprintf(scan_cmd, sizeof(scan_cmd), "%s%s%s",
+ scan_jobs, result, scanner_start);
+ escl_curl_url(curl_handle, device, scan_cmd);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback);
if (curl_easy_perform(curl_handle) == CURLE_OK) {
curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &answer);
- if (i < 3 && answer == 503) {
- curl_easy_cleanup(curl_handle);
- i++;
- goto CURL_CALL;
- }
+ i++;
+ if (i >= 15) return;
}
curl_easy_cleanup(curl_handle);
+ if (SANE_STATUS_GOOD != escl_status(device,
+ PLATEN,
+ NULL,
+ NULL))
+ goto CURL_CALL;
}
}
diff --git a/backend/escl/escl_scan.c b/backend/escl/escl_scan.c
index 8f077a1..9fce801 100644
--- a/backend/escl/escl_scan.c
+++ b/backend/escl/escl_scan.c
@@ -43,13 +43,14 @@
static size_t
write_callback(void *str, size_t size, size_t nmemb, void *userp)
{
- size_t to_write = fwrite(str, size, nmemb, (FILE *)userp);
-
+ capabilities_t *scanner = (capabilities_t *)userp;
+ size_t to_write = fwrite(str, size, nmemb, scanner->tmp);
+ scanner->real_read += to_write;
return (to_write);
}
/**
- * \fn SANE_Status escl_scan(capabilities_t *scanner, SANE_String_Const name, char *result)
+ * \fn SANE_Status escl_scan(capabilities_t *scanner, const ESCL_Device *device, char *result)
* \brief Function that, after recovering the 'new job', scans the image writed in the
* temporary file, using curl.
* This function is called in the 'sane_start' function and it's the equivalent of
@@ -58,7 +59,7 @@ write_callback(void *str, size_t size, size_t nmemb, void *userp)
* \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
*/
SANE_Status
-escl_scan(capabilities_t __sane_unused__ *scanner, SANE_String_Const name, char *result)
+escl_scan(capabilities_t *scanner, const ESCL_Device *device, char *result)
{
CURL *curl_handle = NULL;
const char *scan_jobs = "/eSCL/ScanJobs";
@@ -66,34 +67,41 @@ escl_scan(capabilities_t __sane_unused__ *scanner, SANE_String_Const name, char
char scan_cmd[PATH_MAX] = { 0 };
SANE_Status status = SANE_STATUS_GOOD;
- if (name == NULL)
+ if (device == NULL)
return (SANE_STATUS_NO_MEM);
+ scanner->real_read = 0;
curl_handle = curl_easy_init();
if (curl_handle != NULL) {
- strcpy(scan_cmd, name);
- strcat(scan_cmd, scan_jobs);
- strcat(scan_cmd, result);
- strcat(scan_cmd, scanner_start);
- curl_easy_setopt(curl_handle, CURLOPT_URL, scan_cmd);
- DBG( 1, "Scan : %s.\n", scan_cmd);
- if (strncmp(name, "https", 5) == 0) {
- DBG( 1, "Ignoring safety certificates, use https\n");
- curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
- }
+ snprintf(scan_cmd, sizeof(scan_cmd), "%s%s%s",
+ scan_jobs, result, scanner_start);
+ escl_curl_url(curl_handle, device, scan_cmd);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback);
+ if (scanner->tmp)
+ fclose(scanner->tmp);
scanner->tmp = tmpfile();
if (scanner->tmp != NULL) {
- curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, scanner->tmp);
- if (curl_easy_perform(curl_handle) != CURLE_OK) {
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, scanner);
+ CURLcode res = curl_easy_perform(curl_handle);
+ if (res != CURLE_OK) {
+ DBG( 1, "Unable to scan: %s\n", curl_easy_strerror(res));
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
status = SANE_STATUS_INVAL;
+ goto cleanup;
}
- else
- curl_easy_cleanup(curl_handle);
fseek(scanner->tmp, 0, SEEK_SET);
}
else
status = SANE_STATUS_NO_MEM;
+cleanup:
+ curl_easy_cleanup(curl_handle);
+ }
+ DBG(10, "eSCL scan : [%s]\treal read (%ld)\n", sane_strstatus(status), scanner->real_read);
+ if (scanner->real_read == 0)
+ {
+ fclose(scanner->tmp);
+ scanner->tmp = NULL;
+ return SANE_STATUS_NO_DOCS;
}
return (status);
}
diff --git a/backend/escl/escl_status.c b/backend/escl/escl_status.c
index 68b51dc..7b98566 100644
--- a/backend/escl/escl_status.c
+++ b/backend/escl/escl_status.c
@@ -83,34 +83,105 @@ find_nodes_s(xmlNode *node)
return (1);
}
-/**
- * \fn static void print_xml_s(xmlNode *node, SANE_Status *status)
- * \brief Function that browses the xml file, node by node.
- * If the node 'State' is found, we are expecting to found in this node the 'Idle'
- * content (if the scanner is ready to use) and then 'status' = SANE_STATUS_GOOD.
- * Otherwise, this means that the scanner isn't ready to use.
- */
static void
-print_xml_s(xmlNode *node, SANE_Status *status)
+print_xml_job_status(xmlNode *node,
+ SANE_Status *job,
+ int *image)
{
- int x = 0;
+ while (node) {
+ if (node->type == XML_ELEMENT_NODE) {
+ if (find_nodes_s(node)) {
+ if (strcmp((const char *)node->name, "JobState") == 0) {
+ const char *state = (const char *)xmlNodeGetContent(node);
+ if (!strcmp(state, "Processing")) {
+ *job = SANE_STATUS_DEVICE_BUSY;
+ DBG(10, "jobId Processing SANE_STATUS_DEVICE_BUSY\n");
+ }
+ else if (!strcmp(state, "Completed")) {
+ *job = SANE_STATUS_GOOD;
+ DBG(10, "jobId Completed SANE_STATUS_GOOD\n");
+ }
+ else if (strcmp((const char *)node->name, "ImagesToTransfer") == 0) {
+ const char *state = (const char *)xmlNodeGetContent(node);
+ *image = atoi(state);
+ }
+ }
+ }
+ }
+ print_xml_job_status(node->children, job, image);
+ node = node->next;
+ }
+}
+static void
+print_xml_platen_and_adf_status(xmlNode *node,
+ SANE_Status *platen,
+ SANE_Status *adf,
+ const char* jobId,
+ SANE_Status *job,
+ int *image)
+{
while (node) {
if (node->type == XML_ELEMENT_NODE) {
if (find_nodes_s(node)) {
- if (strcmp((const char *)node->name, "State") == 0)
- x = 1;
+ if (strcmp((const char *)node->name, "State") == 0) {
+ DBG(10, "State\t");
+ const char *state = (const char *)xmlNodeGetContent(node);
+ if (!strcmp(state, "Idle")) {
+ DBG(10, "Idle SANE_STATUS_GOOD\n");
+ *platen = SANE_STATUS_GOOD;
+ } else if (!strcmp(state, "Processing")) {
+ DBG(10, "Processing SANE_STATUS_DEVICE_BUSY\n");
+ *platen = SANE_STATUS_DEVICE_BUSY;
+ } else {
+ DBG(10, "%s SANE_STATUS_UNSUPPORTED\n", state);
+ *platen = SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ // Thank's Alexander Pevzner (pzz@apevzner.com)
+ else if (adf && strcmp((const char *)node->name, "AdfState") == 0) {
+ const char *state = (const char *)xmlNodeGetContent(node);
+ if (!strcmp(state, "ScannerAdfLoaded")){
+ DBG(10, "ScannerAdfLoaded SANE_STATUS_GOOD\n");
+ *adf = SANE_STATUS_GOOD;
+ } else if (!strcmp(state, "ScannerAdfJam")) {
+ DBG(10, "ScannerAdfJam SANE_STATUS_JAMMED\n");
+ *adf = SANE_STATUS_JAMMED;
+ } else if (!strcmp(state, "ScannerAdfDoorOpen")) {
+ DBG(10, "ScannerAdfDoorOpen SANE_STATUS_COVER_OPEN\n");
+ *adf = SANE_STATUS_COVER_OPEN;
+ } else if (!strcmp(state, "ScannerAdfProcessing")) {
+ /* Kyocera version */
+ DBG(10, "ScannerAdfProcessing SANE_STATUS_NO_DOC\n");
+ *adf = SANE_STATUS_NO_DOCS;
+ } else if (!strcmp(state, "ScannerAdfEmpty")) {
+ DBG(10, "ScannerAdfEmpty SANE_STATUS_NO_DOCS\n");
+ /* Cannon TR4500, EPSON XP-7100 */
+ *adf = SANE_STATUS_NO_DOCS;
+ } else {
+ DBG(10, "%s SANE_STATUS_NO_DOCS\n", state);
+ *adf = SANE_STATUS_UNSUPPORTED;
+ }
+ }
+ else if (jobId && job && strcmp((const char *)node->name, "JobUri") == 0) {
+ if (strstr((const char *)xmlNodeGetContent(node), jobId)) {
+ print_xml_job_status(node, job, image);
+ }
+ }
}
- if (x == 1 && strcmp((const char *)xmlNodeGetContent(node), "Idle") == 0)
- *status = SANE_STATUS_GOOD;
}
- print_xml_s(node->children, status);
+ print_xml_platen_and_adf_status(node->children,
+ platen,
+ adf,
+ jobId,
+ job,
+ image);
node = node->next;
}
}
/**
- * \fn SANE_Status escl_status(SANE_String_Const name)
+ * \fn SANE_Status escl_status(const ESCL_Device *device)
* \brief Function that finally recovers the scanner status ('Idle', or not), using curl.
* This function is called in the 'sane_open' function and it's the equivalent of
* the following curl command : "curl http(s)://'ip':'port'/eSCL/ScannerStatus".
@@ -118,40 +189,45 @@ print_xml_s(xmlNode *node, SANE_Status *status)
* \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
*/
SANE_Status
-escl_status(SANE_String_Const name)
+escl_status(const ESCL_Device *device,
+ int source,
+ const char* jobId,
+ SANE_Status *job)
{
- SANE_Status status;
+ SANE_Status status = SANE_STATUS_DEVICE_BUSY;
+ SANE_Status platen= SANE_STATUS_DEVICE_BUSY;
+ SANE_Status adf= SANE_STATUS_DEVICE_BUSY;
CURL *curl_handle = NULL;
struct idle *var = NULL;
xmlDoc *data = NULL;
xmlNode *node = NULL;
const char *scanner_status = "/eSCL/ScannerStatus";
- char tmp[PATH_MAX] = { 0 };
+ int image = -1;
+ int pass = 0;
+reload:
- if (name == NULL)
+ if (device == NULL)
return (SANE_STATUS_NO_MEM);
+ status = SANE_STATUS_DEVICE_BUSY;
+ platen= SANE_STATUS_DEVICE_BUSY;
+ adf= SANE_STATUS_DEVICE_BUSY;
var = (struct idle*)calloc(1, sizeof(struct idle));
if (var == NULL)
return (SANE_STATUS_NO_MEM);
var->memory = malloc(1);
var->size = 0;
curl_handle = curl_easy_init();
- strcpy(tmp, name);
- strcat(tmp, scanner_status);
- curl_easy_setopt(curl_handle, CURLOPT_URL, tmp);
- DBG( 1, "Get Status : %s.\n", tmp);
- if (strncmp(name, "https", 5) == 0) {
- DBG( 1, "Ignoring safety certificates, use https\n");
- curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
- }
+
+ escl_curl_url(curl_handle, device, scanner_status);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, memory_callback_s);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)var);
- if (curl_easy_perform(curl_handle) != CURLE_OK) {
- DBG( 1, "The scanner didn't respond.\n");
+ CURLcode res = curl_easy_perform(curl_handle);
+ if (res != CURLE_OK) {
+ DBG( 1, "The scanner didn't respond: %s\n", curl_easy_strerror(res));
status = SANE_STATUS_INVAL;
goto clean_data;
}
+ DBG( 10, "eSCL : Status : %s.\n", var->memory);
data = xmlReadMemory(var->memory, var->size, "file.xml", NULL, 0);
if (data == NULL) {
status = SANE_STATUS_NO_MEM;
@@ -162,8 +238,18 @@ escl_status(SANE_String_Const name)
status = SANE_STATUS_NO_MEM;
goto clean;
}
- status = SANE_STATUS_DEVICE_BUSY;
- print_xml_s(node, &status);
+ /* Decode Job status */
+ // Thank's Alexander Pevzner (pzz@apevzner.com)
+ print_xml_platen_and_adf_status(node, &platen, &adf, jobId, job, &image);
+ if (platen != SANE_STATUS_GOOD &&
+ platen != SANE_STATUS_UNSUPPORTED) {
+ status = platen;
+ } else if (source == PLATEN) {
+ status = platen;
+ } else {
+ status = adf;
+ }
+ DBG (10, "STATUS : %s\n", sane_strstatus(status));
clean:
xmlFreeDoc(data);
clean_data:
@@ -172,5 +258,14 @@ clean_data:
curl_easy_cleanup(curl_handle);
free(var->memory);
free(var);
+ if (pass == 0 &&
+ source != PLATEN &&
+ image == 0 &&
+ (status == SANE_STATUS_GOOD ||
+ status == SANE_STATUS_UNSUPPORTED ||
+ status == SANE_STATUS_DEVICE_BUSY)) {
+ pass = 1;
+ goto reload;
+ }
return (status);
}
diff --git a/backend/escl/escl_tiff.c b/backend/escl/escl_tiff.c
index 52aec20..98bc5f3 100644
--- a/backend/escl/escl_tiff.c
+++ b/backend/escl/escl_tiff.c
@@ -50,60 +50,59 @@
* \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL)
*/
SANE_Status
-get_TIFF_data(capabilities_t *scanner, int *w, int *h, int *components)
+get_TIFF_data(capabilities_t *scanner, int *width, int *height, int *bps)
{
TIFF* tif = NULL;
- uint32 width = 0; /* largeur */
- uint32 height = 0; /* hauteur */
- unsigned char *raster = NULL; /* données de l'image */
- int bps = 4;
+ uint32 w = 0;
+ uint32 h = 0;
+ unsigned char *surface = NULL; /* image data*/
+ int components = 4;
uint32 npixels = 0;
+ SANE_Status status = SANE_STATUS_GOOD;
lseek(fileno(scanner->tmp), 0, SEEK_SET);
tif = TIFFFdOpen(fileno(scanner->tmp), "temp", "r");
if (!tif) {
DBG( 1, "Escl Tiff : Can not open, or not a TIFF file.\n");
- if (scanner->tmp) {
- fclose(scanner->tmp);
- scanner->tmp = NULL;
- }
- return (SANE_STATUS_INVAL);
+ status = SANE_STATUS_INVAL;
+ goto close_file;
}
- TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
- TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
- npixels = width * height;
- raster = (unsigned char*) malloc(npixels * sizeof (uint32));
- if (raster != NULL)
+ TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
+ TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
+ npixels = w * h;
+ surface = (unsigned char*) malloc(npixels * sizeof (uint32));
+ if (surface != NULL)
{
- DBG( 1, "Escl Tiff : Memory allocation problem.\n");
- if (scanner->tmp) {
- fclose(scanner->tmp);
- scanner->tmp = NULL;
- }
- return (SANE_STATUS_INVAL);
+ DBG( 1, "Escl Tiff : raster Memory allocation problem.\n");
+ status = SANE_STATUS_INVAL;
+ goto close_tiff;
}
- if (!TIFFReadRGBAImage(tif, width, height, (uint32 *)raster, 0))
+ if (!TIFFReadRGBAImage(tif, w, h, (uint32 *)surface, 0))
{
DBG( 1, "Escl Tiff : Problem reading image data.\n");
- if (scanner->tmp) {
- fclose(scanner->tmp);
- scanner->tmp = NULL;
- }
- return (SANE_STATUS_INVAL);
+ status = SANE_STATUS_INVAL;
+ free(surface);
+ goto close_tiff;
}
- *w = (int)width;
- *h = (int)height;
- *components = bps;
- // we don't need row pointers anymore
- scanner->img_data = raster;
- scanner->img_size = (int)(width * height * bps);
- scanner->img_read = 0;
+
+ *bps = components;
+
+ // If necessary, trim the image.
+ surface = escl_crop_surface(scanner, surface, w, h, components, width, height);
+ if (!surface) {
+ DBG( 1, "Escl Tiff : Surface Memory allocation problem\n");
+ status = SANE_STATUS_INVAL;
+ }
+
+close_tiff:
TIFFClose(tif);
- fclose(scanner->tmp);
+close_file:
+ if (scanner->tmp)
+ fclose(scanner->tmp);
scanner->tmp = NULL;
- return (SANE_STATUS_GOOD);
+ return (status);
}
#else