Java Tech: Acquire Images with TWAIN and SANE, Part 1
Scanners, digital cameras, and other image-acquisition devices are part of the
computing landscape. Despite their ubiquity, however, Java does not provide a
standard API for interacting with these devices. And yet there certainly is a
desire to have a standard API--see java.net's image acquisition API
forum for evidence of that desire. For now, we must either be content to use a
commercial API, such as Gnome's Morena,
or create our own API (to save money or implement our own features).
Welcome to a three-part series that explores the TWAIN and SANE specifications
for image acquisition, and presents TWAIN-based and SANE-based Java APIs that
I created to support image acquisition in the Java world. Because the source
code is freely available, you can customize those APIs as you see fit.
In part one of this series, you begin to discover TWAIN. You then explore a very
simple API that bridges the Java world with the TWAIN world: JTwain.
Finally, you play with a simple Swing-based application that interacts with
JTwain to select an image-acquisition device and acquire images from that
device, to be displayed within a scrollable window: JTwainDemo. Part two
increases your knowledge of TWAIN, and then builds upon the JTwain API to take
advantage of additional TWAIN features. Finally, part three concludes this series,
by exploring SANE, presenting a SANE-based API for Java, explaining the
need for two standards, and studying the goal of merging the TWAIN and SANE
standards into a unified image-acquisition standard.
Note: Because I'm working on a Windows platform, this series is biased in that
direction. If your platform is not Windows, you should still read this series.
I believe you'll find useful material that can be adapted to other platforms.
And Never the TWAIN Shall Meet
TWAIN is not an acronym, even though some believe it stands for Technology
Without An Interesting Name. From the TWAIN Working Group's FAQ (see
Resources): TWAIN is from [Rudyard] Kipling's "The Ballad of East and West"--"... and never the twain shall meet ...", reflecting the difficulty, at the
time, of connecting scanners and personal computers. It was up-cased to TWAIN
to make it more distinctive.
According to the FAQ, TWAIN is an image-capture API for the Microsoft
Windows and Apple Macintosh operating systems. That API was introduced in 1992
and version 1.9 is the most current version. Before we can build a Java API to
interact with that image-capture API, we need to understand TWAIN. The best
way to do that: obtain a copy of the TWAIN specification (see Resources). The
following sections explore that specification, in terms of the big picture and
several important details.
Note: I discuss the rationale for TWAIN not directly supporting Linux and Unix
in the final part of this series. As you will discover, it is still possible
to use TWAIN with Linux or Unix.
The Big Picture
TWAIN requires three software elements (and hardware) working together to enable image acquisition:
Application: The application presents a File menu with
Select source... and Acquire... menu items for choosing an
image-acquisition device and obtaining an image from that device, respectively.
In response to the user selecting one of those menu items, the application
sends messages (also known as events) to TWAIN. When acquiring an image, TWAIN
sends messages to the application, which the application handles in its
message-handling (also known as event-handling) loop.
Source: A source (also known as a data source) is a driver that
controls a specific image-acquisition device and is written by the device's
developer to conform to the TWAIN specification. Sources are stored in files
that end with the .ds file extension. For example, on my Windows
platform, the c:\windows\Twain_32 directory contains a file named
hpprsclt.ds. That file serves as my Hewlett Packard ScanJet 3300C
Source manager: The source manager (also known as the data
source manager) manages the interactions between applications and sources. One
of those sources is known as the default source, which is used by the
source manager in the absence of any specified source. For the Windows
platform, there are two source managers: the twain.dll 16-bit
source manager and the twain_32.dll 32-bit source manager. Both
source managers exist in my c:\windows directory. Along with
those files are
make it possible for the 32-bit source manager to enumerate 16-bit sources and
for the 16-bit source manager to enumerate 32-bit sources. In this series, the
only source manager file I'm interested in is twain_32.dll.
The application communicates directly with the source manager, the source
manager communicates directly with both the application and a source, and each
source communicates directly with the source manager and hardware. All of that
communication is illustrated in Figure 1.
Figure 1. The communicating elements of TWAIN
The Devil is in the Details
Version 1.9 of the TWAIN specification is more than 550 pages long. As I found
out, it's rather overwhelming when first encountered. To help you avoid having
to master the TWAIN specification just to read this article, I present (below)
only those details necessary for understanding JTwain; you can peruse the bulk
of the specification when you have the time.
States and sessions: Communication between TWAIN elements is defined by
a sequence of seven states, where the first three states can be found in every
session (the period of time in which an application is connected to the
source manager or the period of time in which an application is connected to a
source via the source manager): pre-session, source manager loaded, source
manager open, source open, source enabled, transfer is ready, and
transferring. A session normally transitions forward from its current state to
the next state and transitions backward from its current state to the previous
state without missing intermediate states.
In the pre-session state, the source manager exists on disk, but is not
in memory because no application has established a session with it. To start a
session with the source manager, an application must load the source manager
into memory. The session begins in the source manager loaded state, and
the source manager can accept operation messages from the application. Before
the source manager can be used to manage sources, however, it must be opened.
When that is done, the session moves forward to the source manager open
state. In that state, the source manager can provide a list of sources to the
application, can open sources, and can close sources. The source manager
remains in that state until it is closed. However, the source manager will not
close if any sources that it is managing are open.
Once the source manager has been opened, an application starts another session,
by asking the source manager to open a source. That session begins in the
source open state. The source is ready to receive source-specific
operation messages that inquire about the source's capabilities (which I'll talk
about in part two), such as the availability of an automatic document feeder.
The application next moves a source into the source enabled state,
which causes the source to display its own user interface (i.e., a dialog box),
if requested to do so by the application. After being enabled, the source
notifies the application, via the application's message loop, when it is ready
for data transfer to begin. Once that message is sent, the session moves to
the transfer is ready state. For an image transfer, the application
must inquire about image information (such as image size) before the transfer
can start. After obtaining image information, the session transitions to the
transferring state. The source transfers image data to the application.
When the transfer completes, the application informs the source manager, which
transitions the session back to either the transfer is ready or source enabled
state (depending on the message sent). Assuming the session returns to source
enabled, a subsequent message to disable the source transitions the session
back to source open, and a followup message to close the source terminates the
session. However, the application's prior session with the source manager still
The application communicates with the source manager by invoking the source
DSM_Entry()function. In turn, the source manager talks
to a source, by invoking the source's
functions have nearly identical parameter lists and are fully described in the
TWAIN specification. An application never invokes
Data structures: TWAIN specifies a variety of data structures, of which
some are used by JTwain:
TW_IDENTITYdescribes an application and
a source to the source manager,
DSM_Entry()'s return value,
TW_MEMREFserves to cast
DSM_Entry()'s final argument to an untyped pointer,
TW_USERINTERFACEhandles user interface coordination between the
application and a source,
TW_EVENTpasses events (messages, in
Windows-speak) from the application to a source,
tells the application how many more complete transfers the source currently
TW_IMAGEINFOdescribes the image data being
transferred between source and application, and
a Windows handle to the image data.
Operation triplets: An application communicates an operation request to
the source manager by passing three arguments in a
function call: a data group value, a data argument type value, and a message
value. The source manager often forwards that operation triplet to the source,
The data group value identifies an operation category:
operations control a TWAIN session,
DG_IMAGEoperations work with
image data, and
DG_AUDIOoperations work with audio data
(supported by some digital cameras--I don't discuss audio data in this
series). The data argument type value identifies the type of data being passed
as the last argument to
DAT_IDENTITYidentifies the last argument being
passed as the address of a
TW_IDENTITYdata structure. The
message value identifies a specific operation, such as
(open a source). Examples of operation triplets:
MSG_OPENDSM(open the source manager),
MSG_GET(begin transferring an image's data from a source to the
application, via the native data transfer mechanism), and
MSG_USERSELECT(inform the source manager to display a dialog box
that allows the user to select a source).
Data transfer modes: TWAIN provides three modes for transferring image
data from source to application: native, disk file, and buffered memory.
Native (which is used by JTwain and is the default transfer mode) transfers an
image as either a device-independent bitmap on Windows or a PICT bitmap on
Macintosh. Disk file (which may or may not be supported by a source) transfers
an image into a file created by the application, and using a format specified
by the application (and supported by the source). Buffered memory transfers an
image as an unformatted bitmap using one or more memory buffers. Applications
may have to loop repeatedly until all buffered image data has been retrieved.
Return codes and condition codes:
a value that identifies the status of an operation. That value is represented
at the source code level by a constant with a
Statuses tested by JTwain include success (
TWRC_FAILURE), event belongs to an application and not a source
TWRC_NOTDSEVENT), and data transfer complete
TWRC_FAILUREis returned, the
application can invoke
DSM_Entry()with the following operation
triplet to obtain a condition code that clarifies the reason for failure:
condition code is represented by a
TWCC_-prefixed constant at the
source code level. This article's version of JTwain does not examine condition
The JTwain API Library
I've implemented the JTwain API as a hybrid Java/Windows library that consists
of two Java classfiles and a Windows dynamic link library, written in C++. The
following sections tour the Java and C++ sides of the library, and provide the
instructions needed to construct that library.
Tour JTwain's Java Side
The Java side of the JTwain library consists of the files JTwain.java
and JTwainException.java. JTwain.java declares the class
JTwain, which presents three methods that use the Java Native
Interface to initialize JTwain, acquire one image from the default source, and
select the default source:
public static boolean init()loads jtwain.dll (which
contains the compiled C++ code for the Windows side of the JTwain library). If
that file is found, loads, and successfully initializes,
returns Boolean true. Otherwise,
init()returns Boolean false.
Although you can call
init()multiple times, it's good programming
practice to call that method only once. Be sure to call
before calling any other method in the
UnsatisifiedLinkErroris all you will get for your efforts.
Note: Although I could have chosen to use a static initializer to initialize JTwain,
I prefer the
init()method, as its use conveniently allows me to
dynamically reconfigure the application to either gray out or not show the
Acquire... and Select Source... menu items in the event the
library's DLL file cannot be found, or something goes wrong during the DLL's
public static native Image acquire()displays the dialog box that
is associated with the default source, so that you can configure the source.
If you click the Cancel button, this method returns the null reference.
But if you click the Scan (or similar) button, this method attempts to
scan one image from the source (no matter how many images you may have chosen,
via the dialog box). If successful, it returns an
public static native void selectSourceAsDefault()lets you choose
a new default source. If the Select button is clicked, the highlighted
source becomes the new default source (unless it already was the default). But
if you click the Cancel button, the current default source remains.
JTwainException.java declares THE class
which describes failures originating from jtwain.dll and TWAIN.
this checked exception.
Tour JTwain's C++ Side
Three files comprise the C++ side of the JTwain library: twain.h,
jtwain.h, and jtwain.cpp. twain.h is
the standard C-style TWAIN header file that describes TWAIN's public interface
to applications. Consult the Resources section to
obtain a link to that header file. jtwain.h is derived from the
JTwain class; I show you how to generate that header file in
the next section. Finally, jtwain.cpp contains the source code to
jtwain.dll. That source code consists of global variables, the
DLL's entry-point function (
DllMain()), two C++ functions that
selectSourceAsDefault() native methods, and three helper
functions that are private to the DLL.
Because jtwain.cpp is fully documented, I won't bother to discuss
that source code. Rather, I want to focus on a peculiarity that appears in the
source code, and explain my rationale for that peculiarity: instead of opening
and closing the source manager exactly once in
source manager is repeatedly opened and closed each time either of the DLL
functions corresponding to
selectSourceAsDefault() methods is called.
When I first started writing jtwain.cpp, I opened and closed the
source manager exactly once in
DllMain(). It didn't take long to
discover a problem with that approach. If the thread that invokes
DllMain() (which is usually the thread that executes a Java
main() method) differs from the thread that invokes
selectSourceAsDefault functions (which is usually the AWT
event-handling thread), the Win32
GetMessage() function (in the
acquire function) may appear to lock up.
Before the source manager can be opened, a window must be created. When
opening the source manager, that window's handle is passed to
DSM_Entry() and identifies the parent of the source manager's and
source's dialog box windows. Because
GetMessage() does not
retrieve messages for windows belonging to other threads or applications, if
one thread creates the window in
DllMain() and a different thread
GetMessage() in the DLL's
GetMessage() will not return any messages for that window. The
solution I decided upon: open the source manager each time the DLL's
selectSourceAsDefault functions are called, and close the source manager upon
exit from each function.
Now that you have some insight into the workings of the JTwain library, you'll
want to create that library's executable code. Unzip this article's
code.zip file, and you should end up with the directory structure below (assuming the c: drive):
The net, javajeff, and the final jtwain
directories correspond to the package name I've assigned to this library.
Assuming c:\unzipped\code is the current directory, construct the
Java portion of the library by executing the following command line to compile
the library's JTwain.java and JTwainException.java
If all goes well, you should observe the classfiles JTwain.class and
JTwainException.class in the jtwain subdirectory of
Before you can build the Windows portion of the library, you need to choose an
appropriate C++ compiler. I used version 5.5.1 of Borland's free C++ compiler
to compile the C++ source code. (Check Resources for a
link to that compiler.) You'll have to register with Borland (if you're not already
registered), which costs nothing.
If you prefer Microsoft's Visual C++ product, you will probably need to remove
or comment out all
#pragma argsused directives from the
jtwain.cpp source file. Borland compilers use that directive to
suppress warning messages arising from passing arguments to functions, but not
referring to those arguments inside the functions.
Complete the following steps to construct the Windows portion of the library:
Create jtwain.h. Assuming
c:\unzipped\code is the current directory,
accomplish that task by executing this command line:
javah net.javajeff.jtwain.JTwain. You should observe a
net_javajeff_jtwain_JTwain.h header file. You will need to rename
file to jtwain.h and move it into the
Create jtwain.dll. Assuming
is the current directory, and that you installed Borland C+ 5.5.1 and kept
defaults, place the following commands in a batch file, and execute the
batch file to compile
jtwain.cpp and link the resulting object file into jtwain.dll
should appear in its entirety on a single line):
bcc32 -tWD -I"c:\borland\bcc55\include;
The first command extends the path to include Borland C++ 5.5.1's binary
directory. The second command invokes
bcc32to perform the
linking tasks. The
DLL is being
-Ioption specifies the directory path to include
files. On my platform, c:\jdk1.5.0\include and
c:\jdk1.5.0\include\win32 are the locations of the JNI header files
that are needed by jtwain.cpp. Finally, the
location of library files. Assuming all goes well, you should discover a
jtwain.dll file in the c:\unzipped\code\net\javajeff\jtwain
Note: Once the DLL has been built, make it accessible to Windows by
that file into an appropriate location, such as c:\windows (under
The JTwainDemo Application
We're nearly ready to obtain images from image-acquisition devices via JTwain.
There's only one thing left to do: create a Java application that employs the
JTwain API to get those images. To save you the bother, I've created a simple
JTwainDemo application. That application consists of two source files:
ImageArea.java and JTwainDemo.java. After we compile
those source files, we'll play with JTwain.
Compile the Source Files
Complete the following steps to compile JTwainDemo's source files:
Make sure that c:\unzipped\code is the current directory.
Issue the following command line:
javac JTwainDemo.java. Success
is indicated by the appearance of five classfiles in c:\unzipped\code.
Let There Be TWAIN
java JTwainDemo to launch the JTwainDemo application. The
first item of business accomplished by the application is the initialization
of JTwain in the
main() method, as the code fragment below
if (!JTwain.init ())
System.out.println ("JTwainDemo: TWAIN not supported");
If initialization fails, the application exits after outputting an appropriate
error message to the standard output device.
After a moment, a window appears with a File menu. Open File and
you'll see three menu items: Acquire..., Select Source..., and
Exit. Click Select Source.... That menu item's action listener
executes the following code fragment:
catch (JTwainException e2)
selectSourceAsDefault() executes, it displays the dialog box
shown in Figure 2 (unless a
JTwainException object is thrown
because of some kind of failure). Because source names depend on the types of
connected TWAIN-supported devices, you might observe a different list of
Figure 2. Select Source dialog box
Select an appropriate source name (such as TWAIN_32 Sample Source) and
click the Select button. The highlighted source name identifies the new
Return to File... and select Acquire.... That menu item's action
listener executes the following code fragment:
Image im = JTwain.acquire ();
if (im == null)
jsp.getHorizontalScrollBar ().setValue (0);
jsp.getVerticalScrollBar ().setValue (0);
catch (JTwainException e2)
acquire() executes, it displays the dialog box that Figure 3
reveals (assuming TWAIN_32 Sample Source is the default source
and that a
JTwainException object has not been thrown). If the
user clicks the Cancel button,
acquire() returns null and
no new image will be displayed. But if an image is successfully acquired, the
ia.setImage (im); method call causes the image to be displayed,
and the subsequent scrollbar method calls reorient the scrollbars so that the
upper-left corner of the image appears in the upper-left corner of the window.
Figure 3. Source-specific dialog box
From the Bit-Depth field, choose either an 8-bit or a 24-bit image. If
you choose 1-bit, a
JTwainException object will thrown, because
that bit depth is not supported by JTwain. You can specify as many images as
you want, but only one image will be acquired. When you're ready to acquire an
image, click the Scan button.
Suppose you choose a 24-bit image and then click Scan. In a moment, you
would notice a colorful image in JTwainDemo's window. Figure 4 illustrates some
of that image.
Figure 4. JTwainDemo uses JTwain to acquire an image from TWAIN_32 Sample Source
Note: TWAIN_32 Sample Source is an emulated source that is part
of the Twain developer's SDK. Consult the Resources
section for a link to that SDK.
Java's lack of a standard image-acquisition API is an oversight that hopefully
will be rectified in a future release. Until that time, however, we can either
purchase a commercial API or create our own API.
We can base our API on either of the TWAIN or SANE specifications. So far,
we've only looked at TWAIN, in terms of the big picture and important details.
We have also explored the very simple TWAIN-based JTwain API and played with a
simple JTwainDemo application that demonstrates JTwain.
I have some homework for you to accomplish:
Modify the JTwain API library to include a
public static native String getDefaultSource()method, which
returns the name of the default source as a
method should throw a
JTwainExceptionobject if some kind of
failure occurs. Think carefully about the operation triplet you need to send
getDefaultSource()method by placing the following line
of code (within an appropriate
catchconstruct) after the call to
System.out.println ("Default source = " + JTwain.getDefaultSource ());
Modify JTwainDemo to save an acquired image to a file. Add a Save as...
menu item to the File menu, and provide some logic to choose a filename
and save the image to a file (with that filename) in a format of your choice.
Feel free to use the
imageiopackage, available in J2SE 1.4 and J2SE 5.0.
Next time, Java Tech digs deeper into TWAIN and incorporates new features into
the JTwain API.
- Sample code for this article
- Borland's free compiler
- TWAIN Developer's Toolkit
- TWAIN FAQ
- TWAIN Header File
- TWAIN Specification
Answers to Previous Homework
The previous Java Tech article presented you with some challenging homework on thread communication. Let's revisit that homework and investigate solutions.
Five philosophers sit around a circular table. Each philosopher alternates
between thinking and eating rice. In front of each philosopher is a bowl of
rice that is constantly replenished by a dedicated waiter. Exactly five
chopsticks are on the table, with one chopstick between each adjacent pair of
philosophers. Each philosopher must pick up both chopsticks adjacent to his/her
plate simultaneously before that philosopher can eat.
Create a Java application that simulates this behavior. Avoid deadlock and the
problem of indefinite postponement, where one or more philosophers soon starve
because philosophers adjacent to the starving philosophers are always eating.
Make sure that mutual exclusion is enforced, so that two adjacent philosophers
do not use the same chopstick at the same time.
Consult the Philos.java source code in this article's attached
code file (see Resources).
|width="1" height="1" border="0" alt=" " />|