Dynamic Web TWAIN for Linux is coming soon. The underlying technology is SANE (Scanner Access Now Easy). Although we do not need to write any code other than JavaScript when using Dynamic Web TWAIN to develop SANE document scanning applications on Linux, learning the underlying technology will be helpful for a better understanding of how SANE works.
SANE Download
If SANE is not installed on your system, download it with the following command:
sudo
apt-get update
sudo
apt-get install
sane
In addition, you need to download the source code package which includes the header files for programming.
SANE Document Scanning on Ubuntu and Raspberry Pi
Prerequisites
- Read the SANE documentation to get familiar with APIs.
- Learn the code logic from scanimage.c which is included in the source code package.
SANE Workflow
The basic SANE workflow is as follows:
We can ignore sane_get_option_descriptor() and sane_control_option() to make the code as simple as possible.
Implementation
SANE Initialization:
void
init()
{
SANE_Int version_code = 0;
sane_init (&version_code, auth_callback);
printf("SANE version code: %d\n", version_code);
}
Get all connected SANE-compatible devices:
SANE_Status get_devices(const
SANE_Device ***device_list)
{
printf("Get all devices...\n");
SANE_Status sane_status = 0;
if
(sane_status = sane_get_devices (device_list, SANE_FALSE))
{
printf("sane_get_devices status: %s\n", sane_strstatus(sane_status));
}
return
sane_status;
}
Open a target device:
SANE_Status open_device(SANE_Device *device, SANE_Handle *sane_handle)
{
SANE_Status sane_status = 0;
printf("Name: %s, vendor: %s, model: %s, type: %s\n", device->name, device->model, device->vendor, device->type);
if
(sane_status = sane_open(device->name, sane_handle))
{
printf("sane_open status: %s\n", sane_strstatus(sane_status));
}
return
sane_status;
}
Scan documents:
SANE_Status start_scan(SANE_Handle sane_handle, SANE_String_Const fileName)
{
SANE_Status sane_status = 0;
device = sane_handle;
return
do_scan(fileName);
}
Write pnm header:
static
void
write_pnm_header (SANE_Frame format, int
width, int
height, int
depth, FILE
*ofp)
{
switch
(format)
{
case
SANE_FRAME_RED:
case
SANE_FRAME_GREEN:
case
SANE_FRAME_BLUE:
case
SANE_FRAME_RGB:
fprintf
(ofp, "P6\n# SANE data follows\n%d %d\n%d\n", width, height, (depth <= 8) ? 255 : 65535);
break;
default:
if
(depth == 1)
fprintf
(ofp, "P4\n# SANE data follows\n%d %d\n", width, height);
else
fprintf
(ofp, "P5\n# SANE data follows\n%d %d\n%d\n", width, height,(depth <= 8) ? 255 : 65535);
break;
}
}
Write buffer to pnm file:
while
(1)
{
double
progr;
status = sane_read (device, buffer, buffer_size, &len);
total_bytes += (SANE_Word) len;
progr = ((total_bytes * 100.) / (double) hundred_percent);
if
(progr > 100.)
progr = 100.;
if
(status != SANE_STATUS_GOOD)
{
if
(status != SANE_STATUS_EOF)
{
return
status;
}
break;
}
if
((parm.depth != 16))
fwrite
(buffer, 1, len, ofp);
else
{
#if !defined(WORDS_BIGENDIAN)
int
i, start = 0;
/* check if we have saved one byte from the last sane_read */
if
(hang_over > -1)
{
if
(len > 0)
{
fwrite
(buffer, 1, 1, ofp);
buffer[0] = (SANE_Byte) hang_over;
hang_over = -1;
start = 1;
}
}
/* now do the byte-swapping */
for
(i = start; i < (len - 1); i += 2)
{
unsigned char
LSB;
LSB = buffer[i];
buffer[i] = buffer[i + 1];
buffer[i + 1] = LSB;
}
/* check if we have an odd number of bytes */
if
(((len - start) % 2) != 0)
{
hang_over = buffer[len - 1];
len--;
}
#endif
fwrite
(buffer, 1, len, ofp);
}
Close the device:
void
close_device(SANE_Handle sane_handle)
{
sane_close(sane_handle);
}
Release all SANE resources:
void
exit()
{
sane_exit();
}
Building
Generate a symbolic link for SANE shared library on Ubuntu:
sudo
ln
–s /usr/lib/x86_64-linux-gnu/libsane.so.1 /usr/lib/libsane.so
Generate a symbolic link for SANE shared library on Raspberry Pi:
sudo
ln
–s /usr/lib/arm-linux-gnueabihf/libsane.so.1 /usr/lib/libsane.so
Specify the paths of header files in makefile:
SANE_INCLUDE=<Your SANE Package Path>/include
Build the project:
make
Run the application:
sudo
./hellosane
View the pnm image files with GIMP.