Commit 7c34046a authored by Touboul Nathane's avatar Touboul Nathane Committed by Olaf Meeuwissen
Browse files

Add escl backend

parent fdcb2fa5
......@@ -12,3 +12,7 @@ root = true ; look no further
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
[backend/escl/*]
indent_size = 4
indent_style = space
......@@ -6,7 +6,7 @@
variables:
REGISTRY_HUB: "registry.gitlab.com/sane-project/ci-envs"
CONFIGURE_MINI: "--enable-silent-rules"
CONFIGURE_FULL: "--with-usb --enable-avahi --enable-pnm-backend"
CONFIGURE_FULL: "--with-usb --enable-avahi --enable-pnm-backend --with-libcurl"
stages:
- tarball
......
......@@ -32,6 +32,7 @@ Backends:
epson: Karl Heinz Kremer
epson2: Alessandro Zummo
epsonds: Alessandro Zummo
escl: Touboul Nathane, Thierry HUCHARD (*)
fujitsu: Randolph Bentson, Frederik Ramm, Oliver Schirrmeister,
m. allan noah (*)
genesys: Henning Geinitz, Gerhard Jaeger (*), Stéphane Voltz,
......@@ -248,9 +249,11 @@ Sergey Vlasov <vsu@altlinux.ru>
Simon Krix <kinsei@users.sourceforge.net>
Simon Munton <simon@munton.demon.co.uk>
Stéphane Voltz <stef.dev@free.fr>
Thierry HUCHARD <thierry@ordissimo.com>
Thomas Soumarmon <soumarmt@nerim.net>
Tom Martone <tom@martoneconsulting.com>
Tom Wang <tom.wang@mustek.com.tw>
Touboul Nathane <nathane.touboul@gmail.com>
Tristan Tarrant <ttarrant@etnoteam.it>
Troy Rollo <sane@troy.rollo.name>
Ullrich Sigwanz <usigwanz@freesurf.ch>
......
......@@ -611,6 +611,26 @@ for be in ${BACKENDS}; do
fi
;;
escl)
if test "x${enable_avahi}" != "xyes"; then
echo "*** $be backend requires AVAHI library - $DISABLE_MSG"
backend_supported="no"
fi
if test "x${with_libcurl}" != "xyes"; then
echo "*** $be backend requires cURL library - $DISABLE_MSG"
backend_supported="no"
fi
if test "x${have_libxml}" != "xyes"; then
echo "*** $be backend requires XML library - $DISABLE_MSG"
backend_supported="no"
fi
# FIXME: Remove when PNG and/or PDF support have been added.
if test "x${sane_cv_use_libjpeg}" != "xyes"; then
echo "*** $be backend currently requires JPEG library - $DISABLE_MSG"
backend_supported="no"
fi
;;
gphoto2)
if test "${HAVE_GPHOTO2}" != "true" \
|| test "${sane_cv_use_libjpeg}" != "yes"; then
......
......@@ -68,8 +68,8 @@ BACKEND_CONFS= abaton.conf agfafocus.conf apple.conf artec.conf \
canon_pp.conf cardscan.conf coolscan2.conf coolscan3.conf \
coolscan.conf dc210.conf dc240.conf dc25.conf \
dell1600n_net.conf dmc.conf epjitsu.conf epson2.conf \
epson.conf epsonds.conf fujitsu.conf genesys.conf gphoto2.conf \
gt68xx.conf hp3900.conf hp4200.conf hp5400.conf \
epson.conf epsonds.conf escl.conf fujitsu.conf genesys.conf \
gphoto2.conf gt68xx.conf hp3900.conf hp4200.conf hp5400.conf \
hp.conf hpsj5s.conf hs2p.conf ibm.conf kodak.conf kodakaio.conf\
kvs1025.conf \
leo.conf lexmark.conf ma1509.conf magicolor.conf \
......@@ -161,7 +161,7 @@ be_convenience_libs = libabaton.la libagfafocus.la \
libcoolscan2.la libcoolscan3.la libdc25.la \
libdc210.la libdc240.la libdell1600n_net.la \
libdmc.la libdll.la libdll_preload.la libepjitsu.la libepson.la \
libepson2.la libepsonds.la libfujitsu.la libgenesys.la \
libepson2.la libepsonds.la libescl.la libfujitsu.la libgenesys.la \
libgphoto2_i.la libgt68xx.la libhp.la \
libhp3500.la libhp3900.la libhp4200.la \
libhp5400.la libhp5590.la libhpljm1005.la \
......@@ -194,8 +194,8 @@ be_dlopen_libs = libsane-abaton.la libsane-agfafocus.la \
libsane-coolscan2.la libsane-coolscan3.la libsane-dc25.la \
libsane-dc210.la libsane-dc240.la libsane-dell1600n_net.la \
libsane-dmc.la libsane-epjitsu.la libsane-epson.la \
libsane-epson2.la libsane-epsonds.la libsane-fujitsu.la libsane-genesys.la \
libsane-gphoto2.la libsane-gt68xx.la libsane-hp.la \
libsane-epson2.la libsane-epsonds.la libsane-escl.la libsane-fujitsu.la \
libsane-genesys.la libsane-gphoto2.la libsane-gt68xx.la libsane-hp.la \
libsane-hp3500.la libsane-hp3900.la libsane-hp4200.la \
libsane-hp5400.la libsane-hp5590.la libsane-hpljm1005.la \
libsane-hpsj5s.la libsane-hs2p.la libsane-ibm.la libsane-kodak.la libsane-kodakaio.la\
......@@ -434,6 +434,21 @@ libsane_dmc_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
libsane_dmc_la_LIBADD = $(COMMON_LIBS) libdmc.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo ../sanei/sanei_config2.lo sane_strstatus.lo ../sanei/sanei_scsi.lo $(SCSI_LIBS) $(RESMGR_LIBS)
EXTRA_DIST += dmc.conf.in
if have_libavahi
if have_libcurl
if have_libxml2
libescl_la_SOURCES = escl/escl.c escl/escl_capabilities.c escl/escl_devices.c escl/escl.h escl/escl_newjob.c escl/escl_reset.c escl/escl_scan.c escl/escl_status.c
libescl_la_CPPFLAGS = $(AM_CPPFLAGS) $(JPEG_CFLAGS) $(XML_CFLAGS) $(libcurl_CFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=escl
nodist_libsane_escl_la_SOURCES = escl-s.c
libsane_escl_la_CPPFLAGS = $(AM_CPPFLAGS) $(JPEG_CFLAGS) $(XML_CFLAGS) $(libcurl_CFLAGS) $(AVAHI_CFLAGS) -DBACKEND_NAME=escl
libsane_escl_la_LDFLAGS = $(DIST_SANELIBS_LDFLAGS)
libsane_escl_la_LIBADD = $(COMMON_LIBS) libescl.la ../sanei/sanei_init_debug.lo ../sanei/sanei_constrain_value.lo ../sanei/sanei_config.lo sane_strstatus.lo $(JPEG_LIBS) $(XML_LIBS) $(libcurl_LIBS) $(AVAHI_LIBS)
endif
endif
endif
EXTRA_DIST += escl.conf.in
libepjitsu_la_SOURCES = epjitsu.c epjitsu.h epjitsu-cmd.h
libepjitsu_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=epjitsu
......
# escl.conf -- ESCL configuration
# Lines starting with a # or a ; are comments. Comments must be on a
# line of their own. End-of-line comments are not supported.
# Explanation : if you can't detect your device but it's an eSCL device, modify this escl conf' file to use your device.
# -> uncomment the lines below, from '[device]' to 'port'.
# -> put your device name instead of 'EPSON X'.
# -> put your type of protocol instead of 'https' : http or https.
# -> put your device ip instead of '123.456.789.10'.
# -> put the port that you use instead of '88'.
# For example, the lines below are for one device, but if you have several devices to use, you can duplicate the lines below as many times as you have devices.
#[device]
#model EPSON X
#type https
#ip 123.456.789.10
#port 88
This diff is collapsed.
/* sane - Scanner Access Now Easy.
Copyright (C) 2019 Touboul Nathane
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. */
#ifndef __ESCL_H__
#define __ESCL_H__
#include "../include/sane/config.h"
#if !(HAVE_LIBCURL && defined(WITH_AVAHI) && defined(HAVE_LIBXML2))
#error "The escl backend requires libcurl, libavahi and libxml2"
#endif
#ifndef HAVE_LIBJPEG
/* FIXME: Make JPEG support optional.
Support for PNG and PDF is to be added later but currently only
JPEG is supported. Absence of JPEG support makes the backend a
no-op at present.
*/
#error "The escl backend currently requires libjpeg"
#endif
#include "../include/sane/sane.h"
#include <stdio.h>
#ifndef BACKEND_NAME
#define BACKEND_NAME escl
#endif
#define ESCL_CONFIG_FILE "escl.conf"
typedef struct {
int p1_0;
int p2_0;
int p3_3;
int DocumentType;
int p4_0;
int p5_0;
int p6_1;
int reserve[11];
} ESCL_SCANOPTS;
typedef struct ESCL_Device {
struct ESCL_Device *next;
char *model_name;
int port_nb;
char *ip_address;
char *type;
} ESCL_Device;
typedef struct capabilities
{
int height;
int width;
int pos_x;
int pos_y;
SANE_String default_color;
SANE_String_Const default_format;
SANE_Int default_resolution;
int MinWidth;
int MaxWidth;
int MinHeight;
int MaxHeight;
int MaxScanRegions;
SANE_String_Const *ColorModes;
int ColorModesSize;
SANE_String_Const *ContentTypes;
int ContentTypesSize;
SANE_String_Const *DocumentFormats;
int DocumentFormatsSize;
SANE_Int *SupportedResolutions;
int SupportedResolutionsSize;
SANE_String_Const *SupportedIntents;
int SupportedIntentsSize;
SANE_String_Const SupportedIntentDefault;
int MaxOpticalXResolution;
int RiskyLeftMargin;
int RiskyRightMargin;
int RiskyTopMargin;
int RiskyBottomMargin;
FILE *tmp;
int format_ext;
} capabilities_t;
typedef struct {
int XRes;
int YRes;
int Left;
int Top;
int Right;
int Bottom;
int ScanMode;
int ScanMethod;
ESCL_SCANOPTS opts;
} ESCL_ScanParam;
enum
{
OPT_NUM_OPTS = 0,
OPT_MODE_GROUP,
OPT_MODE,
OPT_RESOLUTION,
OPT_PREVIEW,
OPT_GRAY_PREVIEW,
OPT_GEOMETRY_GROUP,
OPT_TL_X,
OPT_TL_Y,
OPT_BR_X,
OPT_BR_Y,
NUM_OPTIONS
};
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);
#endif
/* sane - Scanner Access Now Easy.
Copyright (C) 2019 Touboul Nathane
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. */
#include "escl.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <libxml/parser.h>
#include "../include/sane/saneopts.h"
struct cap
{
char *memory;
size_t size;
};
/**
* \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
*/
static SANE_String_Const
convert_elements(SANE_String_Const str)
{
if (strcmp(str, "Grayscale8") == 0)
return (SANE_VALUE_SCAN_MODE_GRAY);
else if (strcmp(str, "RGB24") == 0)
return (SANE_VALUE_SCAN_MODE_COLOR);
return (NULL);
}
/**
* \fn static SANE_String_Const *char_to_array(SANE_String_Const *tab, int *tabsize, SANE_String_Const mode, int good_array)
* \brief Function that creates the character arrays to put inside :
* the 'color modes', the 'content types', the 'document formats' and the 'supported intents'.
*
* \return board (the allocated array)
*/
static SANE_String_Const *
char_to_array(SANE_String_Const *tab, int *tabsize, SANE_String_Const mode, int good_array)
{
SANE_String_Const *board = NULL;
int i = 0;
SANE_String_Const convert = NULL;
if (mode == NULL)
return (tab);
if (good_array != 0) {
convert = convert_elements(mode);
if (convert == NULL)
return (tab);
}
else
convert = mode;
for (i = 0; i < (*tabsize); i++) {
if (strcmp(tab[i], convert) == 0)
return (tab);
}
(*tabsize)++;
if (*tabsize == 1)
board = (SANE_String_Const *)malloc(sizeof(SANE_String_Const) * (*tabsize) + 1);
else
board = (SANE_String_Const *)realloc(tab, sizeof(SANE_String_Const) * (*tabsize) + 1);
board[*tabsize - 1] = (SANE_String_Const)strdup(convert);
board[*tabsize] = NULL;
return (board);
}
/**
* \fn static SANE_Int *int_to_array(SANE_Int *tab, int *tabsize, int cont)
* \brief Function that creates the integer array to put inside the 'supported resolutions'.
*
* \return board (the allocated array)
*/
static SANE_Int *
int_to_array(SANE_Int *tab, int *tabsize, int cont)
{
SANE_Int *board = NULL;
int i = 0;
for (i = 0; i < (*tabsize); i++) {
if (tab[i] == cont)
return (tab);
}
(*tabsize)++;
if (*tabsize == 1) {
(*tabsize)++;
board = malloc(sizeof(SANE_Int *) * (*tabsize) + 1);
}
else
board = realloc(tab, sizeof(SANE_Int *) * (*tabsize) + 1);
board[0] = *tabsize - 1;
board[*tabsize - 1] = cont;
board[*tabsize] = -1;
return (board);
}
/**
* \fn static size_t memory_callback_c(void *contents, size_t size, size_t nmemb, void *userp)
* \brief Callback function that stocks in memory the content of the scanner capabilities.
*
* \return realsize (size of the content needed -> the scanner capabilities)
*/
static size_t
memory_callback_c(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct cap *mem = (struct cap *)userp;
char *str = realloc(mem->memory, mem->size + realsize + 1);
if (str == NULL) {
fprintf(stderr, "not enough memory (realloc returned NULL)\n");
return (0);
}
mem->memory = str;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size = mem->size + realsize;
mem->memory[mem->size] = 0;
return (realsize);
}
/**
* \fn static int find_nodes_c(xmlNode *node)
* \brief Function that browses the xml file and parses it, to find the xml children node.
* --> to recover the scanner capabilities.
*
* \return 0 if a xml child node is found, 1 otherwise
*/
static int
find_nodes_c(xmlNode *node)
{
xmlNode *child = node->children;
while (child) {
if (child->type == XML_ELEMENT_NODE)
return (0);
child = child->next;
}
return (1);
}
/**
* \fn static int find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner)
* \brief Function that searchs in the xml file if a scanner capabilitie stocked
* in one of the created array (character/integer array) is found.
*
* \return 0
*/
static int
find_valor_of_array_variables(xmlNode *node, capabilities_t *scanner)
{
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);
else if (strcmp(name, "ContentType") == 0)
scanner->ContentTypes = char_to_array(scanner->ContentTypes, &scanner->ContentTypesSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
else if (strcmp(name, "DocumentFormat") == 0)
scanner->DocumentFormats = char_to_array(scanner->DocumentFormats, &scanner->DocumentFormatsSize, (SANE_String_Const)xmlNodeGetContent(node), 0);
else if (strcmp(name, "DocumentFormatExt") == 0)
scanner->format_ext = 1;
else if (strcmp(name, "Intent") == 0)
scanner->SupportedIntents = char_to_array(scanner->SupportedIntents, &scanner->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)));
return (0);
}
/**
* \fn static int find_valor_of_int_variables(xmlNode *node, capabilities_t *scanner)
* \brief Function that searchs in the xml file if a integer scanner capabilitie is found.
* The integer scanner capabilities that are interesting are :
* MinWidth, MaxWidth, MaxHeight, MinHeight, MaxScanRegions, MaxOpticalXResolution,
* RiskyLeftMargin, RiskyRightMargin, RiskyTopMargin, RiskyBottomMargin.
*
* \return 0
*/
static int
find_valor_of_int_variables(xmlNode *node, capabilities_t *scanner)
{
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));
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));
}
else if (strcmp(name, "MinHeight") == 0)
scanner->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));
}
else if (strcmp(name, "MaxScanRegions") == 0)
scanner->MaxScanRegions = atoi((const char *)xmlNodeGetContent(node));
else if (strcmp(name, "MaxOpticalXResolution") == 0)
scanner->MaxOpticalXResolution = atoi((const char *)xmlNodeGetContent(node));
else if (strcmp(name, "RiskyLeftMargin") == 0)
scanner->RiskyLeftMargin = atoi((const char *)xmlNodeGetContent(node));
else if (strcmp(name, "RiskyRightMargin") == 0)
scanner->RiskyRightMargin = atoi((const char *)xmlNodeGetContent(node));
else if (strcmp(name, "RiskyTopMargin") == 0)
scanner->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);
return (0);
}
/**
* \fn static int find_true_variables(xmlNode *node, capabilities_t *scanner)
* \brief Function that searchs in the xml file if we find a scanner capabilitie stocked
* in one of the created array (character/integer array),
* or, if we find a integer scanner capabilitie.
*
* \return 0
*/
static int
find_true_variables(xmlNode *node, capabilities_t *scanner)
{
const char *name = (const char *)node->name;
if (strcmp(name, "MinWidth") == 0 || strcmp(name, "MaxWidth") == 0 || strcmp(name, "MinHeight") == 0 || strcmp(name, "MaxHeight") == 0
|| strcmp(name, "MaxScanRegions") == 0 || strcmp(name, "ColorMode") == 0 || strcmp(name, "ContentType") == 0
|| strcmp(name, "DocumentFormat") == 0 || strcmp(name, "XResolution") == 0 || strcmp(name, "Intent") == 0
|| strcmp(name, "MaxOpticalXResolution") == 0 || strcmp(name, "RiskyLeftMargin") == 0 || strcmp(name, "RiskyRightMargin") == 0
|| strcmp(name, "RiskyTopMargin") == 0 || strcmp(name, "RiskyBottomMargin") == 0)
find_valor_of_int_variables(node, scanner);
return (0);
}
/**
* \fn static int print_xml_c(xmlNode *node, capabilities_t *scanner)
* \brief Function that browses the xml file, node by node.
*
* \return 0
*/
static int
print_xml_c(xmlNode *node, capabilities_t *scanner)
{
while (node) {
if (node->type == XML_ELEMENT_NODE) {
if (find_nodes_c(node))
find_true_variables(node, scanner);
}
print_xml_c(node->children, scanner);
node = node->next;
}
return (0);
}
/**
* \fn capabilities_t *escl_capabilities(SANE_String_Const name, 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".
*
* \return scanner (the structure that stocks all the capabilities elements)
*/
capabilities_t *
escl_capabilities(SANE_String_Const name, 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;
const char *scanner_capabilities = "/eSCL/ScannerCapabilities";
char tmp[PATH_MAX] = { 0 };
*status = SANE_STATUS_GOOD;
if (name == NULL)
*status = SANE_STATUS_NO_MEM;
var = (struct cap *)calloc(1, sizeof(struct cap));
if (var == NULL)
*status = SANE_STATUS_NO_MEM;
var->memory = malloc(1);
var->size = 0;
curl_global_init(CURL_GLOBAL_ALL);
curl_handle = curl_easy_init();
strcpy(tmp, name);