Updated 2012-03-21 21:16:22 by superlinux

WJG (10/Sep/06) A quick search for Tcl/Tk resources to access TWAIN devices doesn't reveal much. There's Xbit but this is not open source. A quick Google Search provides details of only a small handful of development resources which are quite pricey for simple one-off projects. One these firms is DOSDADI, the creators of an excellent commercial package called EZTwain Pro which does everything but they have also released the first version, EZTwain Classic, into the public domain including source code - problem solved! EZTWAIN Classic is a Windows DLL which adds a higher level of abstraction over TWAIN_32.dll shipped with Windows. Getting access to these functions was unbelievably easy using ffidl.dll and a quick look at the function declarations given in EZTWAIN.H. Further examples and details are given in the documentation for the full-product available at http://www.dosadi.com/EZTwain_User_Guide.pdf though some simple, yet consistent function renaming has taken place.

The code sample below shows how, in three lines, a TCL script can automate the scanning process. As EZTWAIN is a freebie it is limited to BMP file support only. However, this has not been to much of a hassle as I, like others that would find this useful, have probably already installed GraphicsMagick for conversions etc. I do have a fuller version that covers all the remaining functions of the EZTWAIN.DLL which I can send to anyone that asks. Happy scanning!
 #---------------
 # Tcl_EZTWAIN.tcl
 #---------------
 # William J Giddings, 2006
 #
 # Convert required functions in EZTW32.dll into Tcl proceedures using Ffidl
 #
 # EZTW32.DLL  http://www.dosadi.com/eztwain1.htm
 # ffidl.dll   http://rutherglen.ics.mq.edu.au/~steffen/tcltk/ffidl/doc/

 load ffidl05.dll

 #---------------
 # Basic Calls
 #---------------
 ffidl::callout EZ_TWAIN_AcquireToFilename {long pointer-utf8 } int [ffidl::symbol EZTW32.dll   TWAIN_AcquireToFilename]

 #---------------
 # select source and pre-scan and acquire to file
 #---------------
 proc demo {} {
  EZ_TWAIN_AcquireToFilename 0 Demo.bmp
  exit
 }

 demo

PWE (20060911) I'd be interested in the fuller version, is it too long to post here?

