From ad38bc6ecb80ddeb562841b33258dd53659b1da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 24 Aug 2020 18:44:51 +0200 Subject: New upstream version 1.0.31 --- backend/escl/escl.c | 893 ++++++++++++++++++++++++++++++--------- backend/escl/escl.h | 65 ++- backend/escl/escl_capabilities.c | 194 ++++++--- backend/escl/escl_crop.c | 102 +++++ backend/escl/escl_jpeg.c | 69 ++- backend/escl/escl_mupdf.c | 256 +++++++++++ backend/escl/escl_newjob.c | 115 ++--- backend/escl/escl_pdf.c | 223 ++++++++++ backend/escl/escl_png.c | 157 +++---- backend/escl/escl_reset.c | 42 +- backend/escl/escl_scan.c | 48 ++- backend/escl/escl_status.c | 159 +++++-- backend/escl/escl_tiff.c | 71 ++-- 13 files changed, 1877 insertions(+), 517 deletions(-) create mode 100644 backend/escl/escl_crop.c create mode 100644 backend/escl/escl_mupdf.c create mode 100644 backend/escl/escl_pdf.c (limited to 'backend/escl') 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 +#include #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 + + 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 +#include + +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 + + 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 +#include +#include + +#include + +#if(defined HAVE_MUPDF) +#include +#endif + +#include + + +#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[] = " %s" \ " %d" \ " %d" \ - " Platen" \ + " %s" \ + " %s" \ + "%s" \ ""; -static char formatExtJPEG[] = - " image/jpeg"; - -static char formatExtPNG[] = - " image/png"; - -static char formatExtTIFF[] = - " image/tiff"; - /** * \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), + " %s", + 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), + " %s", + 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 + + 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 +#include +#include +#include +#include + +#include + +#if(defined HAVE_POPPLER_GLIB) +#include +#endif + +#include + + +#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 +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 -- cgit v1.2.3