TechTricks
Technical answers from the trenches 
 
 
 
 

     
   
TWAIN Support in ObjectPAL, Part 2: TWAIN Support Overview
 
   
by Paul Cronk 
 Posted: 1 July 2001
 
   
 
 Applies to: Paradox 10
 
   
 
Audience: Developers
 
       
   

Note: This is Part 2 of 4. You can also:

TWAIN Support Overview

In Paradox 9, interactive TWAIN support was added in SP1. This gave the ability to acquire images from scanners and cameras into tables and forms. However, it was not until Paradox 10 did the support become available to the developer. The TWAIN type in ObjectPAL gives the developer the ability to acquire images from scanners and cameras, and placing those images into files, graphic types, and tables. The TWAIN type also contains functions to enumerate through the list of data sources on the computer. With the addition of the TWAIN type developers can add the ability to acquire and manage scanned images.

A TWAIN variable type is used to acquire images from TWAIN-compliant devices. Communication between Paradox (or more precisely the TWAIN variable) and the source manager is established by opening a session with the source manager.

The source manager is installed when the first TWAIN-compliant device is installed onto the workstation. The latest version of the twain_32.dll, which contains the source manager, is placed into the operating system directory by the installation program of a TWAIN-complaint device.

For Windows ME, and Windows 2000, Microsoft came out with WIATWAIN, an acronym for Windows Imaging Acquisition TWAIN. This means that all Windows ME and 2000 machines come with the source manager installed as part of the operating system. WIATWAIN is a windows driver and interface for data sources. Regardless of the operating system, Paradox still behaves the same way.

TWAIN support is treated like a plug-in for Paradox. The file 'pxtwn32.dll' contains the interface that Paradox uses to communicate with the source manager. If this DLL is not present (or not plugged-in), then all the functionality related to TWAIN support is effectively disabled. Note that you cannot use the Paradox SP1 version of pxtwn32.dll in the absence of its existence in Paradox 10. The only similarity between the two files is the filename. The version shipped with Paradox 9 SP1 does not provide an interface to ObjectPAL.

After a connection has been established with the source manager, the application can enumerate the available data sources, acquire images, get the default source for the machine, and set the data source for the session.

Images acquired via a TWAIN variable can be transferred to three different formats. ObjectPAL can acquire to a file, a graphic type, or a graphic field that has been placed in edit-mode. There is also an option to display the user-interface for the data source before acquiring.

Checking for TWAIN Support

To check for TWAIN support, the application would typically call isTwainAvailable() on the startup of the application, and store the result for later reference.

;# FormData1::var
var
   loTwain      Logical
endvar

;# FormData1::init
method init(var eventInfo Event)

loTwain = isTwainAvailable()

endMethod

Whenever the application is about to display user-interface items regarding acquisition, the variable loTwain is referenced. If this value returns FALSE, then the user-interface items are disabled, hidden, or removed.

This allows applications to take advantage of workstations that have TWAIN-compliant devices attached and still function with those workstations with no TWAIN-compliant devices.

Each developer/application will have their own methods of handling application settings. This example is not to point out the best method, but recommend to store the return value of isTwainAvailable() for later reference is a good idea.

Opening a Session

As stated previously, communication between the source manager and the TWAIN variable type is handled by a session. Typically, this is known as 'opening a TWAIN session'. The session is the only means to talk to the source manager. To establish a session, the function open() is called.

While not efficient, this does provide another means to verify that TWAIN support is available. If the return code from open() is false, then the TWAIN session could not be established.

Lastly, developers can code an isAssigned() call to determine if the session was established. Normally, conditional code would be wrapped around the open() call.

Like most session style objects, the close() function will terminate the session. Like the TCursor object, when the variable goes out of scope, the variable is unassigned, and the session is terminated. A simple example is described below.

;# btnExample::pushbutton
method pushbutton(var eventInfo Event)
var
   twSes   Twain
endvar

;// open a session to the source manager, and report
;// an error if one is returned.
if not twSes.open() then
   errorShow()
   return
endif

if not twSes.isAssigned() then
   ;// acquire, select source, etc.
endif

;// close the session.
twSes.close()

endMethod

Default Sources

The term default source refers to the system-wide TWAIN-compliant data source that is used by default for acquiring images. The only supported way of changing the system default data source is through the Select Source dialog. If the source for a TWAIN session is not explicity set, the session inherits the system's default data source for the acquisition.