WJG (11/Sep/06] PWE here's the fuller version as requested. I originally posted the above few lines to show how simple the process is using Ffidl (this deserves to be in the batteries included distro's of Tcl). A more careful review of the EZTWAIN docs may result in better variable typing in the conversion calls but so far so good.
 #---------------
 # Tcl_EZTWAIN.tcl
 #---------------
 # William J Giddings, 2006
 #
 # Convert required functions in EZTW32.dll into Tcl proceedures using Ffidl
 #
 # EZTW32.DLL  http://www.dosadi.com/eztwain1.htm
 # ffidl.dll   http://rutherglen.ics.mq.edu.au/~steffen/tcltk/ffidl/doc/

 # modified EZTWAIN.H
 load ffidl05.dll

 # create some global constants:
 set TWAIN_BW      0x0001  ;# 1 bit
 set TWAIN_GRAY    0x0002  ;# 1,4,8 bit grayscale
 set TWAIN_RGB     0x0004  ;# 24-bit rgb
 set TWAIN_PALETTE 0x0008  ;# 1,4 or 8-bit palette
 set TWAIN_ANYTYPE 0x0000  ;# any of the above

 #---------------
 # Basic Calls
 #---------------
 ffidl::callout EZ_TWAIN_AcquireNative {long unsigned} long [ffidl::symbol EZTW32.dll TWAIN_AcquireNative]
 ffidl::callout EZ_TWAIN_FreeNative {long} void [ffidl::symbol EZTW32.dll TWAIN_FreeNative]
 ffidl::callout EZ_TWAIN_AcquireToClipboard {long unsigned} int [ffidl::symbol EZTW32.dll TWAIN_AcquireToClipboard]
 ffidl::callout EZ_TWAIN_AcquireToFilename {long pointer-utf8 } int [ffidl::symbol EZTW32.dll TWAIN_AcquireToFilename]
 ffidl::callout EZ_TWAIN_SelectImageSource {long} int [ffidl::symbol EZTW32.dll TWAIN_SelectImageSource]

 #---------------
 # Basic TWAIN Inquiries
 #---------------
 ffidl::callout EZ_TWAIN_IsAvailable {} int [ffidl::symbol EZTW32.dll TWAIN_IsAvailable]
 ffidl::callout EZ_TWAIN_EasyVersion {} int [ffidl::symbol EZTW32.dll TWAIN_EasyVersion]
 ffidl::callout EZ_TWAIN_State {} long [ffidl::symbol EZTW32.dll TWAIN_State]

 #---------------
 # DIB Handling Utilities
 #---------------
 ffidl::callout EZ_TWAIN_DibDepth {long} int [ffidl::symbol EZTW32.dll TWAIN_DibDepth]
 ffidl::callout EZ_TWAIN_DibWidth {long} int [ffidl::symbol EZTW32.dll TWAIN_DibWidth]
 ffidl::callout EZ_TWAIN_DibHeight {long} int [ffidl::symbol EZTW32.dll TWAIN_DibHeight]
 ffidl::callout EZ_TWAIN_DibNumColors {long} int [ffidl::symbol EZTW32.dll TWAIN_DibNumColors]

 # The Header declares TWAIN_RowSize, but EZ_TWAIN.C has TWAIN_RowBytes, but EZTW32.dll has neither!
 # ffidl::callout EZ_TWAIN_RowSize {long} int [ffidl::symbol EZTW32.dll TWAIN_RowSize]
 # ffidl::callout EZ_TWAIN_ReadRow {long int long} void [ffidl::symbol EZTW32.dll TWAIN_ReadRow]

 ffidl::callout EZ_TWAIN_DrawDibToDC {long int int int int long int int} void [ffidl::symbol EZTW32.dll TWAIN_DrawDibToDC]

 #---------------
 # BMP File Utilities
 #---------------
 ffidl::callout EZ_TWAIN_WriteNativeToFileName {long pointer-utf8} int [ffidl::symbol EZTW32.dll TWAIN_WriteNativeToFilename]
 ffidl::callout EZ_TWAIN_WriteNativeToFile {long long} long [ffidl::symbol EZTW32.dll TWAIN_WriteNativeToFile]
 ffidl::callout EZ_TWAIN_LoadNativeFromFilename {pointer-utf8} long [ffidl::symbol EZTW32.dll TWAIN_LoadNativeFromFilename]

 #---------------
 # UI Display Settings
 #---------------
 ffidl::callout EZ_TWAIN_SetHideUI {int} void [ffidl::symbol EZTW32.dll TWAIN_SetHideUI]
 ffidl::callout EZ_TWAIN_GetHIdeUI {} int [ffidl::symbol EZTW32.dll TWAIN_GetHideUI]

 #---------------
 # Application Registration
 #---------------
 ffidl::callout EZ_TWAIN_RegisterApp {int int int int pointer-utf8 pointer-utf8 pointer-utf8 pointer-utf8} void [ffidl::symbol EZTW32.dll TWAIN_RegisterApp]

 #---------------
 # Error Analysis and Reporting
 #---------------
 ffidl::callout EZ_TWAIN_GetResultCode {} unsigned [ffidl::symbol EZTW32.dll TWAIN_GetResultCode]
 ffidl::callout EZ_TWAIN_GetConditionCode {} unsigned [ffidl::symbol EZTW32.dll TWAIN_GetConditionCode]
 ffidl::callout EZ_TWAIN_ErrorBox {pointer-utf8} void [ffidl::symbol EZTW32.dll TWAIN_ErrorBox]
 ffidl::callout EZ_TWAIN_ReportLastError {pointer-utf8} void [ffidl::symbol EZTW32.dll TWAIN_ReportLastError]

 #---------------
 # TWAIN State Control
 #---------------
 ffidl::callout EZ_TWAIN_LoadSourceManager {} int [ffidl::symbol EZTW32.dll TWAIN_LoadSourceManager]
 ffidl::callout EZ_TWAIN_OpenSourceManager {long} int [ffidl::symbol EZTW32.dll TWAIN_OpenSourceManager]
 ffidl::callout EZ_TWAIN_OpenDefaultSource {} int [ffidl::symbol EZTW32.dll TWAIN_OpenDefaultSource]
 ffidl::callout EZ_TWAIN_EnableSource {long} int [ffidl::symbol EZTW32.dll TWAIN_EnableSource]
 ffidl::callout EZ_TWAIN_DisableSource {} int [ffidl::symbol EZTW32.dll TWAIN_DisableSource]
 ffidl::callout EZ_TWAIN_CloseSource {} int [ffidl::symbol EZTW32.dll TWAIN_CloseSource]
 ffidl::callout EZ_TWAIN_CloseSourceManager {long} int [ffidl::symbol EZTW32.dll TWAIN_CloseSourceManager]
 ffidl::callout EZ_TWAIN_UnloadSourceManager {} int [ffidl::symbol EZTW32.dll TWAIN_UnloadSourceManager]
 ffidl::callout EZ_TWAIN_MessageHook {long} int [ffidl::symbol EZTW32.dll TWAIN_MessageHook]
 ffidl::callout EZ_TWAIN_ModalEventLoop {} void [ffidl::symbol EZTW32.dll TWAIN_ModalEventLoop]
 ffidl::callout EZ_TWAIN_EndXfer {} int [ffidl::symbol EZTW32.dll TWAIN_EndXfer]
 ffidl::callout EZ_TWAIN_AbortAllPendingXfers {} int [ffidl::symbol EZTW32.dll TWAIN_AbortAllPendingXfers]
 ffidl::callout EZ_TWAIN_WriteDibToFile {long long} long [ffidl::symbol EZTW32.dll TWAIN_WriteDibToFile]

 #---------------
 # High-Level Capability Negotiation Functions
 #---------------
 ffidl::callout EZ_TWAIN_NegotiateXferCount {int} int [ffidl::symbol EZTW32.dll TWAIN_NegotiateXferCount]
 ffidl::callout EZ_TWAIN_NegotiatePixelTypes {unsigned} int [ffidl::symbol EZTW32.dll TWAIN_NegotiatePixelTypes]
 ffidl::callout EZ_TWAIN_SetCurrentUnits {int} int [ffidl::symbol EZTW32.dll TWAIN_SetCurrentUnits]
 ffidl::callout EZ_TWAIN_GetCurrentUnits {} int [ffidl::symbol EZTW32.dll TWAIN_GetCurrentUnits]
 ffidl::callout EZ_TWAIN_GetBitDepth {} int [ffidl::symbol EZTW32.dll TWAIN_GetBitDepth]
 ffidl::callout EZ_TWAIN_SetBitDepth {int} int [ffidl::symbol EZTW32.dll TWAIN_SetBitDepth]
 ffidl::callout EZ_TWAIN_GetPixelType {} int [ffidl::symbol EZTW32.dll TWAIN_GetPixelType]
 ffidl::callout EZ_TWAIN_SetCurrenPixelType {int} int [ffidl::symbol EZTW32.dll TWAIN_SetCurrentPixelType]
 ffidl::callout EZ_TWAIN_GetCurrentResolution {} double [ffidl::symbol EZTW32.dll TWAIN_GetCurrentResolution]
 ffidl::callout EZ_TWAIN_GetYResolution {} double [ffidl::symbol EZTW32.dll TWAIN_GetYResolution]
 ffidl::callout EZ_TWAIN_SetCurrentResolution {double} int [ffidl::symbol EZTW32.dll TWAIN_SetCurrentResolution]
 ffidl::callout EZ_TWAIN_SetContrast {double} int [ffidl::symbol EZTW32.dll TWAIN_SetContrast]
 ffidl::callout EZ_TWAIN_SetBrightness {double} int [ffidl::symbol EZTW32.dll TWAIN_SetBrightness]
 ffidl::callout EZ_TWAIN_SetXferMech {int} int [ffidl::symbol EZTW32.dll TWAIN_SetXferMech]
 ffidl::callout EZ_TWAIN_XferMEch {} void [ffidl::symbol EZTW32.dll TWAIN_XferMech]

 #---------------
 # Low-Level Capability Negotiation Functions
 #---------------
 # Setting a capability is valid only in State 4 (TWAIN_SOURCE_OPEN)
 # Getting a capability is valis in a State 4 or higher state.
 ffidl::callout EZ_TWAIN_SetCapOneValue {unsigned unsigned long} int [ffidl::symbol EZTW32.dll TWAIN_SetCapOneValue]
 ffidl::callout EZ_TWAIN_GetCapCurrent {unsigned unsigned long} int [ffidl::symbol EZTW32.dll TWAIN_GetCapCurrent]
 ffidl::callout EZ_TWAIN_ToFix32 {double} int [ffidl::symbol EZTW32.dll TWAIN_ToFix32]
 ffidl::callout EZ_TWAIN_Fix32ToFloat {long} int [ffidl::symbol EZTW32.dll TWAIN_Fix32ToFloat]
 ffidl::callout EZ_TWAIN_DS {long unsigned unsigned long} int [ffidl::symbol EZTW32.dll TWAIN_DS]
 ffidl::callout EZ_TWAIN_Mgr {long unsigned unsigned long} int [ffidl::symbol EZTW32.dll TWAIN_Mgr]

 #---------------
 # DEMO BLOCK
 #---------------

 # select source and pre-scan and acquire to file..
 proc demo1 {} {
  EZ_TWAIN_SelectImageSource 0
  EZ_TWAIN_AcquireToFilename 0 Demo1.bmp
  # exit
 }

 # scan straight to file using previous settings..
 proc demo2 {} {
  EZ_TWAIN_SetHideUI 1
  EZ_TWAIN_AcquireToFilename 0 Demo2.bmp
  # exit
 }

 # this one scans to a device independent bitmap (DIB), then saves is to file, the puts some settings..
 proc demo3 {{PageName}} {
  if {[EZ_TWAIN_OpenDefaultSource]} {
   EZ_TWAIN_SetHideUI 1
   EZ_TWAIN_SetCurrentUnits 0
   EZ_TWAIN_SetCurrentResolution 300.0
   set hd [EZ_TWAIN_AcquireNative 0 $::TWAIN_BW]
   EZ_TWAIN_WriteNativeToFileName $hd $PageName.bmp

  }
  if {[EZ_TWAIN_OpenDefaultSource]} {
    foreach i {PixelType BitDepth CurrentResolution CurrentUnits} {
      puts [EZ_TWAIN_Get$i ]
    }
  }
 }

 # getting details of current settings..
 proc demo4 {} {
   EZ_TWAIN_AcquireToFilename 0 Demo4.bmp
   if {[EZ_TWAIN_OpenDefaultSource]} {
    foreach i {PixelType BitDepth CurrentResolution} {
      puts [EZ_TWAIN_Get$i ]
    }
   }
 }

 # expirementing with scanning incremental A4 pages, convert with GraphicsMagick to display..

 namespace eval scan {
  set PageNumber 1
  set Prefix Scan
  variable display
 }

 proc demo5 {pathName} {
  #load xphoto.dll
  package require Img

  set i 1
  set base [frame $pathName]
  button $base.b$i -text scan -command demo1
  button $base.b[incr i] -text append -command {
    demo3 $scan::Prefix$scan::PageNumber
    # flip vertical
    # flop horizontal

    # scan and rotate convert with graphicsmagick and then display in label
    eval exec gm convert $scan::Prefix$scan::PageNumber.bmp -resize 800 -despeckle -rotate -90 display.gif
    $scan::display configure -image [image create photo -file display.gif]
    }

  eval pack [winfo children $base] -side left -anchor nw
  pack $base -side top -anchor nw

  set scan::display [label [winfo parent $base].lab]
  pack $scan::display -fill both -expand 1 -side left -anchor nw

 }

 demo5 .scan

 console show

How to install the required DLL files:

Copy the files ffidl05.dll and EZTW32.lib and EZTW32.dll to the folder system32 of the Windows installation directory which is usually by default C:\Windows\System32 .

Another important thing: the code says:
load ffidl05.dll

At the time of this writing Ffidl is in version 0.6 . Therefore change it to
load ffidl06.dll

which means that you have to change it according to how the name of the file ffidlnnn.dll is written.

tclsane is an old reference to a Tcl binding to a SANE scanner library.

WJG (07/12/07) It would be interesting to see this package, but the link is dead.