Simple SANE Document Scanning in C on Linux
Dynamic Web TWAIN for Linux was introduced on 11/03/2016. It enables interaction with document scanners from within a browser on Linux. The underlying technology is SANE (Scanner Access Now Easy).
We do not need to write any code other than JavaScript when using Dynamic Web TWAIN. Learn how to to develop SANE document scanning applications on Linux.
Dynamic Web TWAIN is a commercial product. If that’s not an option for you, you may try building a simple SANE scanning application in C from scratch. This article discusses how to implement scanning WITHOUT Dynamic Web TWAIN.
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.