The following example describes how to get the default source for the machine. This information is useful, if you are coding your Select Source dialog. When coding your own Select Source dialog, the application is responsible for saving, and retrieving the selected data source. The application is also responsible for ensuring the data source still exists on the system.

;# btnExample::pushbutton
method pushbutton(var eventInfo Event)
var
   twSes       Twain
   strSource   String
endvar

if not twSes.open() then
   errorShow()
   return
endif

strSource = twSes.getDefaultSource()
msgInfo ( "Default Source", strSource )

twSes.close()
endMethod

Showing the Select Source Dialog

The Select Source dialog is used to select a data source to acquire from. The data source that is selected from the Select Source dialog becomes the default data source for the machine.

;# btnExample::pushbutton
method pushbutton(var eventInfo Event)
var
   twSes          Twain
   strOldSource   String
   strNewSource   String
endvar

if not twSes.open() then
   errorShow()
   return
endif

strOldSource = twSes.getDefaultSource()
twSes.ShowSelectSourceDlg()
strNewSource = twSes.getDefaultSource()

if strOldSource = strNewSource then
   msgInfo ( "Twain", "The data source has not changed." )
else
   msgInfo ( "Twain", "The data source has changed" )
endif

twSes.close()

endMethod

Enumerating Data Sources

A TWAIN session can enumerate all the data sources on a machine. This is as simple as opening a TWAIN session and calling enumSourceNames(). This information returns the source names of the data sources located on the machine. The index of each data source is stored in the key of the returned dynArray entry. The index should only be used as a means to retrieve the data source name, and should not be stored for later use.

;# btnExample::pushbutton
method pushbutton(var eventInfo Event)
var
   twSes           Twain
   strOldSource    String
   dynSourceNames  DynArray[] String
endvar

if not twSes.open() then
   errorShow()
   return
endif

twSes.enumSourceNames( dynSourceNames )
dynSourceNames.view()

twSes.close()

endMethod

Setting the source for a session

To set a data source for the TWAIN session, the caller would use setSource(). If the source is not found in the source list, then the setSource() returns FALSE. The following example will select the first source name in the source name list.

;# BtnExample::pushbutton
Method pushbutton(var eventInfo Event)
var
   twSes           Twain
   strOldSource    String
   dynSourceNames  DynArray[] String
endvar

if not twSes.open() then
   errorShow()
   return
endif

twSes.enumSourceNames ( dynSourceNames )

;// if there was at least one source name, then set it.
if dynSourceNames.size() >= 1 then
twSes.setSource ( dynSourceNames[ 1 ] )
else
   msgInfo ( "Twain", "There are no sources available." )
endif

twSes.close()

;// ...

endMethod

Getting the source for the session

The source for the current session can be set by calling setSource(). Likewise, to retrieve the selected source use getSource(). The difference between getSource(), and getDefaultSource(), is that in the latter we are retrieving the default source for the system, and getSource() retrieves the source for the session. In the event that the source has explicitly been set by the developer, calling getSource() without setting the session source will return the same result as getDefaultSource(). For this example, we'll assume we always want to acquire from the camera, regardless of what they have selected.

;# BtnExample::pushbutton
Method pushbutton(var eventInfo Event)
var
   twSes           Twain
   strSourceName   String
   dynSourceNames  DynArray[] String
endvar

if not twSes.open() then
   errorShow()
   return
endif

strSourceName = twSes.getSource()
if strSourceName <> "QV QuickCam 2.33 32bit" then
   if not twSes.setSource ( "QV QuickCam 2.33 32bit" ) then
      msgInfo ( "Twain", "The camera isn't installed." )
      return
   endif
endif

twSes.close()

endMethod

Getting the number of sources

Earlier, an example set the source based on results from the enumSourceNames() method of the Twain type. The code relies on the size() function of the DynArray type to return the number of sources available in the array. By using getSourceCount(), the number of TWAIN-compliant data sources is retrieved.

Since at least one TWAIN-compliant device is required for TWAIN support to be available, this function should never return 0. Thus, we can safeguard our code by getting the source code after opening the TWAIN session. The example below clearly illustrates how we can produce more efficient code:

;# BtnExample::pushbutton
Method pushbutton(var eventInfo Event)
var
   twSes           Twain
   strOldSource    String
   dynSourceNames  DynArray[] String
endvar

if not twSes.open() then
   errorShow()
   return
