{DELPHI IMPLEMENTATION OF TWAIN INTERFACE} {december 2003®, initially created by Gustavo Daud} {This is my newest contribution for Delphi comunity, a powerfull} {implementation of latest Twain features. As you know, twain is } {the most common library to acquire images from most acquisition} {devices such as Scanners and Web-Cameras.} {Twain library is a bit different from other libraries, because} {most of the hard work can be done by a a single method. Also it} {automatically changes in the application message loop, which is} {not a simple task, at least in delphi VCL.} {It is not 100% sure to to Twain not to be installed in Windows,} {as it ships with Windows and later and with most of the } {acquisition device drivers (automatically with their installation)} {This library dynamically calls the library, avoiding the application} {hand when it is not present.} {Also, as in most of my other components, I included a trigger} {to allow the component to work without the heavy delphi VCL} {for small final executables. To enable, edit DelphiTwain.inc} {20/01/2004 - Some updates and bug fixes by Nemeth Peter} {$INCLUDE DelphiTwain.inc} unit DelphiTwain; interface {Used units} uses Twain, Windows {$IFNDEF DONTUSEVCL}, Classes, SysUtils, Graphics{$ENDIF}, DelphiTwainUtils; const {Name of the Twain library for 32 bits enviroment} TWAINLIBRARY = 'TWAIN_32.DLL'; VIRTUALWIN_CLASSNAME = 'DELPHITWAIN_VIRTUALWINDOW'; const {Error codes} ERROR_BASE = 300; ERROR_INT16: TW_INT16 = HIGH(TW_INT16); type {From twain} TW_STR255 = Twain.TW_STR255; {Forward declaration} TDelphiTwain = class; {Component kinds} {$IFDEF DONTUSEVCL} TTwainComponent = TObject; {$ELSE} TTwainComponent = TComponent; {$ENDIF} {File formats} TTwainFormat = (tfTIFF, tfPict, tfBMP, tfXBM, tfJPEG, tfFPX, tfTIFFMulti, tfPNG, tfSPIFF, tfEXIF, tfUnknown); {Twain units} TTwainUnit = (tuInches, tuCentimeters, tuPicas, tuPoints, tuTwips, tuPixels, tuUnknown); TTwainUnitSet = set of TTwainUnit; {Twain pixel flavor} TTwainPixelFlavor = (tpfChocolate, tpfVanilla, tpfUnknown); TTwainPixelFlavorSet = set of TTwainPixelFlavor; {Twain pixel type} TTwainPixelType = (tbdBw, tbdGray, tbdRgb, tbdPalette, tbdCmy, tbdCmyk, tbdYuv, tbdYuvk, tbdCieXYZ, tbdUnknown); TTwainPixelTypeSet = set of TTwainPixelType; {Twain bit depth} TTwainBitDepth = array of TW_UINT16; {Twain resolutions} TTwainResolution = array of Extended; {Events} TOnTwainError = procedure(Sender: TObject; const Index: Integer; ErrorCode, Additional: Integer) of object; TOnTwainAcquire = procedure(Sender: TObject; const Index: Integer; Image: {$IFNDEF DONTUSEVCL}TBitmap{$ELSE}HBitmap{$ENDIF}; var Cancel: Boolean) of object; TOnAcquireProgress = procedure(Sender: TObject; const Index: Integer; const Image: HBitmap; const Current, Total: Integer) of object; TOnSourceNotify = procedure(Sender: TObject; const Index: Integer) of object; TOnSourceFileTransfer = procedure(Sender: TObject; const Index: Integer; Filename: TW_STR255; Format: TTwainFormat; var Cancel: Boolean) of object; {Avaliable twain languages} TTwainLanguage = ({-1}tlUserLocale, tlDanish, tlDutch, tlInternationalEnglish, tlFrenchCanadian, tlFinnish, tlFrench, tlGerman, tlIcelandic, tlItalian, tlNorwegian, tlPortuguese, tlSpanish, tlSwedish, tlUsEnglish, tlAfrikaans, tlAlbania, tlArabic, tlArabicAlgeria, tlArabicBahrain, {18} tlArabicEgypt, tlArabicIraq, tlArabJordan, tlArabicKuwait, tlArabicLebanon, tlArabicLibya, tlArabicMorocco, tlArabicOman, tlArabicQatar, tlArabicSaudiarabia, tlArabicSyria, tlArabicTunisia, tlArabicUae, tlArabicYemen, tlBasque, tlByelorussian, tlBulgarian, {35} tlCatalan, tlChinese, tlChineseHongkong, tlChinesePeoplesRepublic, tlChineseSingapore, tlChineseSimplified, tlChineseTwain, {42} tlChineseTraditional, tlCroatia, tlCzech, tlDutchBelgian, {46} tlEnglishAustralian, tlEnglishCanadian, tlEnglishIreland, tlEnglishNewZealand, tlEnglishSouthAfrica, tlEnglishUk, {52} tlEstonian, tlFaeroese, tlFarsi, tlFrenchBelgian, tlFrenchLuxembourg, {57} tlFrenchSwiss, tlGermanAustrian, tlGermanLuxembourg, tlGermanLiechtenstein, tlGermanSwiss, tlGreek, tlHebrew, tlHungarian, tlIndonesian, {66} tlItalianSwiss, tlJapanese, tlKorean, tlKoreanJohab, tlLatvian, {71} tlLithuanian, tlNorewgianBokmal, tlNorwegianNynorsk, tlPolish, {75} tlPortugueseBrazil, tlRomanian, tlRussian, tlSerbianLatin, tlSlovak, tlSlovenian, tlSpanishMexican, tlSpanishModern, tlThai, tlTurkish, tlUkranian, tlAssamese, tlBengali, tlBihari, tlBodo, tlDogri, tlGujarati {92}, tlHarayanvi, tlHindi, tlKannada, tlKashmiri, tlMalayalam, tlMarathi, tlMarwari, tlMeghalayan, tlMizo, tlNaga {102}, tlOrissi, tlPunjabi, tlPushtu, tlSerbianCyrillic, tlSikkimi, tlSwidishFinland, tlTamil, tlTelugu, tlTripuri, tlUrdu, tlVietnamese); {Twain supported groups} TTwainGroups = set of (tgControl, tgImage, tgAudio); {Transfer mode for twain} TTwainTransferMode = (ttmFile, ttmNative, ttmMemory); {rect for LAYOUT; npeter 2004.01.12.} TTwainRect = record Left: double; Top: double; Right: double; Bottom: double; end; {Object to handle TW_IDENTITY} TTwainIdentity = class{$IFNDEF DONTUSEVCL}(TPersistent){$ENDIF} private {Structure which should be filled} Structure: TW_IDENTITY; {Owner} fOwner: {$IFNDEF DONTUSEVCL}TComponent{$ELSE}TObject{$ENDIF}; {Returns/sets application language property} function GetLanguage(): TTwainLanguage; procedure SetLanguage(const Value: TTwainLanguage); {Returns/sets text values} function GetString(const Index: integer): String; procedure SetString(const Index: Integer; const Value: String); {Returns/sets avaliable groups} function GetGroups(): TTwainGroups; procedure SetGroups(const Value: TTwainGroups); protected {$IFNDEF DONTUSEVCL}function GetOwner(): TPersistent; override;{$ENDIF} public {Object being created} {$IFNDEF DONTUSEVCL} constructor Create(AOwner: TComponent); {$ELSE} constructor Create(AOwner: TObject); {$ENDIF} {Copy properties from another TTwainIdentity} {$IFDEF DONTUSEVCL} procedure Assign(Source: TObject); {$ELSE} procedure Assign(Source: TPersistent); override; {$ENDIF} published {Application major version} property MajorVersion: TW_UINT16 read Structure.Version.MajorNum write Structure.Version.MajorNum; {Application minor version} property MinorVersion: TW_UINT16 read Structure.Version.MinorNum write Structure.Version.MinorNum; {Language} property Language: TTwainLanguage read GetLanguage write SetLanguage; {Country code} property CountryCode: word read Structure.Version.Country write Structure.Version.Country; {Supported groups} property Groups: TTwainGroups read GetGroups write SetGroups; {Text values} property VersionInfo: String index 0 read GetString write SetString; property Manufacturer: String index 1 read GetString write SetString; property ProductFamily: String index 2 read GetString write SetString; property ProductName: String index 3 read GetString write SetString; end; {Return set for capability retrieving/setting} TCapabilityRet = (crSuccess, crUnsupported, crBadOperation, crDependencyError, crLowMemory, crInvalidState, crInvalidContainer); {Kinds of capability retrieving} TRetrieveCap = (rcGet, rcGetCurrent, rcGetDefault, rcReset); {Capability list type} TGetCapabilityList = array of string; TSetCapabilityList = array of pointer; {Source object} TTwainSource = class(TTwainIdentity) private {Holds the item index} fIndex: Integer; {Transfer mode for the images} fTransferMode: TTwainTransferMode; {Stores if user interface should be shown} fShowUI: Boolean; {Stores if the source window is modal} fModal: Boolean; {Stores if the source is enabled} fEnabled: Boolean; {Stores if the source is loaded} fLoaded: Boolean; {Stores the owner} fOwner: TDelphiTwain; {Used with property SourceManagerLoaded to test if the source manager} {is loaded or not.} function GetSourceManagerLoaded(): Boolean; {Returns a pointer to the application} function GetAppInfo(): pTW_IDENTITY; {Sets if the source is loaded} procedure SetLoaded(const Value: Boolean); {Sets if the source is enabled} procedure SetEnabled(const Value: Boolean); {Returns a pointer to the source pTW_IDENTITY} function GetStructure: pTW_IDENTITY; {Returns a resolution} function GetResolution(Capability: TW_UINT16; var Return: Extended; var Values: TTwainResolution; Mode: TRetrieveCap): TCapabilityRet; protected {Reads a native image} procedure ReadNative(Handle: TW_UINT32; var Cancel: Boolean); {Reads the file image} procedure ReadFile(Name: TW_STR255; Format: TW_UINT16; var Cancel: Boolean); {Call event for memory image} procedure ReadMemory(Image: HBitmap; var Cancel: Boolean); protected {Prepare image memory transference} function PrepareMemXfer(var BitmapHandle: HBitmap; var PixelType: TW_INT16): TW_UINT16; {Transfer image memory} function TransferImageMemory(var ImageHandle: HBitmap; PixelType: TW_INT16): TW_UINT16; {Returns a pointer to the TW_IDENTITY for the application} property AppInfo: pTW_IDENTITY read GetAppInfo; {Method to transfer the images} procedure TransferImages(); {Message received in the event loop} function ProcessMessage(const Msg: TMsg): Boolean; {Returns if the source manager is loaded} property SourceManagerLoaded: Boolean read GetSourceManagerLoaded; {Source configuration methods} {************************} protected {Gets an item and returns it in a string} procedure GetItem(var Return: String; ItemType: TW_UINT16; Data: Pointer); {Converts from a result to a TCapabilityRec} function ResultToCapabilityRec(const Value: TW_UINT16): TCapabilityRet; {Sets a capability} function SetCapabilityRec(const Capability, ConType: TW_UINT16; Data: HGLOBAL): TCapabilityRet; public {Returns a capability strucutre} function GetCapabilityRec(const Capability: TW_UINT16; var Handle: HGLOBAL; Mode: TRetrieveCap; var Container: TW_UINT16): TCapabilityRet; {************************} {Returns an one value capability} function GetOneValue(Capability: TW_UINT16; var ItemType: TW_UINT16; var Value: string; Mode: TRetrieveCap{$IFDEF DEFAULTPARAM}=rcGet{$ENDIF}; MemHandle: HGLOBAL{$IFDEF DEFAULTPARAM}=0{$ENDIF}): TCapabilityRet; {Returns an range capability} function GetRangeValue(Capability: TW_UINT16; var ItemType: TW_UINT16; var Min, Max, Step, Default, Current: String; MemHandle: HGLOBAL{$IFDEF DEFAULTPARAM}=0{$ENDIF}): TCapabilityRet; {Returns an enumeration capability} function GetEnumerationValue(Capability: TW_UINT16; var ItemType: TW_UINT16; var List: TGetCapabilityList; var Current, Default: Integer; Mode: TRetrieveCap{$IFDEF DEFAULTPARAM}=rcGet{$ENDIF}; MemHandle: HGLOBAL{$IFDEF DEFAULTPARAM}=0{$ENDIF}): TCapabilityRet; {Returns an array capability} function GetArrayValue(Capability: TW_UINT16; var ItemType: TW_UINT16; var List: TGetCapabilityList; MemHandle: HGLOBAL {$IFDEF DEFAULTPARAM}=0{$ENDIF}): TCapabilityRet; {************************} {Sets an one value capability} function SetOneValue(Capability: TW_UINT16; ItemType: TW_UINT16; Value: Pointer): TCapabilityRet; {Sets a range capability} function SetRangeValue(Capability, ItemType: TW_UINT16; Min, Max, Step, Current: TW_UINT32): TCapabilityRet; {Sets an enumeration capability} function SetEnumerationValue(Capability, ItemType: TW_UINT16; CurrentIndex: TW_UINT32; List: TSetCapabilityList): TCapabilityRet; {Sets an array capability} function SetArrayValue(Capability, ItemType: TW_UINT16; List: TSetCapabilityList): TCapabilityRet; public {Setup file transfer} function SetupFileTransfer(Filename: String; Format: TTwainFormat): Boolean; protected {Used with property PendingXfers} function GetPendingXfers(): TW_INT16; public {Set source transfer mode} function ChangeTransferMode(NewMode: TTwainTransferMode): TCapabilityRet; {Returns return status information} function GetReturnStatus(): TW_UINT16; {Capability setting} {Set the number of images that the application wants to receive} function SetCapXferCount(Value: SmallInt): TCapabilityRet; {Returns the number of images that the source will return} function GetCapXferCount(var Return: SmallInt; Mode: TRetrieveCap{$IFDEF DEFAULTPARAM}=rcGet{$ENDIF}): TCapabilityRet; {Retrieve the unit measure for all quantities} function GetICapUnits(var Return: TTwainUnit; var Supported: TTwainUnitSet; Mode: TRetrieveCap {$IFDEF DEFAULTPARAM}=rcGet{$ENDIF}): TCapabilityRet; {Set the unit measure} function SetICapUnits(Value: TTwainUnit): TCapabilityRet; {npeter 2004.01.12 begin} function SetImagelayoutFrame(const fLeft,fTop,fRight, fBottom: double): TCapabilityRet; function SetIndicators(Value: boolean): TCapabilityRet; {npeter 2004.01.12 end} {Retrieve the pixel flavor values} function GetIPixelFlavor(var Return: TTwainPixelFlavor; var Supported: TTwainPixelFlavorSet; Mode: TRetrieveCap {$IFDEF DEFAULTPARAM}=rcGet{$ENDIF}): TCapabilityRet; {Set the pixel flavor values} function SetIPixelFlavor(Value: TTwainPixelFlavor): TCapabilityRet; {Returns bitdepth values} function GetIBitDepth(var Return: Word; var Supported: TTwainBitDepth; Mode: TRetrieveCap {$IFDEF DEFAULTPARAM}=rcGet{$ENDIF}): TCapabilityRet; {Set current bitdepth value} function SetIBitDepth(Value: Word): TCapabilityRet; {Returns pixel type values} function GetIPixelType(var Return: TTwainPixelType; var Supported: TTwainPixelTypeSet; Mode: TRetrieveCap {$IFDEF DEFAULTPARAM}=rcGet{$ENDIF}): TCapabilityRet; {Set the pixel type value} function SetIPixelType(Value: TTwainPixelType): TCapabilityRet; {Returns X and Y resolutions} function GetIXResolution(var Return: Extended; var Values: TTwainResolution; Mode: TRetrieveCap {$IFDEF DEFAULTPARAM}=rcGet{$ENDIF}): TCapabilityRet; function GetIYResolution(var Return: Extended; var Values: TTwainResolution; Mode: TRetrieveCap {$IFDEF DEFAULTPARAM}=rcGet{$ENDIF}): TCapabilityRet; {Sets X and X resolutions} function SetIXResolution(Value: Extended): TCapabilityRet; function SetIYResolution(Value: Extended): TCapabilityRet; {Returns physical width and height} function GetIPhysicalWidth(var Return: Extended; Mode: TRetrieveCap {$IFDEF DEFAULTPARAM}=rcGet{$ENDIF}): TCapabilityRet; function GetIPhysicalHeight(var Return: Extended; Mode: TRetrieveCap {$IFDEF DEFAULTPARAM}=rcGet{$ENDIF}): TCapabilityRet; {Returns if user interface is controllable} function GetUIControllable(var Return: Boolean): TCapabilityRet; {Returns feeder is loaded or not} function GetFeederLoaded(var Return: Boolean): TCapabilityRet; {Returns/sets if feeder is enabled} function GetFeederEnabled(var Return: Boolean): TCapabilityRet; function SetFeederEnabled(Value: WordBool): TCapabilityRet; {Returns/sets if auto feed is enabled} function GetAutofeed(var Return: Boolean): TCapabilityRet; function SetAutoFeed(Value: WordBool): TCapabilityRet; {Returns number of pending transfer} property PendingXfers: TW_INT16 read GetPendingXfers; public {Enables the source} function EnableSource(ShowUI, Modal: Boolean): Boolean; {Disables the source} function DisableSource: Boolean; {Loads the source} function LoadSource(): Boolean; {Unloads the source} function UnloadSource(): Boolean; {Returns a pointer to the source identity} property SourceIdentity: pTW_IDENTITY read GetStructure; {Returns/sets if the source is enabled} property Enabled: Boolean read fEnabled write SetEnabled; {Returns/sets if this source is loaded} property Loaded: Boolean read fLoaded write SetLoaded; {Object being created/destroyed} constructor Create(AOwner: TDelphiTwain); destructor Destroy; override; {Returns owner} property Owner: TDelphiTwain read fOwner; {Source window is modal} property Modal: Boolean read fModal write fModal; {Sets if user interface should be shown} property ShowUI: Boolean read fShowUI write fShowUI; {Transfer mode for transfering images from the source to} {the component and finally to the application} property TransferMode: TTwainTransferMode read fTransferMode write fTransferMode; {Returns the item index} property Index: Integer read fIndex; {Convert properties from write/read to read only} {(read description on TTwainIdentity source)} property MajorVersion: TW_UINT16 read Structure.Version.MajorNum; property MinorVersion: TW_UINT16 read Structure.Version.MinorNum; property Language: TTwainLanguage read GetLanguage; property CountryCode: word read Structure.Version.Country; property Groups: TTwainGroups read GetGroups; property VersionInfo: String index 0 read GetString; property Manufacturer: String index 1 read GetString; property ProductFamily: String index 2 read GetString; property ProductName: String index 3 read GetString; end; {Component part} TDelphiTwain = class(TTwainComponent) private {Should contain the number of Twain sources loaded} fSourcesLoaded: Integer; {Contains if the select source dialog is being displayed} SelectDialogDisplayed: Boolean; private {Event pointer holders} fOnSourceDisable: TOnSourceNotify; fOnAcquireCancel: TOnSourceNotify; fOnTwainAcquire: TOnTwainAcquire; fOnSourceSetupFileXfer: TOnSourceNotify; fOnSourceFileTransfer: TOnSourceFileTransfer; fOnAcquireError: TOnTwainError; fOnAcquireProgress: TOnAcquireProgress; private {Temp variable to allow SourceCount to be displayed in delphi} {property editor} fDummySourceCount: integer; {Contains list of source devices} DeviceList: TPointerList; {Contains a pointer to the structure with the application} {information} AppInfo: pTW_IDENTITY; {Holds the object to allow the user to set the application information} fInfo: TTwainIdentity; {Holds the handle for the virtual window which will receive} {twain message notifications} VirtualWindow: THandle; {Will hold Twain library handle} fHandle: HInst; {Holds if the component has enumerated the devices} fHasEnumerated: Boolean; {Holds twain dll procedure handle} fTwainProc: TDSMEntryProc; {Holds the transfer mode to be used} fTransferMode: TTwainTransferMode; {Contains if the library is loaded} fLibraryLoaded: Boolean; {Contains if the source manager was loaded} fSourceManagerLoaded: Boolean; {Procedure to load and unload twain library and update property} procedure SetLibraryLoaded(const Value: Boolean); {Procedure to load or unloaded the twain source manager} procedure SetSourceManagerLoaded(const Value: Boolean); {Updates the application information object} procedure SetInfo(const Value: TTwainIdentity); {Returns the number of sources} function GetSourceCount(): Integer; {Returns a source from the list} function GetSource(Index: Integer): TTwainSource; {Finds a matching source index} function FindSource(Value: pTW_IDENTITY): Integer; protected {Returns the default source} function GetDefaultSource: Integer; {Creates the virtual window} procedure CreateVirtualWindow(); {Clears the list of sources} procedure ClearDeviceList(); public {Allows Twain to display a dialog to let the user choose any source} {and returns the source index in the list} function SelectSource(): Integer; {Returns the number of loaded sources} property SourcesLoaded: Integer read fSourcesLoaded; {Enumerate the avaliable devices after Source Manager is loaded} function EnumerateDevices(): Boolean; {Object being created} {$IFNDEF DONTUSEVCL} constructor Create(AOwner: TComponent);override; {$ELSE} constructor Create; {$ENDIF} {Object being destroyed} destructor Destroy(); override; {Loads twain library and returns if it loaded sucessfully} function LoadLibrary(): Boolean; {Unloads twain and returns if it unloaded sucessfully} function UnloadLibrary(): Boolean; {Loads twain source manager} function LoadSourceManager(): Boolean; {Unloads the source manager} function UnloadSourceManager(forced: boolean): Boolean; {Returns the application TW_IDENTITY} property AppIdentity: pTW_IDENTITY read AppInfo; {Returns Twain library handle} property Handle: HInst read fHandle; {Returns a pointer to Twain only procedure} property TwainProc: TDSMEntryProc read fTwainProc; {Holds if the component has enumerated the devices} property HasEnumerated: Boolean read fHasEnumerated; {Returns a source} property Source[Index: Integer]: TTwainSource read GetSource; published {Events} {Source being disabled} property OnSourceDisable: TOnSourceNotify read fOnSourceDisable write fOnSourceDisable; {Acquire cancelled} property OnAcquireCancel: TOnSourceNotify read fOnAcquireCancel write fOnAcquireCancel; {Image acquired} property OnTwainAcquire: TOnTwainAcquire read fOnTwainAcquire write fOnTwainAcquire; {User should set information to prepare for the file transfer} property OnSourceSetupFileXfer: TOnSourceNotify read fOnSourceSetupFileXfer write fOnSourceSetupFileXfer; {File transfered} property OnSourceFileTransfer: TOnSourceFileTransfer read fOnSourceFileTransfer write fOnSourceFileTransfer; {Acquire error} property OnAcquireError: TOnTwainError read fOnAcquireError write fOnAcquireError; {Acquire progress, for memory transfers} property OnAcquireProgress: TOnAcquireProgress read fOnAcquireProgress write fOnAcquireProgress; published {Default transfer mode to be used with sources} property TransferMode: TTwainTransferMode read fTransferMode write fTransferMode; {Returns the number of sources, after Library and Source Manager} {has being loaded} property SourceCount: Integer read GetSourceCount write fDummySourceCount; {User should fill the application information} property Info: TTwainIdentity read fInfo write SetInfo; {Loads or unload Twain library} property LibraryLoaded: Boolean read fLibraryLoaded write SetLibraryLoaded; {Loads or unloads the source manager} property SourceManagerLoaded: Boolean read fSourceManagerLoaded write SetSourceManagerLoaded; end; {Puts a string inside a TW_STR255} function StrToStr255(Value: String): TW_STR255; {This method returns if Twain is installed in the current machine} function IsTwainInstalled(): Boolean; {Called by Delphi to register the component} procedure Register(); {Returns the size of a twain type} function TWTypeSize(TypeName: TW_UINT16): Integer; implementation {Units used bellow} uses Messages; {Called by Delphi to register the component} procedure Register(); begin {$IFNDEF DONTUSEVCL} RegisterComponents('NP', [TDelphiTwain]); {$ENDIF} end; {Returns the size of a twain type} function TWTypeSize(TypeName: TW_UINT16): Integer; begin {Test the type to return the size} case TypeName of TWTY_INT8 : Result := sizeof(TW_INT8); TWTY_UINT8 : Result := sizeof(TW_UINT8); TWTY_INT16 : Result := sizeof(TW_INT16); TWTY_UINT16: Result := sizeof(TW_UINT16); TWTY_INT32 : Result := sizeof(TW_INT32); TWTY_UINT32: Result := sizeof(TW_UINT32); TWTY_FIX32 : Result := sizeof(TW_FIX32); TWTY_FRAME : Result := sizeof(TW_FRAME); TWTY_STR32 : Result := sizeof(TW_STR32); TWTY_STR64 : Result := sizeof(TW_STR64); TWTY_STR128: Result := sizeof(TW_STR128); TWTY_STR255: Result := sizeof(TW_STR255); //npeter: the following types were not implemented //especially the bool caused problems TWTY_BOOL: Result := sizeof(TW_BOOL); TWTY_UNI512: Result := sizeof(TW_UNI512); TWTY_STR1024: Result := sizeof(TW_STR1024); else Result := 0; end {case} end; {Puts a string inside a TW_STR255} function StrToStr255(Value: String): TW_STR255; begin {Clean result} Fillchar(Result, sizeof(TW_STR255), #0); {If value fits inside the TW_STR255, copy memory} if Length(Value) <= sizeof(TW_STR255) then CopyMemory(@Result[0], @Value[1], Length(Value)) else CopyMemory(@Result[0], @Value[1], sizeof(TW_STR255)); end; {Returns full Twain directory (usually in Windows directory)} function GetTwainDirectory(): String; var i: TDirectoryKind; Dir: String; begin {Searches in all the directories} FOR i := LOW(TDirectoryKind) TO HIGH(TDirectoryKind) DO begin {Directory to search} Dir := GetCustomDirectory(i); {Tests if the file exists in this directory} if FileExists(Dir + TWAINLIBRARY) then begin {In case it exists, returns this directory and exit} {the for loop} Result := Dir; Break; end {if FileExists} end {FOR i} end; {This method returns if Twain is installed in the current machine} function IsTwainInstalled(): Boolean; begin {If GetTwainDirectory function returns an empty string, it means} {that Twain was not found} Result := (GetTwainDirectory() <> ''); end; { TTwainIdentity object implementation } {Object being created} {$IFNDEF DONTUSEVCL} constructor TTwainIdentity.Create(AOwner: TComponent); {$ELSE} constructor TTwainIdentity.Create(AOwner: TObject); {$ENDIF} begin {Allows ancestor to work} inherited Create; {Set initial properties} FillChar(Structure, sizeof(Structure), #0); Language := tlUserLocale; CountryCode := 1; MajorVersion := 1; VersionInfo := 'Application name'; Structure.ProtocolMajor := TWON_PROTOCOLMAJOR; Structure.ProtocolMinor := TWON_PROTOCOLMINOR; Groups := [tgImage, tgControl]; Manufacturer := 'Application manufacturer'; ProductFamily := 'App product family'; ProductName := 'App product name'; fOwner := AOwner; {Copy owner pointer} end; {$IFNDEF DONTUSEVCL} function TTwainIdentity.GetOwner(): TPersistent; begin Result := fOwner; end; {$ENDIF} {Sets a text value} procedure TTwainIdentity.SetString(const Index: Integer; const Value: String); var PropStr: PChar; begin {Select and copy pointer} case Index of 0: PropStr := @Structure.Version.Info[0]; 1: PropStr := @Structure.Manufacturer[0]; 2: PropStr := @Structure.ProductFamily[0]; else PropStr := @Structure.ProductName[0]; end {case}; {Set value} Fillchar(PropStr^, sizeof(TW_STR32), #0); if Length(Value) > sizeof(TW_STR32) then CopyMemory(PropStr, @Value[1], sizeof(TW_STR32)) else CopyMemory(PropStr, @Value[1], Length(Value)); end; {Returns a text value} function TTwainIdentity.GetString(const Index: Integer): String; begin {Test for the required property} case Index of 0: Result := Structure.Version.Info; 1: Result := Structure.Manufacturer; 2: Result := Structure.ProductFamily; else Result := Structure.ProductName; end {case} end; {Returns application language property} function TTwainIdentity.GetLanguage(): TTwainLanguage; begin Result := TTwainLanguage(Structure.Version.Language + 1); end; {Sets application language property} procedure TTwainIdentity.SetLanguage(const Value: TTwainLanguage); begin Structure.Version.Language := Word(Value) - 1; end; {Copy properties from another TTwainIdentity} {$IFDEF DONTUSEVCL} procedure TTwainIdentity.Assign(Source: TObject); {$ELSE} procedure TTwainIdentity.Assign(Source: TPersistent); {$ENDIF} begin {The source should also be a TTwainIdentity} if Source is TTwainIdentity then {Copy properties} Structure := TTwainIdentity(Source).Structure else {$IFNDEF DONTUSEVCL}inherited; {$ENDIF} end; {Returns avaliable groups} function TTwainIdentity.GetGroups(): TTwainGroups; begin {Convert from Structure.SupportedGroups to TTwainGroups} Include(Result, tgControl); if DG_IMAGE AND Structure.SupportedGroups <> 0 then Include(Result, tgImage); if DG_AUDIO AND Structure.SupportedGroups <> 0 then Include(Result, tgAudio); end; {Sets avaliable groups} procedure TTwainIdentity.SetGroups(const Value: TTwainGroups); begin {Convert from TTwainGroups to Structure.SupportedGroups} Structure.SupportedGroups := DG_CONTROL; if tgImage in Value then Structure.SupportedGroups := Structure.SupportedGroups or DG_IMAGE; if tgAudio in Value then Structure.SupportedGroups := Structure.SupportedGroups or DG_AUDIO; end; { TDelphiTwain component implementation } {Loads twain library and returns if it loaded sucessfully} function TDelphiTwain.LoadLibrary(): Boolean; var TwainDirectory: String; begin {The library must not be already loaded} if (not LibraryLoaded) then begin Result := FALSE; {Initially returns FALSE} {Searches for Twain directory} TwainDirectory := GetTwainDirectory(); {Continue only if twain is installed in an known directory} if TwainDirectory <> '' then begin fHandle := Windows.LoadLibrary(PChar(TwainDirectory + TWAINLIBRARY)); {If the library was sucessfully loaded} if (fHandle <> INVALID_HANDLE_VALUE) then begin {Obtains method handle} @fTwainProc := GetProcAddress(fHandle, MAKEINTRESOURCE(1)); {Returns TRUE/FALSE if the method was obtained} Result := (@fTwainProc <> nil); {If the method was not obtained, also free the library} if not Result then begin {Free the handle and clears the variable} Windows.FreeLibrary(fHandle); fHandle := 0; end {if not Result} end else {If it was not loaded, clears handle value} fHandle := 0; end {if TwainDirectory <> ''}; end else {If it was already loaded, returns true, since that is} {what was supposed to happen} Result := TRUE; {In case the method was sucessful, updates property} if Result then fLibraryLoaded := TRUE; end; {Unloads twain and returns if it unloaded sucessfully} function TDelphiTwain.UnloadLibrary(): Boolean; begin {The library must not be already unloaded} if (LibraryLoaded) then begin {Unloads the source manager} SourceManagerLoaded := FALSE; {Just call windows method to unload} Result := Windows.FreeLibrary(Handle); {If it was sucessfull, also clears handle value} if Result then fHandle := 0; {Updates property} fLibraryLoaded := not Result; end else {If it was already unloaded, returns true, since that is} {what was supposed to happen} Result := TRUE; {In case the method was sucessful, updates property} if Result then fLibraryLoaded := FALSE; end; {Enumerate the avaliable devices after Source Manager is loaded} function TDelphiTwain.EnumerateDevices(): Boolean; var NewSource: TTwainSource; CallRes : TW_UINT16; begin {Booth library and source manager must be loaded} if (LibraryLoaded and SourceManagerLoaded) then begin {Clears the preview list of sources} ClearDeviceList(); {Allocate new identity and tries to enumerate} NewSource := TTwainSource.Create(Self); CallRes := TwainProc(AppInfo, nil, DG_CONTROL, DAT_IDENTITY, MSG_GETFIRST, @NewSource.Structure); if CallRes = TWRC_SUCCESS then repeat {Add this item to the list} DeviceList.Add(NewSource); {Allocate memory for the next} NewSource := TTwainSource.Create(Self); NewSource.TransferMode := Self.TransferMode; NewSource.fIndex := DeviceList.Count; {Try to get the next item} until TwainProc(AppInfo, nil, DG_CONTROL, DAT_IDENTITY, MSG_GETNEXT, @NewSource.Structure) <> TWRC_SUCCESS; {Set that the component has enumerated the devices} {if everything went correctly} Result := TRUE; fHasEnumerated := Result; {Dispose un-needed source object} NewSource.Free; end else Result := FALSE; {If library and source manager aren't loaded} end; {Procedure to load and unload twain library and update property} procedure TDelphiTwain.SetLibraryLoaded(const Value: Boolean); begin {The value must be changing to activate} if (Value <> fLibraryLoaded) then begin {Depending on the parameter load/unload the library and updates} {property whenever it loaded or unloaded sucessfully} if Value then LoadLibrary() else {if not Value then} UnloadLibrary(); end {if (Value <> fLibraryLoaded)} end; {Loads twain source manager} function TDelphiTwain.LoadSourceManager(): Boolean; begin {The library must be loaded} if LibraryLoaded and not SourceManagerLoaded then {Loads source manager} Result := (fTwainProc(AppInfo, nil, DG_CONTROL, DAT_PARENT, MSG_OPENDSM, @VirtualWindow) = TWRC_SUCCESS) else {The library is not loaded, thus the source manager could} {not be loaded} Result := FALSE or SourceManagerLoaded; {In case the method was sucessful, updates property} if Result then fSourceManagerLoaded := TRUE; end; {UnLoads twain source manager} function TDelphiTwain.UnloadSourceManager(forced: boolean): Boolean; begin {The library must be loaded} if LibraryLoaded and SourceManagerLoaded then begin {Clears the list of sources} ClearDeviceList(); {Unload source manager} if not forced then Result := (TwainProc(AppInfo, nil, DG_CONTROL, DAT_PARENT, MSG_CLOSEDSM, @VirtualWindow) = TWRC_SUCCESS) else result:=true; end else {The library is not loaded, meaning that the Source Manager isn't either} Result := TRUE; {In case the method was sucessful, updates property} if Result then fSourceManagerLoaded := FALSE; end; {Procedure to load or unloaded the twain source manager} procedure TDelphiTwain.SetSourceManagerLoaded(const Value: Boolean); begin {The library must be loaded to have access to the method} if LibraryLoaded and (Value <> fSourceManagerLoaded) then begin {Load/unload the source manager} if Value then LoadSourceManager() else {if not Value then} UnloadSourceManager(false); end {if LibraryLoaded} end; {Clears the list of sources} procedure TDelphiTwain.ClearDeviceList(); var i: Integer; begin {Deallocate pTW_IDENTITY} FOR i := 0 TO DeviceList.Count - 1 DO TTwainSource(DeviceList.Item[i]).Free; {Clears the list} DeviceList.Clear; {Set trigger to tell that it has not enumerated again yet} fHasEnumerated := FALSE; end; {Finds a matching source index} function TDelphiTwain.FindSource(Value: pTW_IDENTITY): Integer; var i : Integer; begin Result := -1; {Default result} {Search for this source in the list} for i := 0 TO SourceCount - 1 DO if CompareMem(@Source[i].Structure, pChar(Value), SizeOf(TW_IDENTITY)) then begin {Return index and exit} Result := i; break; end; {if CompareMem, for i} end; {Allows Twain to display a dialog to let the user choose any source} {and returns the source index in the list} function TDelphiTwain.SelectSource: Integer; var Identity: TW_IDENTITY; begin Result := -1; {Default result} {Booth library and source manager must be loaded} if (LibraryLoaded and SourceManagerLoaded and not SelectDialogDisplayed) then begin {Don't allow this dialog to be displayed twice} SelectDialogDisplayed := TRUE; {Call twain to display the dialog} if TwainProc(AppInfo, nil, DG_CONTROL, DAT_IDENTITY, MSG_USERSELECT, @Identity) = TWRC_SUCCESS then Result := FindSource(@Identity); {Ended using} SelectDialogDisplayed := FALSE end {(LibraryLoaded and SourceManagerLoaded)} end; {Returns the number of sources} function TDelphiTwain.GetSourceCount(): Integer; begin {Library and source manager must be loaded} if (LibraryLoaded and SourceManagerLoaded) then begin {Enumerate devices, if needed} if not HasEnumerated then EnumerateDevices(); {Returns} Result := DeviceList.Count; end {In case library and source manager aren't loaded, returns 0} else Result := 0 end; {Returns the default source} function TDelphiTwain.GetDefaultSource: Integer; var Identity: TW_IDENTITY; begin {Call twain to display the dialog} if SourceManagerLoaded and (TwainProc(AppInfo, nil, DG_CONTROL, DAT_IDENTITY, MSG_GETDEFAULT, @Identity) = TWRC_SUCCESS) then Result := FindSource(@Identity) else Result := 0 {Returns} end; {Returns a source from the list} function TDelphiTwain.GetSource(Index: Integer): TTwainSource; begin {Booth library and source manager must be loaded} if (LibraryLoaded and SourceManagerLoaded) then begin {If index is in range, returns} {(Call to SourceCount property enumerates the devices, if needed)} if Index in [0..SourceCount - 1] then Result := DeviceList.Item[Index] else if (Index = -1) and (SourceCount > 0) then Result := DeviceList.Item[GetDefaultSource] {Unknown object, returns nil} else Result := nil; end {In case either the library or the source manager aren't} {loaded, it returns nil} else Result := nil end; {Object being created} constructor TDelphiTwain.Create{$IFNDEF DONTUSEVCL}(AOwner: TComponent){$ENDIF}; begin {Let the ancestor class also handle the call} inherited; {Create source list} DeviceList := TPointerList.Create; {Clear variables} fSourcesLoaded := 0; fHandle := 0; @fTwainProc := nil; SelectDialogDisplayed := FALSE; fSourceManagerLoaded := FALSE; fHasEnumerated := FALSE; fTransferMode := ttmMemory; {Creates the virtual window which will intercept messages} {from Twain} CreateVirtualWindow(); {Creates the object to allow the user to set the application} {information to inform twain source manager and sources} fInfo := TTwainIdentity.Create(Self); AppInfo := @fInfo.Structure; end; {Object being destroyed} destructor TDelphiTwain.Destroy; begin {Full unload the library} LibraryLoaded := FALSE; {Free the virtual window handle} DestroyWindow(VirtualWindow); {Free the object} fInfo.Free; {Clears and free source list} ClearDeviceList(); DeviceList.Free(); {Let ancestor class handle} inherited Destroy; end; {Creates the virtual window} procedure TDelphiTwain.CreateVirtualWindow; begin {Creates the window and passes a pointer to the class object} VirtualWindow := CreateWindow(VIRTUALWIN_CLASSNAME, 'Delphi Twain virtual ' + 'window', 0, 10, 10, 100, 100, 0, 0, hInstance, Self); end; {Updates the application information object} procedure TDelphiTwain.SetInfo(const Value: TTwainIdentity); begin {Assign one object to another} fInfo.Assign(Value); end; { TTwainSource object implementation } {Used with property SourceManagerLoaded to test if the source manager} {is loaded or not.} function TTwainSource.GetSourceManagerLoaded: Boolean; begin {Obtain information from owner TDelphiTwain} Result := Owner.SourceManagerLoaded; end; {Sets if the source is loaded} procedure TTwainSource.SetLoaded(const Value: Boolean); begin {Value should be changing} if (Value <> fLoaded) then begin {Loads or unloads the source} if Value then LoadSource() else {if not Value then} UnloadSource(); end {if (Value <> fLoaded)} end; {Sets if the source is enabled} procedure TTwainSource.SetEnabled(const Value: Boolean); begin {Source must be already enabled and value changing} if (Loaded) and (Value <> fEnabled) then begin {Enables/disables} if Value then EnableSource(ShowUI, Modal) else {if not Value then} DisableSource(); end {if (Loaded) and (Value <> fEnabled)} end; {Enables the source} function TTwainSource.EnableSource(ShowUI, Modal: Boolean): Boolean; var twUserInterface: TW_USERINTERFACE; begin {Source must be loaded and the value changing} if (Loaded) and (not Enabled) then begin {Builds UserInterface structure} twUserInterface.ShowUI := ShowUI; twUserInterface.ModalUI := Modal; twUserInterface.hParent := owner.VirtualWindow; //npeter may be it is better to send messages to VirtualWindow //I am not sure, but it seems more stable with a HP TWAIN driver //it was: := GetActiveWindow; fEnabled := TRUE; {Call method} Result := (Owner.TwainProc(AppInfo, @Structure, DG_CONTROL, DAT_USERINTERFACE, MSG_ENABLEDS, @twUserInterface) in [TWRC_SUCCESS, TWRC_CHECKSTATUS]); end else {If it's either not loaded or already enabled} {If it is not loaded} Result := FALSE or Enabled; {Updates property} if (Result = TRUE) then fEnabled := TRUE; end; {Disables the source} function TTwainSource.DisableSource(): Boolean; var twUserInterface: TW_USERINTERFACE; begin {Source must be loaded and the value changing} if (Loaded) and (Enabled) then begin {Call method} Result := (Owner.TwainProc(AppInfo, @Structure, DG_CONTROL, DAT_USERINTERFACE, MSG_DISABLEDS, @twUserInterface) = TWRC_SUCCESS); {Call notification event if being used} if (Result) and (Assigned(Owner.OnSourceDisable)) then Owner.OnSourceDisable(Owner, Index); end else {If it's either not loaded or already disabled} {If it is not loaded} Result := TRUE; {Updates property} if (Result = TRUE) then fEnabled := FALSE; end; {Loads the source} function TTwainSource.LoadSource: Boolean; begin {Only loads if it is not already loaded} if Not Loaded then begin Result := (Owner.TwainProc(AppInfo, nil, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, @Structure) = TWRC_SUCCESS); {Increase the loaded sources count variable} if Result then inc(Owner.fSourcesLoaded); end else {If it was already loaded, returns true} Result := TRUE; {In case the method was sucessful, updates property} if Result then fLoaded := TRUE; end; {Unloads the source} function TTwainSource.UnloadSource: Boolean; begin {Only unloads if it is loaded} if Loaded then begin {If the source was enabled, disable it} DisableSource(); {Call method to load} Result := (Owner.TwainProc(AppInfo, nil, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, @Structure) = TWRC_SUCCESS); {Decrease the loaded sources count variable} if Result then dec(Owner.fSourcesLoaded); end else {If it was already unloaded, returns true} Result := TRUE; {In case the method was sucessful, updates property} fLoaded := FALSE; end; {Object being destroyed} destructor TTwainSource.Destroy; begin {If loaded, unloads source} UnloadSource(); {Let ancestor class process} inherited Destroy; end; {Returns a pointer to the application} function TTwainSource.GetAppInfo: pTW_IDENTITY; begin Result := Owner.AppInfo; end; {Returns a pointer to the source identity} function TTwainSource.GetStructure: pTW_IDENTITY; begin Result := @Structure; end; {Object being created} constructor TTwainSource.Create(AOwner: TDelphiTwain); begin {Allows ancestor class to process} inherited Create(AOwner); {Initial values} fTransferMode := ttmNative; fLoaded := FALSE; fShowUI := TRUE; fEnabled := FALSE; fModal := TRUE; {Stores owner} fOwner := AOwner; end; {Set source transfer mode} function TTwainSource.ChangeTransferMode( NewMode: TTwainTransferMode): TCapabilityRet; const TransferModeToTwain: Array[TTwainTransferMode] of TW_UINT16 = (TWSX_FILE, TWSX_NATIVE, TWSX_MEMORY); var Value: TW_UINT16; begin {Set transfer mode method} Value := TransferModeToTwain[NewMode]; Result := SetOneValue(ICAP_XFERMECH, TWTY_UINT16, @Value); TransferMode := NewMode; end; {Message received in the event loop} function TTwainSource.ProcessMessage(const Msg: TMsg): Boolean; var twEvent: TW_EVENT; begin {Make twEvent structure} twEvent.TWMessage := MSG_NULL; twEvent.pEvent := TW_MEMREF(@Msg); {Call Twain procedure to handle message} Result := (Owner.TwainProc(AppInfo, @Structure, DG_CONTROL, DAT_EVENT, MSG_PROCESSEVENT, @twEvent) = TWRC_DSEVENT); {If it is a message from the source, process} if Result then case twEvent.TWMessage of {No message from the source} MSG_NULL: exit; {Requested to close the source} MSG_CLOSEDSREQ: begin {Call notification event} if (Assigned(Owner.OnAcquireCancel)) then Owner.OnAcquireCancel(Owner, Index); {Disable the source} DisableSource(); end; {Ready to transfer the images} MSG_XFERREADY: {Call method to transfer} TransferImages(); MSG_CLOSEDSOK: result:=true; MSG_DEVICEEVENT: result:=true; end {case twEvent.TWMessage} end; {Returns return status information} function TTwainSource.GetReturnStatus: TW_UINT16; var StatusInfo: TW_STATUS; begin {The source must be loaded in order to get the status} if Loaded then begin {Call method to get the information} Owner.TwainProc(AppInfo, @Structure, DG_CONTROL, DAT_STATUS, MSG_GET, @StatusInfo); Result := StatusInfo.ConditionCode; end else Result := 0 {In case it was called while the source was not loaded} end; {Converts from a result to a TCapabilityRec} function TTwainSource.ResultToCapabilityRec( const Value: TW_UINT16): TCapabilityRet; begin {Test result code to return} case Value of {Successull, copy handle and return a success value} TWRC_SUCCESS: Result := crSuccess; {Error, get more on the error, and return result} {case} else case GetReturnStatus() of TWCC_CAPUNSUPPORTED: Result := crUnsupported; TWCC_CAPBADOPERATION: Result := crBadOperation; TWCC_CAPSEQERROR: Result := crDependencyError; TWCC_LOWMEMORY: Result := crLowMemory; TWCC_SEQERROR: Result := crInvalidState; else Result := crBadOperation; end {case GetReturnStatus of} end {case}; end; {Sets a capability} function TTwainSource.SetCapabilityRec(const Capability, ConType: TW_UINT16; Data: HGlobal): TCapabilityRet; var CapabilityInfo: TW_CAPABILITY; begin {Source must be loaded to set} if Loaded then begin {Fill structure} CapabilityInfo.Cap := Capability; CapabilityInfo.ConType := ConType; CapabilityInfo.hContainer := Data; {Call method and store return} Result := ResultToCapabilityRec(Owner.TwainProc(AppInfo, @Structure, DG_CONTROL, DAT_CAPABILITY, MSG_SET, @CapabilityInfo)); end else Result := crInvalidState {In case the source is not loaded} end; {Returns a capability strucutre} function TTwainSource.GetCapabilityRec( const Capability: TW_UINT16; var Handle: HGLOBAL; Mode: TRetrieveCap; var Container: TW_UINT16): TCapabilityRet; const ModeToTwain: Array[TRetrieveCap] of TW_UINT16 = (MSG_GET, MSG_GETCURRENT, MSG_GETDEFAULT, MSG_RESET); var CapabilityInfo: TW_CAPABILITY; begin {Source must be loaded} if Loaded then begin {Fill structure} CapabilityInfo.Cap := Capability; CapabilityInfo.ConType := TWON_DONTCARE16; CapabilityInfo.hContainer := 0; {Call method and store return} Result := ResultToCapabilityRec(Owner.TwainProc(AppInfo, @Structure, DG_CONTROL, DAT_CAPABILITY, ModeToTwain[Mode], @CapabilityInfo)); if Result = crSuccess then begin Handle := CapabilityInfo.hContainer; Container := CapabilityInfo.ConType; end end {if not Loaded} else Result := crInvalidState {In case the source is not loaded} end; {Gets an item and returns it in a string} procedure TTwainSource.GetItem(var Return: String; ItemType: TW_UINT16; Data: Pointer); begin {Test the item type} case ItemType of TWTY_INT8 : Return := IntToStr(pTW_INT8(Data)^); TWTY_UINT8 : Return := IntToStr(pTW_UINT8(Data)^); TWTY_INT16, 44 {TWTY_HANDLE} : Return := IntToStr(pTW_INT16(Data)^); TWTY_UINT16, TWTY_BOOL : Return := IntToStr(pTW_UINT16(Data)^); TWTY_INT32 : Return := IntToStr(pTW_INT32(Data)^); TWTY_UINT32, 43 {TWTY_MEMREF} : Return := IntToStr(pTW_UINT32(Data)^); {Floating integer type} TWTY_FIX32: with pTW_FIX32(Data)^ do //npeter bugfix: //it is better to use the actual decimal separator //and not a wired in value! //If not, you may get error on strtofloat //original: Return := IntToStr(Whole) + ',' + IntToStr(Frac); Return := IntToStr(Whole) + decimalseparator + IntToStr(Frac); {String types, which are all ended by a null char (#0)} TWTY_STR32, TWTY_STR64, TWTY_STR128, TWTY_STR255 : Return := PChar(Data); end {case ItemType} end; {Returns an array capability} function TTwainSource.GetArrayValue(Capability: TW_UINT16; var ItemType: TW_UINT16; var List: TGetCapabilityList; MemHandle: HGLOBAL): TCapabilityRet; var ArrayV : pTW_ARRAY; ItemSize : Integer; Data : PChar; CurItem : Integer; Value : String; Container: TW_UINT16; begin {Call method to get the memory to the return} if MemHandle = 0 then Result := GetCapabilityRec(Capability, MemHandle, rcGet, Container) else begin Result := crSuccess; Container := TWON_ARRAY; end; if (Result = crSuccess) and (Container <> TWON_ARRAY) then begin Result := crInvalidContainer; GlobalFree(MemHandle); Exit; end; {If result was sucessfull and memory was allocated} if (Result = crSuccess) then begin {Obtain structure pointer} ArrayV := GlobalLock(MemHandle); {Fill return properties} ItemType := ArrayV^.ItemType; {Prepare to list items} ItemSize := TWTypeSize(ItemType); Data := @ArrayV^.ItemList[0]; SetLength(List, ArrayV^.NumItems); {Copy items} for CurItem := 0 TO ArrayV^.NumItems - 1 do begin {Obtain this item} GetItem(Value, ItemType, Data); List[CurItem] := Value; {Move memory to the next} inc(Data, ItemSize); end; {Unlock memory and unallocate} GlobalUnlock(MemHandle); GlobalFree(MemHandle); end {if (Result = crSuccess)} end; {Returns an enumeration capability} function TTwainSource.GetEnumerationValue(Capability: TW_UINT16; var ItemType: TW_UINT16; var List: TGetCapabilityList; var Current, Default: Integer; Mode: TRetrieveCap; MemHandle: HGLOBAL): TCapabilityRet; var EnumV : pTW_ENUMERATION; ItemSize : Integer; Data : PChar; CurItem : Integer; Value : String; Container: TW_UINT16; begin {Call method to get the memory to the return} if MemHandle = 0 then Result := GetCapabilityRec(Capability, MemHandle, Mode, Container) else begin Result := crSuccess; Container := TWON_ENUMERATION; end; if (Result = crSuccess) and (Container <> TWON_ENUMERATION) then begin Result := crInvalidContainer; GlobalFree(MemHandle); Exit; end; {If result was sucessfull and memory was allocated} if (Result = crSuccess) then begin {Obtain structure pointer} EnumV := GlobalLock(MemHandle); {Fill return properties} Current := EnumV^.CurrentIndex; Default := EnumV^.DefaultIndex; ItemType := EnumV^.ItemType; {Prepare to list items} ItemSize := TWTypeSize(ItemType); Data := @EnumV^.ItemList[0]; SetLength(List, EnumV^.NumItems); {Copy items} for CurItem := 0 TO EnumV^.NumItems - 1 do begin {Obtain this item} GetItem(Value, ItemType, Data); List[CurItem] := Value; {Move memory to the next} inc(Data, ItemSize); end; {Unlock memory and unallocate} GlobalUnlock(MemHandle); GlobalFree(MemHandle); end {if (Result = crSuccess)} end; {Returns a range capability} function TTwainSource.GetRangeValue(Capability: TW_UINT16; var ItemType: TW_UINT16; var Min, Max, Step, Default, Current: String; MemHandle: HGLOBAL): TCapabilityRet; var RangeV : pTW_RANGE; Container: TW_UINT16; begin {Call method to get the memory to the return} if MemHandle = 0 then Result := GetCapabilityRec(Capability, MemHandle, rcGet, Container) else begin Result := crSuccess; Container := TWON_RANGE; end; if (Result = crSuccess) and (Container <> TWON_RANGE) then begin Result := crInvalidContainer; GlobalFree(MemHandle); Exit; end; {If result was sucessfull and memory was allocated} if (Result = crSuccess) then begin {Obtain structure pointer} RangeV := GlobalLock(MemHandle); {Fill return} ItemType := RangeV^.ItemType; GetItem(Min, ItemType, @RangeV^.MinValue); GetItem(Max, ItemType, @RangeV^.MaxValue); GetItem(Step, ItemType, @RangeV^.StepSize); GetItem(Default, ItemType, @RangeV^.DefaultValue); GetItem(Current, ItemType, @RangeV^.CurrentValue); {Unlock memory and unallocate} GlobalUnlock(MemHandle); GlobalFree(MemHandle); end {if (Result = crSuccess)} end; {Returns an one value capability} function TTwainSource.GetOneValue(Capability: TW_UINT16; var ItemType: TW_UINT16; var Value: String; Mode: TRetrieveCap; MemHandle: HGLOBAL): TCapabilityRet; var OneV : pTW_ONEVALUE; Container: TW_UINT16; begin {Call method to get the memory to the return} if MemHandle = 0 then Result := GetCapabilityRec(Capability, MemHandle, Mode, Container) else begin Result := crSuccess; Container := TWON_ONEVALUE; end; if (Result = crSuccess) and (Container <> TWON_ONEVALUE) then begin Result := crInvalidContainer; GlobalFree(MemHandle); Exit; end; {If result was sucessfull and memory was allocated} if (Result = crSuccess) then begin {Obtain structure pointer} OneV := GlobalLock(MemHandle); {Fill return} ItemType := OneV^.ItemType; GetItem(Value, OneV^.ItemType, @OneV^.Item); {Unlock memory and unallocate} GlobalUnlock(MemHandle); GlobalFree(MemHandle); end {if (Result = crSuccess)} end; {Sets an one value capability} function TTwainSource.SetOneValue(Capability: TW_UINT16; ItemType: TW_UINT16; Value: Pointer): TCapabilityRet; var Data: HGLOBAL; OneV: pTW_ONEVALUE; ItemSize,ItemSize2: Integer; begin {Allocate enough memory for the TW_ONEVALUE and obtain pointer} ItemSize := TWTypeSize(ItemType); //npeter: TW_ONEVALUE minimal size !!! //I think to meet the specifications the //Item's size must be at least sizeof(TW_UINT32)! //when I did it, some mistic errors on some drivers went gone if ItemSize 0 do begin {In case the total bytes received now have more than we} {need to complete the line} if Xfer.BytesWritten + DataSize > LineLength then begin Readed := LineLength - DataSize; CopyMemory(Ptr, Xfer.Memory.TheMem, LineLength - DataSize); end else {Otherwise, continue completing the line} begin Readed := Xfer.BytesWritten; CopyMemory(Ptr, Xfer.Memory.TheMem, Readed); end; {Adjust} inc(DataSize, Readed); inc(Ptr, Readed); dec(Xfer.BytesWritten, Readed); Cardinal(Xfer.Memory.TheMem) := Cardinal(Xfer.Memory.TheMem) + Readed; {Reached end of line} if DataSize >= LineLength then begin {Fix RGB to BGR} if PixelType = TWPT_RGB then begin ItemPtr := LinePtr; FOR Index := 1 TO ImageInfo.bmWidth DO begin Temp := ItemPtr^.rgbtRed; ItemPtr^.rgbtRed := ItemPtr^.rgbtBlue; ItemPtr^.rgbtBlue := Temp; inc(ItemPtr); end {FOR Index}; end {if PixelType = TWPT_RGB}; {Adjust pointers} Cardinal(LinePtr) := Cardinal(LinePtr) - LineLength; Ptr := LinePtr; dec(CurLine); DataSize := 0; {Call event} if Assigned(Owner.OnAcquireProgress) then Owner.OnAcquireProgress(Self, Self.Index, ImageHandle, Cardinal(ImageInfo.bmHeight) - CurLine - 1, ImageInfo.bmHeight - 1); end {if DataSize >= LineLength} end {while Xfer.BytesWritten > 0}; {Set again pointer to write to} Xfer.Memory.TheMem := AllocPtr; end {TWRC_SUCCESS}; until Result <> TWRC_SUCCESS; {Free allocated memory} FreeMem(AllocPtr, Setup.Preferred); {Some error ocurred, free memory and returns} if Result <> TWRC_XFERDONE then DeleteObject(ImageHandle); end; {Prepare image memory transference} function TTwainSource.PrepareMemXfer(var BitmapHandle: HBitmap; var PixelType: TW_INT16): TW_UINT16; const PixelColor: Array[TTwainPixelFlavor] of Array[0..1] of Byte = ((0, $FF), ($FF, 00), (0, $FF)); var Handle: HGlobal; Info: TW_IMAGEINFO; Setup: TW_SETUPMEMXFER; structsize, index, Size, Blocks: Integer; XRes, YRes: Extended; Pal : TW_PALETTE8; vUnit : TTwainUnit; vUnits: TTwainUnitSet; Dib : pBitmapInfo; PixelFlavor: TTwainPixelFlavor; PixelFlavors: TTwainPixelFlavorSet; DC: HDC; Data : Pointer; begin {First of all, get information on the image being acquired} Result := Owner.TwainProc(AppInfo, @Structure, DG_IMAGE, DAT_IMAGEINFO, MSG_GET, @Info); if Result <> TWRC_SUCCESS then exit; {Calculate image size} with Info do size := ((((ImageWidth * BitsPerPixel + 31) div 32)*4) * info.ImageLength); {Obtain image buffer transference sizes} Owner.TwainProc(AppInfo, @Structure, DG_CONTROL, DAT_SETUPMEMXFER, MSG_GET, @Setup); blocks := (size div Integer(setup.Preferred)); size := (blocks + 1) * Integer(setup.Preferred); {Prepare new bitmap} structsize := size + sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD); Handle := GlobalAlloc(GHND, StructSize); Dib := GlobalLock(Handle); Fillchar(Dib^, structsize, #0); {Fill image information} Dib^.bmiHeader.biSize := sizeof(BITMAPINFOHEADER); Dib^.bmiHeader.biWidth := info.ImageWidth; Dib^.bmiHeader.biHeight := info.ImageLength; {Only 1 plane supported} Dib^.bmiHeader.biPlanes := 1; Dib^.bmiHeader.biBitCount := info.BitsPerPixel; {No compression} Dib^.bmiHeader.biCompression := BI_RGB; Dib^.bmiHeader.biSizeImage := Size; {Adjust units} XRes := Fix32ToFloat(Info.XResolution); YRes := Fix32ToFloat(Info.YResolution); GetICapUnits(vUnit, vUnits); case vUnit of tuInches: begin Dib^.bmiHeader.biXPelsPerMeter := Trunc((XRes*2.54)*100); Dib^.bmiHeader.biYPelsPerMeter := Trunc((YRes*2.54)*100); end; tuCentimeters: begin Dib^.bmiHeader.biXPelsPerMeter := Trunc(XRes*100); Dib^.bmiHeader.biYPelsPerMeter := Trunc(YRes*100); end else begin Dib^.bmiHeader.biXPelsPerMeter := 0; Dib^.bmiHeader.biYPelsPerMeter := 0; end end {case vUnits of}; {Now it should setup the palette to be used by the image} {by either building a definied palette or retrieving the} {image's one} case (Info.PixelType) of TWPT_BW: begin {Only two colors are used} Dib^.bmiHeader.biClrUsed := 2; Dib^.bmiHeader.biClrImportant := 0; {Try obtaining the pixel flavor} if GetIPixelFlavor(PixelFlavor, PixelFlavors) <> crSuccess then PixelFlavor := tpfChocolate; {Set palette colors} for Index := 0 to 1 do begin Dib^.bmiColors[Index].rgbRed := PixelColor[PixelFlavor][Index]; Dib^.bmiColors[Index].rgbGreen := PixelColor[PixelFlavor][Index]; Dib^.bmiColors[Index].rgbBlue := PixelColor[PixelFlavor][Index]; Dib^.bmiColors[Index].rgbReserved := 0; end; end; TWPT_GRAY: begin {Creates a 256 shades of gray palette} Dib^.bmiHeader.biClrUsed := 256; for index := 0 to 255 do begin Dib^.bmiColors[index].rgbRed := index; Dib^.bmiColors[index].rgbGreen := index; Dib^.bmiColors[index].rgbBlue := index; Dib^.bmiColors[index].rgbReserved := 0; end {for i} end; TWPT_RGB: Dib^.bmiHeader.biClrUsed := 0; else begin {Try obtaining the palette} if Owner.TwainProc(AppInfo, @Structure, DG_CONTROL, DAT_PALETTE8, MSG_GET, @Pal) <> TWRC_SUCCESS then begin {If the source did not provide a palette, uses shades of gray here} Dib^.bmiHeader.biClrUsed := 256; for index := 0 to 255 do begin Dib^.bmiColors[index].rgbRed := index; Dib^.bmiColors[index].rgbGreen := index; Dib^.bmiColors[index].rgbBlue := index; Dib^.bmiColors[index].rgbReserved := 0; end {for i} end else begin {Uses source palette here} Dib^.bmiHeader.biClrUsed := Pal.NumColors; for Index := 0 TO Pal.NumColors - 1 do begin Dib^.bmiColors[index].rgbRed := pal.Colors[index].Channel1; Dib^.bmiColors[index].rgbGreen := pal.Colors[index].Channel2; Dib^.bmiColors[index].rgbBlue := pal.Colors[index].Channel3; Dib^.bmiColors[index].rgbReserved := 0; end {for Index} end {if Owner.TwainProc(AppInfo...} end {case else}; end {case Info.PixelType}; {Creates the bitmap} DC := GetDC(Owner.VirtualWindow); Cardinal(Data) := Cardinal(Dib) + Dib^.bmiHeader.biSize + (Dib^.bmiHeader.biClrUsed * sizeof(RGBQUAD)); BitmapHandle := CreateDIBSection(DC, Dib^, DIB_RGB_COLORS, Data, 0, 0); ReleaseDC(Owner.VirtualWindow, DC); PixelType := Info.PixelType; {Unlock and free data} GlobalUnlock(Handle); GlobalFree(Handle); end; {Method to transfer the images} procedure TTwainSource.TransferImages(); var {To test if the image transfer is done} Cancel, Done : Boolean; {Return code from Twain method} rc : TW_UINT16; {Handle to the native Device independent Image (DIB)} hNative: TW_UINT32; {Pending transfers structure} PendingXfers: TW_PENDINGXFERS; {File transfer info} Info: TW_SETUPFILEXFER; {Image handle and pointer} ImageHandle: HBitmap; PixelType : TW_INT16; begin {Set the transfer mode} //npeter: //on a HP driver I got error events //when it was set above state 5; //commented out // ChangeTransferMode(TransferMode); Cancel := FALSE; {Testing if it was cancelled} Done := FALSE; {Initialize done variable} {Obtain all the images from the source} repeat {Transfer depending on the transfer mode} case TransferMode of {Native transfer, the source creates the image thru a device} {dependent image} ttmNative: begin {Call method to obtain the image} hNative := 0; rc := Owner.TwainProc(AppInfo, @Structure, DG_IMAGE, DAT_IMAGENATIVEXFER, MSG_GET, @hNative); end {case ttmNative}; {File transfering, the source should create a file with} {the acquired image} ttmFile: begin {Event to allow user to set the file transfer information} if Assigned(Owner.OnSourceSetupFileXfer) then Owner.OnSourceSetupFileXfer(Owner, Index); Owner.TwainProc(AppInfo, @Structure, DG_CONTROL, DAT_SETUPFILEXFER, MSG_GET, @Info); {Call method to make source acquire and create file} rc := Owner.TwainProc(AppInfo, @Structure, DG_IMAGE, DAT_IMAGEFILEXFER, MSG_GET, nil); end {case ttmFile}; {Memory buffer transfers} ttmMemory: begin {Prepare for memory transference} rc := PrepareMemXfer(ImageHandle, PixelType); {If the image was sucessfully prepared to be transfered, it's} {now time to transfer it} if rc = TWRC_SUCCESS then rc := TransferImageMemory(ImageHandle, PixelType); end {Unknown transfer mode ?} else Rc := 0; end; {Twain call to transfer image return} case rc of {Transfer sucessfully done} TWRC_XFERDONE: case TransferMode of {Native transfer sucessfull} ttmNative: ReadNative(hNative, Cancel); {File transfer sucessfull} ttmFile: ReadFile(Info.FileName, Info.Format, Cancel); {Memory transfer sucessfull} ttmMemory: ReadMemory(ImageHandle, Cancel); end {case TransferMode, TWRC_XFERDONE}; {User cancelled the transfers} TWRC_CANCEL: begin {Acknowledge end of transfer} Done := TRUE; {Call event, if avaliable} if Assigned(Owner.OnAcquireCancel) then Owner.OnAcquireCancel(Owner, Index) end else {Unknown return or error} if Assigned(Owner.OnAcquireError) then Owner.OnAcquireError(Owner, Index, Rc, GetReturnStatus()) end; {Check if there are pending transfers} if not Done then Done := (Owner.TwainProc(AppInfo, @Structure, DG_CONTROL, DAT_PENDINGXFERS, MSG_ENDXFER, @PendingXfers) <> TWRC_SUCCESS) or (PendingXfers.Count = 0); {If user has cancelled} if not Done and Cancel then Done := (Owner.TwainProc(AppInfo, @Structure, DG_CONTROL, DAT_PENDINGXFERS, MSG_RESET, @PendingXfers) = TWRC_SUCCESS); until Done; {Disable source} Enabled := False; end; {Returns the number of colors in the DIB} function DibNumColors (pv: Pointer): Word; var Bits: Integer; lpbi: PBITMAPINFOHEADER absolute pv; lpbc: PBITMAPCOREHEADER absolute pv; begin //With the BITMAPINFO format headers, the size of the palette //is in biClrUsed, whereas in the BITMAPCORE - style headers, it //is dependent on the bits per pixel ( = 2 raised to the power of //bits/pixel). if (lpbi^.biSize <> sizeof(BITMAPCOREHEADER)) then begin if (lpbi^.biClrUsed <> 0) then begin result := lpbi^.biClrUsed; exit; end; Bits := lpbi^.biBitCount; end else Bits := lpbc^.bcBitCount; {Test bits to return} case (Bits) of 1: Result := 2; 4: Result := 16; 8: Result := 256; else Result := 0; end {case}; end; {Converts from TWain TW_UINT16 to TTwainFormat} function TwainToTTwainFormat(Value: TW_UINT16): TTwainFormat; begin Case Value of TWFF_TIFF : Result := tfTIFF; TWFF_PICT : Result := tfPict; TWFF_BMP : Result := tfBMP; TWFF_XBM : Result := tfXBM; TWFF_JFIF : Result := tfJPEG; TWFF_FPX : Result := tfFPX; TWFF_TIFFMULTI: Result := tfTIFFMulti; TWFF_PNG : Result := tfPNG; TWFF_SPIFF : Result := tfSPIFF; TWFF_EXIF : Result := tfEXIF; else Result := tfUnknown; end {case Value of} end; {Reads the file image} procedure TTwainSource.ReadFile(Name: TW_STR255; Format: TW_UINT16; var Cancel: Boolean); begin {Call event, if set} if Assigned(Owner.OnSourceFileTransfer) then Owner.OnSourceFileTransfer(Self, Index, Name, TwainToTTwainFormat(Format), Cancel); end; {Call event for memory image} procedure TTwainSource.ReadMemory(Image: HBitmap; var Cancel: Boolean); {$IFNDEF DONTUSEVCL} var BitmapObj: TBitmap;{$ENDIF} begin if Assigned(Owner.OnTwainAcquire) then {$IFDEF DONTUSEVCL} Owner.OnTwainAcquire(Owner, Index, Image, Cancel); {$ELSE} begin BitmapObj := TBitmap.Create; BitmapObj.Handle := Image; Owner.OnTwainAcquire(Owner, Index, BitmapObj, Cancel); BitmapObj.Free; end; {$ENDIF} end; {Reads a native image} procedure TTwainSource.ReadNative(Handle: TW_UINT32; var Cancel: Boolean); var DibInfo: ^TBITMAPINFO; ColorTableSize: Integer; lpBits: PChar; DC: HDC; BitmapHandle: HBitmap; {$IFNDEF DONTUSEVCL}BitmapObj: TBitmap;{$ENDIF} begin {Get image information pointer and size} DibInfo := GlobalLock(Handle); ColorTableSize := (DibNumColors(DibInfo) * SizeOf(RGBQUAD)); {Get data memory position} lpBits := PChar(DibInfo); Inc(lpBits, DibInfo.bmiHeader.biSize); Inc(lpBits, ColorTableSize); {Creates the bitmap} DC := GetDC(Owner.VirtualWindow); BitmapHandle := CreateDIBitmap(DC, DibInfo.bmiHeader, CBM_INIT, lpBits, DibInfo^, DIB_RGB_COLORS); ReleaseDC(Owner.VirtualWindow, DC); if Assigned(Owner.OnTwainAcquire) then {$IFDEF DONTUSEVCL} Owner.OnTwainAcquire(Owner, Index, BitmapHandle, Cancel); {$ELSE} begin BitmapObj := TBitmap.Create; BitmapObj.Handle := BitmapHandle; Owner.OnTwainAcquire(Owner, Index, BitmapObj, Cancel); BitmapObj.Free; end; {$ENDIF} {Free bitmap} GlobalUnlock(Handle); GlobalFree(Handle); end; {Setup file transfer} function TTwainSource.SetupFileTransfer(Filename: String; Format: TTwainFormat): Boolean; const FormatToTwain: Array[TTwainFormat] of TW_UINT16 = (TWFF_TIFF, TWFF_PICT, TWFF_BMP, TWFF_XBM, TWFF_JFIF, TWFF_FPX, TWFF_TIFFMULTI, TWFF_PNG, TWFF_SPIFF, TWFF_EXIF, 0); var FileTransferInfo: TW_SETUPFILEXFER; begin {Source must be loaded to set things} if (Loaded) then begin {Prepare structure} FileTransferInfo.FileName := StrToStr255(FileName); FileTransferInfo.Format := FormatToTwain[Format]; {Call method} Result := (Owner.TwainProc(AppInfo, @Structure, DG_CONTROL, DAT_SETUPFILEXFER, MSG_SET, @FileTransferInfo) = TWRC_SUCCESS); end else Result := FALSE; {Could not set file transfer with source unloaded} end; {Set the number of images that the application wants to receive} function TTwainSource.SetCapXferCount(Value: SmallInt): TCapabilityRet; begin {Call method to set the value} Result := SetOneValue(CAP_XFERCOUNT, TWTY_UINT16, @Value); end; {Returns the number of images that the source will return} function TTwainSource.GetCapXferCount(var Return: SmallInt; Mode: TRetrieveCap): TCapabilityRet; var {Will hold the capability information} ItemType: TW_UINT16; Value : String; begin {Call method to return information} Result := GetOneValue(CAP_XFERCOUNT, ItemType, Value, Mode); {Item type must be of TW_UINT16} if (Result = crSuccess) and (ItemType <> TWTY_INT16) then Result := crUnsupported; {If everything gone ok, fill result} if Result = crSuccess then Return := StrToIntDef(Value, -1); end; {Set the unit measure} function TTwainSource.SetICapUnits(Value: TTwainUnit): TCapabilityRet; //npeter //the TTwainUnit is byte!!! //so we have to convert it to TW_UINT16 //before this fix I was not able to set this capability //on a HP driver const Transfer: Array[TTwainUnit] of TW_UINT16 = (TWUN_INCHES, TWUN_CENTIMETERS, TWUN_PICAS, TWUN_POINTS, TWUN_TWIPS, TWUN_PIXELS, TWUN_INCHES); var iValue: TW_UINT16; begin ivalue:=Transfer[Value]; Result := SetOneValue(ICAP_UNITS, TWTY_UINT16, @iValue); end; {Convert from Twain to TTwainPixelFlavor} function TwainToTTwainPixelFlavor(Value: TW_UINT16): TTwainPixelFlavor; begin {Test the value to make the convertion} case Value of TWPF_CHOCOLATE: Result := tpfChocolate; TWPF_VANILLA : Result := tpfVanilla; else Result := tpfUnknown; end {case Value} end; {Convert from Twain to TTwainUnit} function TwainToTTwainUnit(Value: TW_UINT16): TTwainUnit; begin {Test the value to make the convertion} case Value of TWUN_INCHES : Result := tuInches; TWUN_CENTIMETERS: Result := tuCentimeters; TWUN_PICAS : Result := tuPicas; TWUN_POINTS : Result := tuPoints; TWUN_TWIPS : Result := tuTwips; TWUN_PIXELS : Result := tuPixels; else Result := tuUnknown; end {case Value} end; {Retrieve the unit measure for all quantities} function TTwainSource.GetICapUnits(var Return: TTwainUnit; var Supported: TTwainUnitSet; Mode: TRetrieveCap): TCapabilityRet; var ItemType: TW_UINT16; List : TGetCapabilityList; Current, i, Default : Integer; begin {Call method to get result} Result := GetEnumerationValue(ICAP_UNITS, ItemType, List, Current, Default, Mode); if ItemType <> TWTY_UINT16 then Result := crUnsupported; {If it was sucessfull, return values} if Result = crSuccess then begin {Make list} for i := Low(List) to High(List) do Include(Supported, TwainToTTwainUnit(StrToIntDef(List[i], -1))); {Return values depending on the mode} if Mode = rcGetDefault then Return := TwainToTTwainUnit(StrToIntDef(List[Default], -1)) else Return := TwainToTTwainUnit(StrToIntDef(List[Current], -1)); end {if Result = crSuccess} end; {Retrieve the pixel flavor values} function TTwainSource.GetIPixelFlavor(var Return: TTwainPixelFlavor; var Supported: TTwainPixelFlavorSet; Mode: TRetrieveCap): TCapabilityRet; var ItemType: TW_UINT16; List : TGetCapabilityList; Current, i, Default : Integer; begin {Call method to get result} Result := GetEnumerationValue(ICAP_PIXELFLAVOR, ItemType, List, Current, Default, Mode); if ItemType <> TWTY_UINT16 then Result := crUnsupported; {If it was sucessfull, return values} if Result = crSuccess then begin {Make list} for i := Low(List) to High(List) do Include(Supported, TwainToTTwainPixelFlavor(StrToIntDef(List[i], -1))); {Return values depending on the mode} if Mode = rcGetDefault then Return := TwainToTTwainPixelFlavor(StrToIntDef(List[Default], -1)) else Return := TwainToTTwainPixelFlavor(StrToIntDef(List[Current], -1)); end {if Result = crSuccess} end; function TTwainSource.SetIPixelFlavor(Value: TTwainPixelFlavor): TCapabilityRet; //npeter //the TTwainPixelFlavor is byte!!! //so we have to convert it to TW_UINT16 //before this fix I was not able to set this capability //on a HP driver const Transfer: array [TTwainPixelFlavor] of TW_UINT16 = (TWPF_CHOCOLATE,TWPF_VANILLA,TWPF_CHOCOLATE); var iValue: TW_UINT16; begin iValue:=Transfer[value]; Result := SetOneValue(ICAP_PIXELFLAVOR, TWTY_UINT16, @iValue); end; {Convert from Twain to TTwainPixelType} function TwainToTTwainPixelType(Value: TW_UINT16): TTwainPixelType; begin {Test the value to make the convertion} case Value of TWPT_BW : Result := tbdBw; TWPT_GRAY : Result := tbdGray; TWPT_RGB : Result := tbdRgb; TWPT_PALETTE : Result := tbdPalette; TWPT_CMY : Result := tbdCmy; TWPT_CMYK : Result := tbdCmyk; TWPT_YUV : Result := tbdYuv; TWPT_YUVK : Result := tbdYuvk; TWPT_CIEXYZ : Result := tbdCieXYZ; else Result := tbdUnknown; end {case Value} end; {Returns pixel type values} function TTwainSource.GetIPixelType(var Return: TTwainPixelType; var Supported: TTwainPixelTypeSet; Mode: TRetrieveCap): TCapabilityRet; var ItemType: TW_UINT16; List : TGetCapabilityList; Current, i, Default : Integer; begin {Call method to get result} Result := GetEnumerationValue(ICAP_PIXELTYPE, ItemType, List, Current, Default, Mode); if ItemType <> TWTY_UINT16 then Result := crUnsupported; {If it was sucessfull, return values} if Result = crSuccess then begin {Make list} for i := Low(List) to High(List) do Include(Supported, TwainToTTwainPixelType(StrToIntDef(List[i], -1))); {Return values depending on the mode} if Mode = rcGetDefault then Return := TwainToTTwainPixelType(StrToIntDef(List[Default], -1)) else Return := TwainToTTwainPixelType(StrToIntDef(List[Current], -1)); end {if Result = crSuccess} end; {Set the pixel type value} function TTwainSource.SetIPixelType(Value: TTwainPixelType): TCapabilityRet; //npeter //the TTwainPixelType is byte!!! //so we have to convert it to TW_UINT16 //before this fix occasionally I was not able to set this capability //on a HP driver var ivalue: smallint; begin ivalue:=ord(value); Result := SetOneValue(ICAP_PIXELTYPE, TWTY_UINT16, @iValue); end; {Returns bitdepth values} function TTwainSource.GetIBitDepth(var Return: Word; var Supported: TTwainBitDepth; Mode: TRetrieveCap): TCapabilityRet; var ItemType: TW_UINT16; List : TGetCapabilityList; Current, i, Default : Integer; begin {Call GetOneValue to obtain this property} Result := GetEnumerationValue(ICAP_BITDEPTH, ItemType, List, Current, Default, Mode); if ItemType <> TWTY_UINT16 then Result := crUnsupported; {In case everything went ok, fill parameters} if Result = crSuccess then begin {Build bit depth list} SetLength(Supported, Length(List)); FOR i := LOW(List) TO HIGH(List) DO Supported[i] := StrToIntDef(List[i], -1); {Return values depending on the mode} if Mode = rcGetDefault then Return := StrToIntDef(List[Default], -1) else Return := StrToIntDef(List[Current], -1); end {if Result = crSuccess} end; {Set current bitdepth value} function TTwainSource.SetIBitDepth(Value: Word): TCapabilityRet; begin Result := SetOneValue(ICAP_BITDEPTH, TWTY_UINT16, @Value); end; {Returns physical width} function TTwainSource.GetIPhysicalWidth(var Return: Extended; Mode: TRetrieveCap): TCapabilityRet; var Handle: HGlobal; OneV : pTW_ONEVALUE; Container: TW_UINT16; begin {Obtain handle to data from this capability} Result := GetCapabilityRec(ICAP_PHYSICALWIDTH, Handle, Mode, Container); if Result = crSuccess then begin {Obtain data} OneV := GlobalLock(Handle); if OneV^.ItemType <> TWTY_FIX32 then Result := crUnsupported else Return := Fix32ToFloat(pTW_FIX32(@OneV^.Item)^); {Free data} GlobalUnlock(Handle); GlobalFree(Handle); end; end; {Returns physical height} function TTwainSource.GetIPhysicalHeight(var Return: Extended; Mode: TRetrieveCap): TCapabilityRet; var Handle: HGlobal; OneV : pTW_ONEVALUE; Container: TW_UINT16; begin {Obtain handle to data from this capability} Result := GetCapabilityRec(ICAP_PHYSICALHEIGHT, Handle, Mode, Container); if Result = crSuccess then begin {Obtain data} OneV := GlobalLock(Handle); if OneV^.ItemType <> TWTY_FIX32 then Result := crUnsupported else Return := Fix32ToFloat(pTW_FIX32(@OneV^.Item)^); {Free data} GlobalUnlock(Handle); GlobalFree(Handle); end; end; {Returns a resolution} function TTwainSource.GetResolution(Capability: TW_UINT16; var Return: Extended; var Values: TTwainResolution; Mode: TRetrieveCap): TCapabilityRet; var Handle: HGlobal; EnumV: pTW_ENUMERATION; Container: TW_UINT16; Item: pTW_FIX32; i : Integer; begin {Obtain handle to data from this capability} Result := GetCapabilityRec(Capability, Handle, Mode, Container); if Result = crSuccess then begin {Obtain data} //npeter //the "if" is just for sure! if (Container<>TWON_ENUMERATION) and (Container<>TWON_ARRAY) then begin result:=crUnsupported; exit; end; EnumV := GlobalLock(Handle); if EnumV^.ItemType <> TWTY_FIX32 then Result := crUnsupported else begin {Set array size and pointer to the first item} Item := @EnumV^.ItemList[0]; SetLength(Values, EnumV^.NumItems); {Fill array} FOR i := 1 TO EnumV^.NumItems DO begin {Fill array with the item} Values[i - 1] := Fix32ToFloat(Item^); {Move to next item} inc(Item); end {FOR i}; {Fill return} //npeter //DefaultIndex and CurrentIndex valid for enum only! //I got nice AV with an old Mustek scanner which uses TWON_ARRAY //i return 0 in this case (may be not the best solution, but not AV at least :-) if (Container<>TWON_ARRAY) then begin if Mode = rcGetDefault then Return := Values[EnumV^.DefaultIndex] else Return := Values[EnumV^.CurrentIndex]; end else return:=0; end; {Free data} GlobalUnlock(Handle); GlobalFree(Handle); end; end; {Sets X resolution} function TTwainSource.SetIXResolution(Value: Extended): TCapabilityRet; var Fix32: TW_FIX32; begin Fix32 := FloatToFix32(Value); Result := SetOneValue(ICAP_XRESOLUTION, TWTY_FIX32, @Fix32); end; {Sets Y resolution} function TTwainSource.SetIYResolution(Value: Extended): TCapabilityRet; var Fix32: TW_FIX32; begin Fix32 := FloatToFix32(Value); Result := SetOneValue(ICAP_YRESOLUTION, TWTY_FIX32, @Fix32); end; {Returns X resolution} function TTwainSource.GetIXResolution(var Return: Extended; var Values: TTwainResolution; Mode: TRetrieveCap): TCapabilityRet; begin Result := GetResolution(ICAP_XRESOLUTION, Return, Values, Mode); end; {Returns Y resolution} function TTwainSource.GetIYResolution(var Return: Extended; var Values: TTwainResolution; Mode: TRetrieveCap): TCapabilityRet; begin Result := GetResolution(ICAP_YRESOLUTION, Return, Values, Mode); end; {Returns if user interface is controllable} function TTwainSource.GetUIControllable(var Return: Boolean): TCapabilityRet; var ItemType: TW_UINT16; Value : String; begin {Try to obtain value and make sure it is of type TW_BOOL} Result := GetOneValue(CAP_UICONTROLLABLE, ItemType, Value, rcGet); if (Result = crSuccess) and (ItemType <> TWTY_BOOL) then Result := crUnsupported; {Return value, by checked the return value from GetOneValue} if Result = crSuccess then Return := (Value = '1'); end; {Returns if feeder is loaded} function TTwainSource.GetFeederLoaded(var Return: Boolean): TCapabilityRet; var ItemType: TW_UINT16; Value : String; begin {Try to obtain value and make sure it is of type TW_BOOL} Result := GetOneValue(CAP_FEEDERLOADED, ItemType, Value, rcGet); if (Result = crSuccess) and (ItemType <> TWTY_BOOL) then Result := crUnsupported; {Return value, by checked the return value from GetOneValue} if Result = crSuccess then Return := (Value = '1'); end; {Returns if feeder is enabled} function TTwainSource.GetFeederEnabled(var Return: Boolean): TCapabilityRet; var ItemType: TW_UINT16; Value : String; begin {Try to obtain value and make sure it is of type TW_BOOL} Result := GetOneValue(CAP_FEEDERENABLED, ItemType, Value, rcGet); if (Result = crSuccess) and (ItemType <> TWTY_BOOL) then Result := crUnsupported; {Return value, by checked the return value from GetOneValue} if Result = crSuccess then Return := (Value = '1'); end; {Set if feeder is enabled} function TTwainSource.SetFeederEnabled(Value: WordBool): TCapabilityRet; begin {Call SetOneValue to set value} Result := SetOneValue(CAP_FEEDERENABLED, TWTY_BOOL, @Value); end; {Returns if autofeed is enabled} function TTwainSource.GetAutofeed(var Return: Boolean): TCapabilityRet; var ItemType: TW_UINT16; Value : String; begin {Try to obtain value and make sure it is of type TW_BOOL} Result := GetOneValue(CAP_AUTOFEED, ItemType, Value, rcGet); if (Result = crSuccess) and (ItemType <> TWTY_BOOL) then Result := crUnsupported; {Return value, by checked the return value from GetOneValue} if Result = crSuccess then Return := (Value = '1'); end; {Set if autofeed is enabled} function TTwainSource.SetAutoFeed(Value: WordBool): TCapabilityRet; begin {Call SetOneValue to set value} Result := SetOneValue(CAP_AUTOFEED, TWTY_BOOL, @Value); end; {Used with property PendingXfers} function TTwainSource.GetPendingXfers: TW_INT16; var PendingXfers: TW_PENDINGXFERS; begin if Loaded and Enabled then begin {Call method to retrieve} if Owner.TwainProc(AppInfo, @Structure, DG_CONTROL, DAT_PENDINGXFERS, MSG_GET, @PendingXfers) = TWRC_SUCCESS then Result := PendingXfers.Count else Result := ERROR_INT16; {Some error ocurred while calling message} end else Result := ERROR_INT16; {Source not loaded/enabled} end; {Returns a TMsg structure} function MakeMsg(const Handle: THandle; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): TMsg; begin {Fill structure with the parameters} Result.hwnd := Handle; Result.message := uMsg; Result.wParam := wParam; Result.lParam := lParam; GetCursorPos(Result.pt); end; {Virtual window procedure handler} function VirtualWinProc(Handle: THandle; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LResult; stdcall; {Returns the TDelphiTwain object} function Obj: TDelphiTwain; begin Longint(Result) := GetWindowLong(Handle, GWL_USERDATA); end {function}; var Twain: TDelphiTwain; i : Integer; Msg : TMsg; begin {Tests for the message} case uMsg of {Creation of the window} WM_CREATE: {Stores the TDelphiTwain object handle} with pCreateStruct(lParam)^ do SetWindowLong(Handle, GWL_USERDATA, Longint(lpCreateParams)); {case} else begin {Try to obtain the current object pointer} Twain := Obj; if Assigned(Twain) then {If there are sources loaded, we need to verify} {this message} if (Twain.SourcesLoaded > 0) then begin {Convert parameters to a TMsg} Msg := MakeMsg(Handle, uMsg, wParam, lParam); {Tell about this message} FOR i := 0 TO Twain.SourceCount - 1 DO if ((Twain.Source[i].Loaded) and (Twain.Source[i].Enabled)) then if Twain.Source[i].ProcessMessage(Msg) then begin {Case this was a message from the source, there is} {no need for the default procedure to process} Result := 0; Exit; end; end {if (Twain.SourcesLoaded > 0)} end {case Else} end {case uMsg of}; {Calls method to handle} Result := DefWindowProc(Handle, uMsg, wParam, lParam); end; //npeter: 2004.01.12 //sets the acquired area function TTwainSource.SetImagelayoutFrame(const fLeft, fTop, fRight, fBottom: double): TCapabilityRet; var ImageLayout: TW_IMAGELAYOUT; begin if not Loaded then begin Result := crInvalidState; {In case the source is not loaded} exit; end; fillchar(ImageLayout,sizeof(TW_IMAGELAYOUT),0); with ImageLayout.Frame do begin Left:=FloatToFIX32(fLeft); Top:=FloatToFIX32(fTop); Right:=FloatToFIX32(fRight); Bottom:=FloatToFIX32(fBottom); end; {Call method and store return} Result := ResultToCapabilityRec(Owner.TwainProc(AppInfo, @Structure, DG_IMAGE, DAT_IMAGELAYOUT, MSG_SET, @ImageLayout)); end; //npeter: 2004.01.12 //enable/disable progress indicators function TTwainSource.SetIndicators(Value: boolean): TCapabilityRet; begin {Call SetOneValue to set value} Result := SetOneValue(CAP_INDICATORS, TWTY_BOOL, @Value); end; {Information for the virtual window class} var VirtualWinClass: TWNDClass; initialization {Registers the virtual window class} VirtualWinClass.hInstance := hInstance; VirtualWinClass.style := 0; VirtualWinClass.lpfnWndProc := @VirtualWinProc; VirtualWinClass.cbClsExtra := 0; VirtualWinClass.cbWndExtra := 0; VirtualWinClass.hIcon := 0; VirtualWinClass.hCursor := 0; VirtualWinClass.hbrBackground := COLOR_WINDOW + 1; VirtualWinClass.lpszMenuName := ''; VirtualWinClass.lpszClassName := VIRTUALWIN_CLASSNAME; Windows.RegisterClass(VirtualWinClass); finalization {Unregisters the virtual window class} Windows.UnregisterClass(VIRTUALWIN_CLASSNAME, hInstance); end.