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