endif

if twSes.getSourceCount() = 0 then
   errorShow()
   return
endif

twSes.enumSourceNames ( dynSourceNames )
twSes.setSource ( dynSourceNames[ 1 ] )

twSes.close()

endMethod

Acquiring Images

With the Twain type, developers can acquire images into three different formats. For each format, the user-interface can be shown or not shown.

Paradox does not support multiple image handling. Certain cameras can buffer images in the camera memory, and send them in succession. Paradox does not support ADF (automatic document feeders), such as scanners. The reason is quite simple. It makes little sense to scan 40 images into the same graphic field in a table. A graphic type can only hold one graphic, and a file can only have one filename.

Paradox does not support 32bit images well, regardless if they are scanned in, or imported from files. With other topics such as graphics handling, 24bit scanned images can be saved to disk in .gif, .jpg formats to keep the file size down.

Paradox 10 SP1 does not support acquiring to the graphic object type. An error stating that an unassigned variable was referenced that resulted from attempting to acquire to the graphic object. In fact, upon further investigation, it appears that it is calling the 'acquire to file' TWAIN function.

Acquiring to a file

To acquire to a file, only the filename is required. Like other ObjectPAL functions, if the filename is not preceded with an absolute path, the working directory is assumed.

;# BtnExample::pushbutton
Method pushbutton(var eventInfo Event)
var
   twSes         Twain
   strFileName      String
endvar

if not twSes.open() then
   errorShow()
   return
endif

strFileName = "acquired.bmp"

;// acquire to the filename, acquired.bmp.
;// show the user interface.
if not twSes.acquire ( strFileName, true ) then
   errorShow()
endif

twSes.close()

endMethod

Acquiring to a graphic object

Acquiring to a graphic object is straight-forward. The first parameter of the acquire() method is the uiObject that refers to the graphic object. The graphic object can be in run or design mode.

;# BtnExample::pushbutton
Method pushbutton(var eventInfo Event)
var
   twSes         Twain
   uiGraphic      UIObject
endvar

if not twSes.open() then
   errorShow()
   return
endif

;// acquire to the field, fldGraphic.
;// show the user interface.
uiGraphic = fldGraphic

if not twSes.acquire (uiGraphic, true ) then
   errorShow()
endif

twSes.close()

endMethod

Acquiring to a graphic field object

Acquiring to a graphic field is a little more complex than a graphic object. The form must be in edit mode. The uiObject refers to the graphic field. If these two conditions are not met, then the acquire method will log an error to the error stack.

;# BtnExample::pushbutton
Method pushbutton(var eventInfo Event)
var
   twSes         Twain
   uiGraphic      UIObject
endvar

if not twSes.open() then
   errorShow()
   return
endif

;// place the form in edit mode.
;// acquire to the graphic field object, fldGraphic.
;// show the user interface.
fldGraphic.edit()

uiGraphic = fldGraphic

if not twSes.acquire (uiGraphic, true ) then
   errorShow()
endif

twSes.close()

;// end Edit mode which will post the record.
fldGraphic.endEdit()

endMethod

Acquiring to a graphic type

This example does not work in Paradox 10 SP1. Acquiring to a graphic type returns an error stating usage of an unassigned variable. However, for completeness, the example, plus an explanation is listed here. Its intended functionality is the ability to acquire an image from a data source and place the image into the graphic object. Then the graphic object can be written to disk, stored into a table, or displayed on screen.
;# BtnExample::pushbutton
Method pushbutton(var eventInfo Event)
var
   twSes         Twain
   gAcquired     Graphic
   uiGraphic     UIObject
endvar

if not twSes.open() then
   errorShow()
   return
endif

;// acquire to the graphic type, fldGraphic.
;// show the user interface.
if not twSes.acquire (gAcquired, true ) then
   errorShow()
endif

qAcquired.writeToFile ( "c:\\temp\\acquired.bmp" )

twSes.close()

endMethod

Next Part: Part 3: Function Reference

Previous: Part 1: Definitions, Requirements, and Introduction

 

       

Top

Feedback About Paradox Delphi Assorted Web Stuff
 
 
Copyright © 2000-2004, techtricks.com; All Rights Reserved.
Acknowledgements, Disclaimers, Terms and Conditions.
Article last updated on 05 June 2003

 

Other Sites: Paradox, Delphi, Perl, Web Stuff, and More


 

[- End -]