Scan2PDF

Share your advanced PureBasic knowledge/code with the community.
harkon
Enthusiast
Enthusiast
Posts: 217
Joined: Wed Nov 23, 2005 5:48 pm

Scan2PDF

Post by harkon »

A while ago I inherited a scanner who's driver cannot scan to PDF.

Using EZTwain Classic (free) I made my own. I added the ability to scan to PNG and to JPG. I needed this in order to use PurePDF.

I hope someone finds it useful.

Code for Scan2PDF program

Code: Select all

;Scan2PDF
;by Harkon
;written under PureBasic 4.41
;
;requires:
; EZTW32.dll 
; EZTwain.pbi ans needs EZTW32.LIB
;  both are available for download at http://www.dosadi.com/
;  as EZTwain Classic (this is a free download)
; PurePDF by ABBKlaus (appropriate version at http://www.purebasicpower.de/?PurePDF)
; sanner.ico for the .exe

IncludeFile "EZTwain.pbi"

EnableExplicit

;- Window Constants
;
Enumeration
  #Window_Scan2PDF
EndEnumeration

;- Gadget Constants
Enumeration
  #Combo_ImageType
  #Combo_ImageQuality
  #Text_ImageType
  #Text_ImageQuality
  #Text_Header
  #Text_FileLocation
  #Text_Location
  #Button_Browse
  #Button_ScanNext
  #Button_Finish
  #Button_Cancel
EndEnumeration

;- Fonts
Global lFontID1.l
lFontID1 = LoadFont(1, "Arial", 48, #PB_Font_Bold | #PB_Font_Italic)

;Variable defs
Define sPdfFileName.s
Define lQuality.l
Define lEvent.l
Define lWindowID.l
Define lGadgetID.l
Define lEventType.l

Procedure Open_Window_Scan2PDF()
  If OpenWindow(#Window_Scan2PDF, 268, 51, 378, 431, "Scan2PDF",  #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_TitleBar )
      ComboBoxGadget(#Combo_ImageType, 160, 120, 110, 20)
      ComboBoxGadget(#Combo_ImageQuality, 160, 150, 110, 20)
      TextGadget(#Text_ImageType, 50, 120, 90, 20, "Image Type:")
      TextGadget(#Text_ImageQuality, 50, 150, 90, 20, "Image Quality:")
      TextGadget(#Text_Header, 20, 10, 340, 80, "Scan2PDF")
      SetGadgetFont(#Text_Header, lFontID1)
      TextGadget(#Text_FileLocation, 10, 230, 340, 40, "", #PB_Text_Border)
      TextGadget(#Text_Location, 10, 210, 160, 20, "PDF File Destination Location:")
      ButtonGadget(#Button_Browse, 350, 250, 20, 20, "...")
      ButtonGadget(#Button_ScanNext, 90, 300, 90, 30, "Scan First Page")
      ButtonGadget(#Button_Finish, 200, 300, 90, 30, "Finish")
      ButtonGadget(#Button_Cancel, 120, 350, 150, 30, "Cancel / Close Program")
  EndIf
EndProcedure

Procedure.l hkTWAIN_AquireToPDF(wPixTypes.l, dResolution.d)
  Static PDFStarted.l
  Static PageNumber.l
  Define hDib.l
  Define sTempFileName.s

  If TWAIN_State()=#TWAIN_SOURCE_OPEN
      TWAIN_OpenDefaultSource()
    EndIf
    
    If Not TWAIN_GetHideUI()
      TWAIN_SetHideUI(1)
    EndIf

  TWAIN_SetCurrentResolution(dResolution)
  
  hDib=TWAIN_AcquireNative(0, wPixTypes)
  
  If hDib
    PageNumber+1
    sTempFileName=GetTemporaryDirectory() + "tempscan" + RSet(Str(PageNumber),3,"0") + ".png"
    
    hkTWAIN_WriteNativeToPNG(hdib, sTempFileName)
    TWAIN_FreeNative(hDib)
  
    
    If Not PDFStarted
      pdf_Create("P", "in", #PDF_PAGE_FORMAT_LETTER) 
      pdf_SetTitle("Scan")
      PDFStarted=1
    EndIf
    
    pdf_AddPage("P", #PDF_PAGE_FORMAT_LETTER)
    pdf_Image(sTempFileName, 0, 0, 8.5, 0)
    DeleteFile(sTempFileName)
    
    ;- replace this code with in window code
    SetGadgetText(#Button_ScanNext, "Scan Page #" + Str(PageNumber+1))

    
    ProcedureReturn 1
  EndIf
  ProcedureReturn 0  
EndProcedure

;before we actually do anything let's make sure the app isn't already running
If CreateMutex_(0,1,"Scan2PDF")=0 Or GetLastError_()<>0 : End : EndIf

Open_Window_Scan2PDF()

;- Startupcode
If Not TWAIN_IsAvailable()
  MessageRequester("Scan2PDF", "TWAIN scanner interface is not available!")
  End
EndIf

; populate combo boxes
If Not OpenPreferences("Scan2PDF.cfg")
  MessageRequester("Scan2PDF", "Could not find preferences file, using default values")
EndIf

AddGadgetItem(#Combo_ImageType, -1, "Black & White")
AddGadgetItem(#Combo_ImageType, -1, "GrayScale")
AddGadgetItem(#Combo_ImageType, -1, "Color (Photo)")

AddGadgetItem(#Combo_ImageQuality,-1, "  75 dpi")
AddGadgetItem(#Combo_ImageQuality,-1, " 100 dpi")
AddGadgetItem(#Combo_ImageQuality,-1, " 150 dpi")
AddGadgetItem(#Combo_ImageQuality,-1, " 300 dpi")
AddGadgetItem(#Combo_ImageQuality,-1, " 600 dpi")
AddGadgetItem(#Combo_ImageQuality,-1, "1200 dpi")

; initialize gadgets
SetGadgetState(#Combo_ImageType, ReadPreferenceInteger("Color Index", 2))
SetGadgetState(#Combo_ImageQuality, ReadPreferenceInteger("Quality Index", 0))
SetGadgetText(#Text_FileLocation, ReadPreferenceString("Destination File",""))
; disable finish button until we actually have a page to save
DisableGadget(#Button_Finish, 1)


Repeat ; Start of the event loop
  
  lEvent = WaitWindowEvent() ; This line waits until an event is received from Windows
  lWindowID = EventWindow() ; The Window where the event is generated, can be used in the gadget procedures
  lGadgetID = EventGadget() ; Is it a gadget event?
  lEventType = EventType() ; The event type
  
  If lEvent = #PB_Event_Gadget
    If lGadgetID = #Combo_ImageType  
    ElseIf lGadgetID = #Combo_ImageQuality 
    ElseIf lGadgetID = #Button_Browse
      ;user can select default file destination here
      sPdfFileName=GetGadgetText(#Text_FileLocation)
      If sPdfFileName=""
        sPdfFileName=SaveFileRequester("Save PDF file",GetHomeDirectory()+"My Documents\","PDF (*.pdf)|*.pdf",0)
        Else
        sPdfFileName=SaveFileRequester("Save PDF file",sPdfFileName,"PDF (*.pdf)|*.pdf",0)
      EndIf
      
      If LCase(GetExtensionPart(sPdfFileName))<> "pdf"
        sPdfFileName + ".pdf"
      EndIf
      SetGadgetText(#Text_FileLocation, sPdfFileName)
    
    ElseIf lGadgetID = #Button_ScanNext
      ;get dpi for page here
      ;color dep[th is in order of index+1
      Select  GetGadgetState(#Combo_ImageQuality)
        Case 0
          lQuality=75
        Case 1
          lQuality=100
        Case 2
          lQuality=150
        Case 3
          lQuality=300
        Case 4
          lQuality=600
        Case 5
          lQuality=1200
        Default
          lQuality=75
      EndSelect
      DisableGadget(#Button_ScanNext, 1)
      If hkTWAIN_AquireToPDF(GetGadgetState(#Combo_ImageType) + 1, lQuality)
        ;we have at least one page to save so enable the finish button
        DisableGadget(#Button_Finish, 0)
      EndIf
      DisableGadget(#Button_ScanNext, 0)
    
    ElseIf lGadgetID = #Button_Finish
      ;finalize and save pdf file
      sPdfFileName=GetGadgetText(#Text_FileLocation)
      If sPdfFileName=""
        sPdfFileName.s=SaveFileRequester("Save PDF file",GetHomeDirectory()+"My Documents\","PDF (*.pdf)|*.pdf",0)
        If LCase(GetExtensionPart(sPdfFileName))<> "pdf"
          sPdfFileName+".pdf"
        EndIf
      SetGadgetText(#Text_FileLocation, sPdfFileName)
      EndIf
      sPdfFileName=SaveFileRequester("Save PDF file",sPdfFileName,"PDF (*.pdf)|*.pdf",0)
      If sPdfFileName
        pdf_Save(GetGadgetText(#Text_FileLocation))
      EndIf
    
    ElseIf lGadgetID = #Button_Cancel
      lEvent = #PB_Event_CloseWindow
    EndIf
    
  EndIf  
Until lEvent = #PB_Event_CloseWindow ; End of the event loop

;save preferences on exit
If CreatePreferences("Scan2PDF.cfg")
  WritePreferenceInteger("Color Index", GetGadgetState(#Combo_ImageType))
  WritePreferenceInteger("Quality Index", GetGadgetState(#Combo_ImageQuality))
  WritePreferenceString("Destination File", GetGadgetText(#Text_FileLocation))
  ClosePreferences()
  Else
  MessageRequester("Scan2PDF", "Could not save program preferences")
EndIf

End
Code for EZTwain.pbi include file

Code: Select all

EnableExplicit

; XDefs translation of \EZTwain\VC\eztwain.h
;-----------------------------------------------------------------
; EZTWAIN.H - interface to Easy TWAIN library
; (DLL=eztw32.dll)
;
; 1.15     2006.05.09 Fix: If user closed the scan dialog during an Acquire,
;                     the last DIB handle, if any, was returned!
;                     Added VB\Eztwain.bas to package.
; 1.14     2004.08.06 Set XFERMECH=NATIVE as soon as DS is opened.
;                     trying to deal with scanners that default to memory xfer.
; 1.13     1999.09.08 Documented correct return codes of AcquireToFilename.
;                     - No code changes -
; 1.12     1998.09.14 Added Fix32ToFloat, allow MSG_OPENDS triplet.
;                     Added SetXferMech, XferMech.
; 1.11     1998.08.17 Added ToFix32, SetContrast, SetBrightness.
;                     Modified TWAIN_ToFix32 to round away-from-zero.
; 1.09beta 1998.07.27 Reverted from 1.08 to 1.06 and worked forward again.
; 1.06     1997.08.21 correction to message hook, fixed 32-bit exports
; 1.05     1996.11.06 32-bit conversion
; 1.04     1995.05.03 added: WriteNativeToFile, WriteNativeToFilename,
;                         FreeNative, SetHideUI, GetHideUI, SetCurrentUnits,
;                         GetCurrentUnits, SetCurrentResolution, SetBitDepth,
;                         SetCurrentPixelType, SetCapOneValue.
; 1.0a      1994.06.23 first alpha version
; 0.0      1994.05.11 created
;
; EZTWAIN 1.x is not a product, and is not the work of any company involved
; in promoting or using the TWAIN standard.  This code is sample code,
; provided without charge, and you use it entirely at your own risk.
; No rights or ownership is claimed by the author, or by any company
; or organization.  There are no restrictions on use or (re)distribution.
;
; Download from:    www.dosadi.com
;
; Support contact:  support@dosadi.com
;

; PB include file for EZTwain
; Ported from VB source by Harkon
;  provided by the good people at EZTwain www.dosadi.com

; Use these values for wPixTypes
#TWAIN_BW=1
#TWAIN_GRAY=2
#TWAIN_RGB=4
#TWAIN_PALETTE=8
#TWAIN_ANYTYPE=0

;Use these for TWAIN_State
#TWAIN_PRESESSION = 1
#TWAIN_SM_LOADED = 2
#TWAIN_SM_OPEN = 3
#TWAIN_SOURCE_OPEN = 4
#TWAIN_SOURCE_ENABLED = 5
#TWAIN_TRANSFER_READY = 6
#TWAIN_TRANSFERRING = 7

;use these for Xtransfer Mode
#TWAIN_XFERMECH_NATIVE = 0
#TWAIN_XFERMECH_FILE = 1
#TWAIN_XFERMECH_MEMORY = 2  

Import "EZTW32.LIB"

  TWAIN_AcquireNative.l(hwndApp.l, wPixTypes.l) As "_TWAIN_AcquireNative@8"
    ; The minimal use of EZTWAIN.DLL is to just call this routine, with 0 for
    ; both params.  EZTWAIN creates a window if hwndApp is 0.
    ; 
    ; Acquires a single image, from the currently selected Data Source, using
    ; Native-mode transfer. It waits until the source closes (if it's modal) Or
    ; forces the source closed if not.  The return value is a handle to the
    ; acquired image.  Only one image can be acquired per call.
    ; 
    ; Under Windows, the return value is a global memory handle - applying
    ; GlobalLock to it will return a (huge) pointer to the DIB, which
    ; starts with a BITMAPINFOHEADER.
    ; NOTE: You are responsible for disposing of the returned DIB - these things
    ; can eat up your Windows memory fast!  See TWAIN_FreeNative below.
    ; 
    ; The image type can be restricted using the following masks.  A mask of 0
    ; means 'any pixel type is welcome'.
    ; Caution: You should not assume that the source will honor a pixel type
    ; restriction!  If you care, check the parameters of the DIB.  
    
  TWAIN_FreeNative(hdib.l) As "_TWAIN_FreeNative@4"
    ; Release the memory allocated to a native format image, as returned by
    ; TWAIN_AcquireNative. (If you are coding in C or C++, this is just a call
    ; to GlobalFree.)
    ; If you use TWAIN_AcquireNative and don't free the returned image handle,
    ; it stays around taking up Windows (virtual) memory until your application
    ; terminates.  Memory required per square inch:
    ;             1 bit B&W       8-bit grayscale     24-bit color
    ; 100 dpi      1.25KB              10KB               30KB
    ; 200 dpi        5KB               40KB              120KB
    ; 300 dpi      11.25KB             90KB              270KB
    ; 400 dpi       20KB              160KB              480KB
    ; 
    
  TWAIN_AcquireToClipboard.l(hwndApp.l, wPixTypes.l) As "_TWAIN_AcquireToClipboard@8"
    ; Like AcquireNative, but puts the resulting image, if any, into the system
    ; clipboard.  Under Windows, this will put a CF_DIB item in the clipboard
    ; if successful.  If this call fails, the clipboard is either empty or
    ; contains the old contents.
    ; A return value of 1 indicates success, 0 indicates failure.
    ; 
    ; Useful for environments like Visual Basic where it is hard to make direct
    ; use of a DIB handle.  In fact, TWAIN_AcquireToClipboard uses
    ; TWAIN_AcquireNative for all the hard work.  

  TWAIN_AquireToFilename.l(hwndApp.l, sFile.s) As "_TWAIN_AcquireToFilename@8"
    ; Acquire an image and write it to a .BMP (Windows Bitmap) file.
    ; The file name and path in pszFile are used.  If pszFile is NULL or
    ; points to an empty string, the user is prompted with a Save File dialog.
    ; Return values:
    ; 0 success
    ; -1 Acquire failed OR user cancelled File Save dialog
    ; -2 file open error (invalid path or name, or access denied)
    ; -3 (weird) unable to lock DIB - probably an invalid handle.
    ; -4 writing BMP data failed, possibly output device is full  
  
  TWAIN_SelectImageSource.l(hwnd.l) As "_TWAIN_SelectImageSource@4"
    ; This is the routine to call when the user chooses the "Select Source..."
    ; menu command from your application's File menu.  Your app has one of
    ; these, right?  The TWAIN spec calls for this feature to be available in
    ; your user interface, preferably as described.
    ; Note: If only one TWAIN device is installed on a system, it is selected
    ; automatically, so there is no need for the user to do Select Source.
    ; You should not require your users to do Select Source before Acquire.
    ; '
    ; This function posts the Source Manager's Select Source dialog box.
    ; It returns after the user either OK's Or CANCEL's that dialog.
    ; A return of 1 indicates OK, 0 indicates one of the following:
    ;   a) The user cancelled the dialog
    ;   b) The Source Manager found no data sources installed
    ;   c) There was a failure before the Select Source dialog could be posted
    ; -- details --
    ; Only sources that can return images (that are in the DG_IMAGE group) are
    ; displayed.  The current default source will be highlighted initially.
    ; In the standard implementation of "Select Source...", your application
    ; doesn't need To do anything except make this one call.
    ; '
    ; If you want to be meticulous, disable your "Acquire" and "Select Source"
    ; menu items or buttons if TWAIN_IsAvailable() returns 0 - see below.  

 ; --- TWAIN Basic Inquiries ---
  TWAIN_IsAvailable.l() As "_TWAIN_IsAvailable@0"
    ; Call this function any time to find out if TWAIN is installed on the
    ; system.  It takes a little time on the first call, after that it's fast,
    ; just testing a flag.  It returns 1 if the TWAIN Source Manager is
    ; installed & can be loaded, 0 otherwise.
 
  TWAIN_EasyVersion.l() As "_TWAIN_EasyVersion@0"
    ; Returns the version number of EZTWAIN.DLL, multiplied by 100.
    ; So e.g. version 2.01 will return 201 from this call.
  
  TWAIN_State.l() As "_TWAIN_State@0"
    ;Returns the TWAIN Protocol State per the spec.
  
 ; --- DIB Handling ___ 
  TWAIN_DibDepth.l(hdib.l) As "_TWAIN_DibDepth@4"
    ; Depth of DIB, in bits i.e. bits per pixel.
  TWAIN_DibWidth.l(hdib.l) As "_TWAIN_DibWidth@4"
    ; Width of DIB, in pixels (columns)
  TWAIN_DibHeight.l(hdib.l) As "_TWAIN_DibHeight@4"
    ; Height of DIB, in lines (rows)
  TWAIN_DibNumColors.l(hdib) As "_TWAIN_DibNumColors@4"
    ; Number of colors in color table of DIB
  DIB_RowBytes.l(hdib.l) As "_DIB_RowBytes@4"
    ; Number of bytes consumed by one row
  DIB_ReadRow(hdib.l, nRow.l, *prow) As "_DIB_ReadRow@12"
      ; Read row n of the given DIB into buffer at prow.
      ; Caller is responsible for ensuring buffer is large enough.
      ; Row 0 is the *top* row of the image, as it would be displayed.
  TWAIN_CreateDibPalette.l(hdib.l) As "_TWAIN_CreateDibPalette@4"
      ; Create and return a logical palette to be used for drawing the DIB.
      ; For 1, 4, and 8-bit DIBs the palette contains the DIB color table.
      ; For 24-bit DIBs, a default halftone palette is returned.
  TWAIN_DrawDibToDC(hDC.l, dx.l, dy.l, w.l, h.l, hdib.l, sx.l, sy.l) As "_TWAIN_DrawDibToDC@32"
      ; Draws a DIB on a device context.
      ; You should call CreateDibPalette, select that palette
      ; into the DC, and do a RealizePalette(hDC) first.
  
  ; ---  BMP File Utilities ---
  TWAIN_WriteNativeToFilename.l(hdib.l, sFile.s) As "_TWAIN_WriteNativeToFilename@8"
      ; Writes a DIB handle to a .BMP file
      ; 
      ; hdib      = DIB handle, as returned by TWAIN_AcquireNative
      ; pszFile   = far pointer to NUL-terminated filename
      ; If pszFile is NULL or points to a null string, prompts the user
      ; for the filename with a standard file-save dialog.
      ; 
      ; Return values:
      ;    0  success
      ;   -1  user cancelled File Save dialog
      ;   -2  file open error (invalid path or name, or access denied)
      ;   -3  (weird) unable to lock DIB - probably an invalid handle.
      ;   -4  writing BMP data failed, possibly output device is full  
   TWAIN_WriteNativeToFile.l(hdib.l, fh.l) As "_TWAIN_WriteNativeToFile@8"
      ; Writes a DIB to a file in .BMP format.
      ; 
      ; hdib      = DIB handle, as returned by TWAIN_AcquireNative
      ; fh        = file handle, as returned by _open, _lopen or OpenFile
      ; 
      ; Return value as for TWAIN_WriteNativeToFilename
   TWAIN_LoadNativeFromFilename.l(sFile.s) As "_TWAIN_LoadNativeFromFilename@4"
      ; Load a .BMP file and return a DIB handle (as from AcquireNative.)
      ; Accepts a filename (including path & extension).
      ; If pszFile is NULL or points to a null string, the user is prompted.
      ; Returns a DIB handle if successful, otherwise NULL.
  TWAIN_LoadNativeFromFile.l(fh.l) As "_TWAIN_LoadNativeFromFile@4"
      ; Like LoadNativeFromFilename, but takes an already open file handle.  
  TWAIN_SetHideUI(fHide.l) As "_TWAIN_SetHideUI@4"
  TWAIN_GetHideUI.l() As "_TWAIN_GetHideUI@0"
      ; These functions control the 'hide source user Interface' flag.
      ; This flag is cleared initially, but if you set it non-zero, then when
      ; a source is enabled it will be asked to hide its user interface.
      ; Note that this is only a request - some sources will ignore it!
      ; This affects AcquireNative, AcquireToClipboard, and EnableSource.
      ; If the user interface is hidden, you will probably want to set at least
      ; some of the basic acquisition parameters yourself - see
      ; SetCurrentUnits, SetBitDepth, SetCurrentPixelType and
      ; SetCurrentResolution below.
      
  ; --- Appliucation Registration ---
  TWAIN_RegisterApp(nMajorNum.l, nMinorNum.l, nLanguage.l, nCountry.l, lpszVersion.s, lpszMfg.s, lpszFamily.s, lpszProduct.s) As "_TWAIN_RegisterApp@32"
    ; TWAIN_RegisterApp can be called *AS THE FIRST CALL*, to register the
    ; application. If this function is not called, the application is given a
    ; 'generic' registration by EZTWAIN.
    ; Registration only provides this information to the Source Manager and any
    ; sources you may open - it is used for debugging, and (frankly) by some
    ; sources to give special treatment to certain applications.

  ; --- Error Analysis and Reporting ---
  TWAIN_GetResultCode.l() As "_TWAIN_GetResultCode@0"
    ; Return the result code (TWRC_xxx) from the last triplet sent to TWAIN
  TWAIN_GetConditionCode.l() As "_TWAIN_GetConditionCode@0"
    ; Return the condition code from the last triplet sent to TWAIN.
    ; (To be precise, from the last call to TWAIN_DS below)
  TWAIN_ErrorBox(sMsg.s) As "_TWAIN_ErrorBox@4"
    ; Post an error message dialog with an exclamation mark, OK button,
    ; and the title 'TWAIN Error'.
    ; pszMsg points to a null-terminated message string.
  TWAIN_ReportLastError(sMsg.s) As "_TWAIN_ReportLastError@4"
    ; Like TWAIN_ErrorBox, but if some details are available from
    ; TWAIN about the last failure, they are included in the message box.  
  
  ; --- TWAIN State Control ---
  TWAIN_LoadSourceManager.l() As "_TWAIN_LoadSourceManager@0"
    ; Finds and loads the Data Source Manager, TWAIN.DLL.
    ; If Source Manager is already loaded, does nothing and returns TRUE.
    ; This can fail if TWAIN.DLL is not installed (in the right place), or
    ; if the library cannot load for some reason (insufficient memory?) or
    ; if TWAIN.DLL has been corrupted.  
  TWAIN_OpenSourceManager.l(hwnd.l) As "_TWAIN_OpenSourceManager@4"
    ; Opens the Data Source Manager, if not already open.
    ; If the Source Manager is already open, does nothing and returns TRUE.
    ; This call will fail if the Source Manager is not loaded.  
  TWAIN_OpenDefaultSource.l() As "_TWAIN_OpenDefaultSource@0"
    ; This opens the source selected in the Select Source dialog.
    ; If a source is already open, does nothing and returns TRUE.
    ; Fails if the source manager is not loaded and open.
  TWAIN_EnableSource.l(hwnd.l) As "_TWAIN_EnableSource@4"
    ; Enables the open Data Source. This posts the source's user Interface
    ; And allows image acquisition To begin.  If the source is already enabled,
    ; this call does nothing And returns TRUE.
  TWAIN_DisableSource .l() As "_TWAIN_DisableSource@0"
    ; Disables the open Data Source, if any.
    ; This closes the source's user Interface.
    ; If there is Not an enabled source, does nothing And returns TRUE.
  TWAIN_CloseSource.l() As "_TWAIN_CloseSource@0"
    ; Closes the open Data Source, if any.
    ; If the source is enabled, disables it first.
    ; If there is Not an open source, does nothing And returns TRUE.
  TWAIN_CloseSourceManager.l(hwnd.l) As "_TWAIN_CloseSourceManager@4"
    ; Closes the Data Source Manager, If it is open.
    ; If a source is open, disables And closes it As needed.
    ; If the Source Manager is Not open, does nothing And returns TRUE.
  TWAIN_UnloadSourceManager.l() As "_TWAIN_UnloadSourceManager@0"
    ; Unloads the Data Source Manager i.e. TWAIN.DLL - releasing
    ; any associated memory Or resources.
    ; This call will fail If the Source Manager is open, otherwise
    ; it always succeeds And returns TRUE.
  TWAIN_WaitForNativeXfer.l(hwnd.l) As "_TWAIN_WaitForNativeXfer@4"
  TWAIN_ModalEventLoop() As "_TWAIN_ModalEventLoop@0"
    ; Process messages Until termination, source disable, Or image transfer.
  TWAIN_EndXfer.l() As "_TWAIN_EndXfer@0"
  TWAIN_AbortAllPendingXfers.l() As "_TWAIN_AbortAllPendingXfers@0"
  
  
  ; --- High-level Capability Negotiation Functions ---
  TWAIN_NegotiateXferCount.l(nXfers.l) As "_TWAIN_NegotiateXferCount@4"
    ; Negotiate With open Source the number of images application will accept.
    ; This is only allowed in State 4 (TWAIN_SOURCE_OPEN)
    ; nXfers = -1 means any number
  TWAIN_NegotiatePixelTypes.l(wPixTypes.l) As "_TWAIN_NegotiatePixelTypes@4"
    ; Negotiate with the source to restrict pixel types that can be acquired.
    ; This tries To restrict the source To a *set* of pixel types,
    ; See TWAIN_AcquireNative above For some mask constants.
    ; --> This is only allowed in State 4 (TWAIN_SOURCE_OPEN)
    ; A parameter of 0 (TWAIN_ANYTYPE) causes no negotiation & no restriction.
    ; You should Not assume that the source will honor your restrictions, even
    ; If this call succeeds!
  TWAIN_GetCurrentUnits.l() As "_TWAIN_GetCurrentUnits@0"
    ; Ask the source what its current unit of measure is.
    ; If anything goes wrong, this function just returns TWUN_INCHES (0).
  TWAIN_SetCurrentUnits.l(nUnits.l) As "_TWAIN_SetCurrentUnits@4"
    ; Set the current unit of measure for the source.
    ; Unit of measure codes are in TWAIN.H, but TWUN_INCHES is 0.
  TWAIN_GetBitDepth.l() As "_TWAIN_GetBitDepth@0"
    ; Get the current bitdepth, which can depend on the current PixelType.
    ; Bit depth is per color channel e.g. 24-bit RGB has bit depth 8.
    ; If anything goes wrong, this function returns 0.
  TWAIN_SetBitDepth.l(nBits.l) As "_TWAIN_SetBitDepth@4"
    ; (Try to) set the current bitdepth (for the current pixel type).
  TWAIN_GetPixelType.l() As "_TWAIN_GetPixelType@0"
    ; Ask the source For the current pixel type.
    ; If anything goes wrong (it shouldn't), this function returns 0 (TWPT_BW).
  TWAIN_SetCurrentPixelType.l(nPixType.l) As "_TWAIN_SetCurrentPixelType@4"
    ; (Try To) set the current pixel type For acquisition.
    ; This is only allowed in State 4 (TWAIN_SOURCE_OPEN)
    ; The source may Select this pixel type, but don't assume it will.
  TWAIN_GetCurrentResolution.d() As "_TWAIN_GetCurrentResolution@0"
    ; Ask the source For the current (horizontal) resolution.
    ; Resolution is in dots per current unit! (See TWAIN_GetCurrentUnits above)
    ; If anything goes wrong (it shouldn't) this function returns 0.0
  TWAIN_GetYResolution.d() As "_TWAIN_GetYResolution@0"
    ; Returns the current vertical resolution, in dots per *current unit*.
    ; In the event of failure, returns 0.0.
  TWAIN_SetCurrentResolution.l(dRes.d) As "_TWAIN_SetCurrentResolution@8"
    ; (Try To) set the current resolution For acquisition.
    ; Resolution is in dots per current unit! (See TWAIN_GetCurrentUnits above)
    ; This is only allowed in State 4 (TWAIN_SOURCE_OPEN)
    ; Note: The source may Select this resolution, but don't assume it will.
  TWAIN_SetContrast.l(dCon.d) As "_TWAIN_SetContrast@8"
    ; (Try To) set the current contrast For acquisition.
    ; The TWAIN standard says that the range For this cap is -1000 ... +1000
  TWAIN_SetBrightness.l(dBri.d) As "_TWAIN_SetBrightness@8"
    ; (Try To) set the current brightness For acquisition.
    ; The TWAIN standard says that the range For this cap is -1000 ... +1000
  TWAIN_SetXferMech.l(mech.l) As "_TWAIN_SetXferMech@4"
  TWAIN_XferMech.l() As "_TWAIN_XferMech@0"
    ; (Try To) set Or get the transfer mode - one of the following:
    ; #TWAIN_XFERMECH_NATIVE = 0
    ; #TWAIN_XFERMECH_FILE = 1
    ; #TWAIN_XFERMECH_MEMORY = 2  

  ; --- Low-level Capability Negotiation Functions ---
    ; Setting a capability is valid only in State 4 (TWAIN_SOURCE_OPEN)
    ; Getting a capability is valid in State 4 Or any higher state.
  TWAIN_SetCapOneValue.l(Cap.l, ItemType.l, ItemVal.l) As "_TWAIN_SetCapOneValue@12"
    ; Do a DAT_CAPABILITY/MSG_SET, on capability 'Cap' (e.g. ICAP_PIXELTYPE,
    ; CAP_AUTOFEED, etc.) using a TW_ONEVALUE container With the given item type
    ; And value.  The item value must fit into 32 bits.
    ; Returns TRUE (1) If successful, FALSE (0) otherwise.
  TWAIN_GetCapCurrent.l(Cap.l, ItemType.l, *pVal) As "_TWAIN_GetCapCurrent@12"
    ; Do a DAT_CAPABILITY/MSG_GETCURRENT on capability 'Cap'.
    ; Copy the current value out of the returned container into *pVal.
    ; If the operation fails (the source refuses the request), Or If the
    ; container is Not a ONEVALUE Or Enumeration, Or If the item type of the
    ; returned container is incompatible With the expected TWTY_ type in nType,
    ; returns FALSE.  If this function returns FALSE, *pVal is Not touched.
  TWAIN_ToFix32.l(d.d) As "_TWAIN_ToFix32@8"
    ; Convert a floating-point value To a 32-bit TW_FIX32 value that can be passed
    ; To e.g. TWAIN_SetCapOneValue
  TWAIN_Fix32ToFloat.d(nfix.l) As "_TWAIN_Fix32ToFloat@4"
    ; Convert a TW_FIX32 value (As returned from some capability inquiries)
    ; To a double (floating point) value.  
  
  ; --- Lowest-level functions For TWAIN protocol ---


  TWAIN_DS.l(DG.l, DAT.l, MSG.l, *pData) As "_TWAIN_DS@16"
    ; Passes the triplet (DG, DAT, MSG, pData) To the open Data source If any.
    ; Returns 1 (TRUE) If the result code is TWRC_SUCCESS, 0 (FALSE) otherwise.
    ; The last result code can be retrieved With TWAIN_GetResultCode(), And the corresponding
    ; condition code can be retrieved With TWAIN_GetConditionCode().
    ; If no source is open this call will fail, result code TWRC_FAILURE, condition code TWCC_NODS.

  TWAIN_Mgr.l(DG.l, DAT.l, MSG.l, *pData) As "_TWAIN_Mgr@16"
    ; Passes a triplet To the Data Source Manager (DSM).
    ; Returns 1 (TRUE) If the result code is TWRC_SUCCESS, 0 (FALSE) otherwise.
    ; The last result code can be retrieved With TWAIN_GetResultCode(), And the corresponding
    ; condition code can be retrieved With TWAIN_GetConditionCode().
    ; If the Source Manager is Not open, this call will fail, And set the result code To TWRC_FAILURE,
    ; With a condition code of TWCC_SEQERROR (triplet out of sequence).
  
EndImport

UseJPEGImageEncoder()
UsePNGImageEncoder()

Procedure.l hkTWAIN_WriteNativeToPNG(hDib.l, sPNGFileName.s)
  Define lpDib.l
  Define StructSize.l
  Define Width.l 
  Define Height.l
  Define Planes.w
  Define BitCount.w
  Define Compression.l
  Define SizeImage.l
  Define XPix_Meter.l
  Define YPix_Meter.l
  Define ColorsUsed.l
  Define ColorsImportant.l
  Define PalletteSize.l
  Define BMPOffset.l
  Define BMPSize.l
  Define ImageNumber.l
  Define *BMPBuffer
  
  lpDib=GlobalLock_(hDib)
  
  StructSize=PeekL(lpDib)
  Width=PeekL(lpDib+4)
  Height=PeekL(lpDib+8)
  Planes=PeekW(lpDib+12)
  BitCount=PeekW(lpDib+14)
  Compression=PeekL(lpDib+16)
  SizeImage=PeekL(lpDib+20)
  XPix_Meter=PeekL(lpDib+24)
  YPix_Meter=PeekL(lpDib+28)
  ColorsUsed=PeekL(lpDib+32)
  ColorsImportant=PeekL(lpDib+36)
 
  ;make BMP for CatchImage
  If BitCount<24
    PalletteSize=BitCount*8
  EndIf
  ;BMPFileHeader is
  ;"BM",BMPSize.l,Reserved.w, Reserved.w, Offset.l where bmp data starts
  ; the offset is after the pallette info
  ; pallette info is RGB+1 byte for each color in the pallette
  ; unless it's 24bit then there's no pallette
  BMPOffset=14+StructSize+PalletteSize
  BMPSize=BMPOffset+SizeImage
  
  *BMPBuffer=AllocateMemory(BMPSize)
  PokeS(*BMPBuffer,"BM")
  PokeL(*BMPBuffer+2,BMPSize)
  PokeL(*BMPBuffer+10,BMPOffset)
  CopyMemory(lpDib, *BMPBuffer+14, BMPSize-14)
  
  GlobalUnlock_(hDib)
  
  ImageNumber=CatchImage(#PB_Any,*BMPBuffer,BMPSize)
  
  If sPNGFileName=""
    sPNGFileName=SaveFileRequester("Save PNG file",GetHomeDirectory()+"My Documents\","PNG (*.png)|*.png",0)
  EndIf
  
  ProcedureReturn SaveImage(ImageNumber,sPNGFileName,#PB_ImagePlugin_PNG)
  FreeImage(ImageNumber)
  FreeMemory(*BMPBuffer)
  

EndProcedure

Procedure.l hkTWAIN_WriteNativeToJPG(hDib.l, sJPGFileName.s, lQuality.l)
  Define lpDib.l
  Define StructSize.l
  Define Width.l 
  Define Height.l
  Define Planes.w
  Define BitCount.w
  Define Compression.l
  Define SizeImage.l
  Define XPix_Meter.l
  Define YPix_Meter.l
  Define ColorsUsed.l
  Define ColorsImportant.l
  Define PalletteSize.l
  Define BMPOffset.l
  Define BMPSize.l
  Define ImageNumber.l
  Define *BMPBuffer

  lpDib=GlobalLock_(hDib)
  
  StructSize=PeekL(lpDib)
  Width=PeekL(lpDib+4)
  Height=PeekL(lpDib+8)
  Planes=PeekW(lpDib+12)
  BitCount=PeekW(lpDib+14)
  Compression=PeekL(lpDib+16)
  SizeImage=PeekL(lpDib+20)
  XPix_Meter=PeekL(lpDib+24)
  YPix_Meter=PeekL(lpDib+28)
  ColorsUsed=PeekL(lpDib+32)
  ColorsImportant=PeekL(lpDib+36)
 
  ;make BMP for CatchImage
  If BitCount<24
    PalletteSize=BitCount*8
  EndIf
  ;BMPFileHeader is
  ;"BM",BMPSize.l,Reserved.w, Reserved.w, Offset.l where bmp data starts
  ; the offset is after the pallette info
  ; pallette info is RGB+1 byte for each color in the pallette
  ; unless it's 24bit then there's no pallette
  BMPOffset=14+StructSize+PalletteSize
  BMPSize=BMPOffset+SizeImage
  
  *BMPBuffer=AllocateMemory(BMPSize)
  PokeS(*BMPBuffer,"BM")
  PokeL(*BMPBuffer+2,BMPSize)
  PokeL(*BMPBuffer+10,BMPOffset)
  CopyMemory(lpDib, *BMPBuffer+14, BMPSize-14)
  
  GlobalUnlock_(hDib)
  
  ImageNumber=CatchImage(#PB_Any,*BMPBuffer,BMPSize)
  
  If sJPGFileName=""
    sJPGFileName.s=SaveFileRequester("Save JPG file",GetHomeDirectory()+"My Documents\","JPG (*.jpg)|*.jpg",0)
  EndIf
  
  ;lQuality is spcified in terms of a number between 1 and 10, 10 being the best quality lowest compression
  ; if not specified and the quality is 0 or invalid, we will default to 7
  If lQuality<1 Or lQuality>10
    lQuality=7
  EndIf  
  ProcedureReturn SaveImage(ImageNumber,sJPGFileName,#PB_ImagePlugin_JPEG, lQuality)
  FreeImage(ImageNumber)
  FreeMemory(*BMPBuffer)
  
EndProcedure

DisableExplicit
Missed it by that much!!
HK
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Scan2PDF

Post by IdeasVacuum »

Thank you for sharing your hard work.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4747
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: Scan2PDF

Post by Fangbeast »

Old post, I know, I know!!

I've just found a use for this but get the following error. "Attempt to set capability outside State 4"

Then the scan starts and saves an empty page.

PurePdf is installed, EZTW32.dll, EZTwain.pbi and EZTW32.LIB are all in the same directory as the sample code.

**Edit**

In "Procedure.l hkTWAIN_AquireToPDF(wPixTypes.l, dResolution.d)", I commented out "DeleteFile(sTempFileName)" to see what; if anything; the temporary image contained and found that it was empty so the PDF isn't the problem.

Not sure what to do here.
Amateur Radio, D-STAR/VK3HAF
Num3
PureBasic Expert
PureBasic Expert
Posts: 2810
Joined: Fri Apr 25, 2003 4:51 pm
Location: Portugal, Lisbon
Contact:

Re: Scan2PDF

Post by Num3 »

Fangbeast wrote:Old post, I know, I know!!

I've just found a use for this but get the following error. "Attempt to set capability outside State 4"

Then the scan starts and saves an empty page.

PurePdf is installed, EZTW32.dll, EZTwain.pbi and EZTW32.LIB are all in the same directory as the sample code.

**Edit**

In "Procedure.l hkTWAIN_AquireToPDF(wPixTypes.l, dResolution.d)", I commented out "DeleteFile(sTempFileName)" to see what; if anything; the temporary image contained and found that it was empty so the PDF isn't the problem.

Not sure what to do here.
Hi mate,

That error is from the Scanner Driver, if i recall correctly, it's because you're trying to scan at a DPI or Color palette that your scanner does not support! (Twain specs did not contemplate error handling, so on some scanners it works on others it just crashes the driver...)

Try 150 DPI & Color, i think that is pretty standard and it should work with most scanners.
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4747
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: Scan2PDF

Post by Fangbeast »

Hi mate,
G'day.
That error is from the Scanner Driver, if i recall correctly, it's because you're trying to scan at a DPI or Color palette that your scanner does not support!
Strange, my Canon LIDE25 scanner supports up to 400DPI. Available settings below.

Black and White, Grayscale, Colour, Colour(Magazine), Colour(Multi-Scan)
Colour(Auto-Crop)

75 DPI, 100 DPI, 150 DPI, 200 DPI, 300 DPI, 400 DPI
Try 150 DPI & Color, i think that is pretty standard and it should work with most scanners.
I tried all the way down to 75 with the same results. When I use my own canon scanner software, I can use 400DPI and it works fine.

Thanks for trying.
Amateur Radio, D-STAR/VK3HAF
Num3
PureBasic Expert
PureBasic Expert
Posts: 2810
Joined: Fri Apr 25, 2003 4:51 pm
Location: Portugal, Lisbon
Contact:

Re: Scan2PDF

Post by Num3 »

Fear not! :twisted:

I went to the old stuff in my HDD, and here's something i made back in 2003 :lol:

Still works fine, and it's the most simple way to use twain (only drawback is that it does not support ADF feeders)

Code:
It's old, i just tweaked it to work with PB 4.xx

Code: Select all

If OpenLibrary(0, "Twain32d.dll")
  IsAvailable.i  = GetFunction(0, "TWAIN_IsAvailable")
  SelectImageSorce.i  = GetFunction(0, "TWAIN_SelectImageSource")
  AcquireToFilename.i  = GetFunction(0, "TWAIN_AcquireToFilename")
  AcquireToClipboard.i = GetFunction(0,"TWAIN_AcquireToClipboard")
  
Else
  MessageRequester("Error",  "Could Not Open DLL", #MB_ICONERROR)
  End
EndIf


picpath.s  = GetTemporaryDirectory()+"scanned.bmp"
#cmdSelect  = 1
#cmdScan  = 2
#cmdView  = 3
#img  = 0

hWnd  = OpenWindow(0, 0, 0, 767, 476, "Scan", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
If hWnd  = 0
  End
EndIf
ButtonGadget(#cmdSelect,  10, 10, 190, 30, "1. Select TWAIN source")
ButtonGadget(#cmdScan,  210, 10, 190, 30, "2. Scan!")
ButtonGadget(#cmdView,  410, 10, 190, 30, "3. View Result")

Repeat
  EventID  = WaitWindowEvent()
  Select EventID
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #cmdSelect
          Gosub cmdSelect_Click
        Case #cmdScan
          Gosub cmdScan_Click
          Gosub cmdView
        Case #cmdView
          Gosub cmdView
      EndSelect
  EndSelect
Until EventID  = #PB_Event_CloseWindow
End

cmdSelect_Click :
result  = CallFunctionFast(SelectImageSorce, hWnd)
Return

cmdScan_Click :

; Import these at your convenience 
;result = CallFunction(0,"TWAIN_SetHideUI")
;result = CallFunction(0,"TWAIN_OpenDefaultSource")
;result = CallFunction(0,"TWAIN_SetCurrentPixelType",$2)
;result = CallFunction(0,"TWAIN_SetBitDepth",4)
;result  = CallFunctionFast(AcquireToClipboard,hWnd)


result  = CallFunctionFast(AcquireToFilename, hWnd, @picpath)
Return

cmdView :

RunProgram (picpath)

Return
The DLL (it's free)
https://www.dropbox.com/s/ras2wj5f8y0t65o/TWain32d.DLL
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4747
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: Scan2PDF

Post by Fangbeast »

Fear not! :twisted:
I fear, I fear!!!
I went to the old stuff in my HDD, and here's something i made back in 2003 :lol:
Bugger, everyone is ahead of me :):)
Still works fine, and it's the most simple way to use twain (only drawback is that it does not support ADF feeders)
I have no idea what ADF feeders are so I won't stress.

Well, tie me up with hamster legs and call me Martha, it works. Now if I can combine that with the other code to save to PDF, I will be mostly home.

Thanks for that Num3. I know bugger all about this sort of stuff.
Amateur Radio, D-STAR/VK3HAF
harkon
Enthusiast
Enthusiast
Posts: 217
Joined: Wed Nov 23, 2005 5:48 pm

Re: Scan2PDF

Post by harkon »

There is a slightly newer version that dealt with some of those errors.

Scan2PDF code

Code: Select all

;Scan2PDF
;by Harkon
;written under PureBasic 4.41
;
;requires:
; EZTW32.dll 
; EZTwain.pbi ans needs EZTW32.LIB
;  both are available for download at http://www.dosadi.com/
;  as EZTwain Classic (this is a free download)
; PurePDF by ABBKlaus (appropriate version at http://www.purebasicpower.de/?PurePDF)
; sanner.ico for the .exe

IncludeFile "EZTwain.pbi"

EnableExplicit

;- Window Constants
;
Enumeration
  #Window_Scan2PDF
EndEnumeration

;- Gadget Constants
Enumeration
  #Combo_ImageType
  #Combo_ImageQuality
  #Text_ImageType
  #Text_ImageQuality
  #Text_Header
  #Text_FileLocation
  #Text_Location
  #Button_Browse
  #Button_ScanNext
  #Button_Finish
  #Button_Cancel
  #Button_Select_Source
  #Text_VersionInfo
EndEnumeration

;- Fonts
Global lFontID1.l
lFontID1 = LoadFont(1, "Arial", 48, #PB_Font_Bold | #PB_Font_Italic)

;Variable defs
Define sPdfFileName.s
Define lQuality.l
Define lEvent.l
Define lWindowID.l
Define lGadgetID.l
Define lEventType.l
Define sAppPath.s=GetPathPart(ProgramFilename())
Define sCfgFileName.s=sAppPath + "Scan2PDF.cfg"
Define sPgmVersion.s="version 0.1." + Str(#PB_Editor_CompileCount) + "." + Str(#PB_Editor_BuildCount)

Procedure Open_Window_Scan2PDF()
  If OpenWindow(#Window_Scan2PDF, 268, 51, 378, 431, "Scan2PDF",  #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_TitleBar )
      ComboBoxGadget(#Combo_ImageType, 190, 100, 110, 20)
      ComboBoxGadget(#Combo_ImageQuality, 190, 130, 110, 20)
      TextGadget(#Text_ImageType, 80, 100, 90, 20, "Image Type:")
      TextGadget(#Text_ImageQuality, 80, 130, 90, 20, "Image Quality:")
      TextGadget(#Text_Header, 20, 10, 340, 80, "Scan2PDF")
      SetGadgetFont(#Text_Header, lFontID1)
      TextGadget(#Text_FileLocation, 10, 230, 340, 40, "", #PB_Text_Border)
      TextGadget(#Text_Location, 10, 210, 160, 20, "PDF File Destination Location:")
      ButtonGadget(#Button_Browse, 350, 250, 20, 20, "...")
      ButtonGadget(#Button_ScanNext, 90, 300, 90, 30, "Scan Next Page")
      ButtonGadget(#Button_Finish, 200, 300, 90, 30, "Finish")
      ButtonGadget(#Button_Cancel, 120, 350, 150, 30, "Cancel / Close Program")
      ButtonGadget(#Button_Select_Source, 190, 160, 110, 20, "Select Source")
      TextGadget(#Text_VersionInfo, 10, 400, 360, 20, "")
  EndIf
EndProcedure

Procedure.l hkTWAIN_AquireToPDF(wPixTypes.l, dResolution.d, reset.w)
  Static PDFStarted.l
  Static PageNumber.l
  Define hDib.l
  Define sTempFileName.s

  If reset
    PDFStarted=0
    PageNumber=0
    ProcedureReturn 1
  EndIf

  If TWAIN_State()<>#TWAIN_SOURCE_OPEN
      TWAIN_OpenDefaultSource()
  EndIf
    
  If Not TWAIN_GetHideUI()
    TWAIN_SetHideUI(1)
  EndIf

  TWAIN_SetCurrentResolution(dResolution)
  
  ;color should be 4 not 3
  ; this is a hck fix
  If wPixTypes=3
    wPixtypes=4
  EndIf
  
  hDib=TWAIN_AcquireNative(0, wPixTypes)
  
  If hDib
    PageNumber+1
    sTempFileName=GetTemporaryDirectory() + "tempscan" + RSet(Str(PageNumber),3,"0") + ".png"
    
    hkTWAIN_WriteNativeToPNG(hdib, sTempFileName)
    TWAIN_FreeNative(hDib)
  
    
    If Not PDFStarted
      pdf_Create("P", "in", #PDF_PAGE_FORMAT_LETTER) 
      pdf_SetTitle("Scan")
      PDFStarted=1
    EndIf
    
    pdf_AddPage("P", #PDF_PAGE_FORMAT_LETTER)
    pdf_Image(sTempFileName, 0, 0, 8.5, 0)
    DeleteFile(sTempFileName)
    
    ;- replace this code with in window code
    SetGadgetText(#Button_ScanNext, "Scan Page #" + Str(PageNumber+1))

    
    ProcedureReturn 1
  EndIf
  ProcedureReturn 0  
EndProcedure

;before we actually do anything let's make sure the app isn't already running
If CreateMutex_(0,1,"Scan2PDF")=0 Or GetLastError_()<>0
  End
EndIf

Open_Window_Scan2PDF()

;- Startupcode
If Not TWAIN_IsAvailable()
  MessageRequester("Scan2PDF", "TWAIN scanner interface is not available!")
  End
EndIf

; populate combo boxes
If Not OpenPreferences(sCfgFileName)
  MessageRequester("Scan2PDF", "Could not find preferences file, using default values")
EndIf

AddGadgetItem(#Combo_ImageType, -1, "Black & White")
AddGadgetItem(#Combo_ImageType, -1, "GrayScale")
AddGadgetItem(#Combo_ImageType, -1, "Color (Photo)")

AddGadgetItem(#Combo_ImageQuality,-1, "  75 dpi")
AddGadgetItem(#Combo_ImageQuality,-1, " 100 dpi")
AddGadgetItem(#Combo_ImageQuality,-1, " 150 dpi")
AddGadgetItem(#Combo_ImageQuality,-1, " 300 dpi")
AddGadgetItem(#Combo_ImageQuality,-1, " 600 dpi")
AddGadgetItem(#Combo_ImageQuality,-1, "1200 dpi")

; initialize gadgets
SetGadgetState(#Combo_ImageType, ReadPreferenceInteger("Color Index", 2))
SetGadgetState(#Combo_ImageQuality, ReadPreferenceInteger("Quality Index", 0))
SetGadgetText(#Text_FileLocation, ReadPreferenceString("Destination File",""))
; disable finish button until we actually have a page to save
DisableGadget(#Button_Finish, 1)
SetGadgetText(#Text_VersionInfo, sPgmVersion)

Repeat ; Start of the event loop
  
  lEvent = WaitWindowEvent() ; This line waits until an event is received from Windows
  lWindowID = EventWindow() ; The Window where the event is generated, can be used in the gadget procedures
  lGadgetID = EventGadget() ; Is it a gadget event?
  lEventType = EventType() ; The event type
  
  If lEvent = #PB_Event_Gadget
    If lGadgetID = #Combo_ImageType  
    ElseIf lGadgetID = #Combo_ImageQuality 
    ElseIf lGadgetID = #Button_Browse
      ;user can select default file destination here
      sPdfFileName=GetGadgetText(#Text_FileLocation)
      If sPdfFileName=""
        sPdfFileName=SaveFileRequester("Save PDF file",GetHomeDirectory()+"My Documents\","PDF (*.pdf)|*.pdf",0)
        Else
        sPdfFileName=SaveFileRequester("Save PDF file",sPdfFileName,"PDF (*.pdf)|*.pdf",0)
      EndIf
      
      If LCase(GetExtensionPart(sPdfFileName))<> "pdf"
        sPdfFileName + ".pdf"
      EndIf
      SetGadgetText(#Text_FileLocation, sPdfFileName)
    
    ElseIf lGadgetID = #Button_ScanNext
      ;get dpi for page here
      ;color dep[th is in order of index+1
      Select  GetGadgetState(#Combo_ImageQuality)
        Case 0
          lQuality=75
        Case 1
          lQuality=100
        Case 2
          lQuality=150
        Case 3
          lQuality=300
        Case 4
          lQuality=600
        Case 5
          lQuality=1200
        Default
          lQuality=75
      EndSelect
      DisableGadget(#Button_ScanNext, 1)
      DisableGadget(#Button_Finish, 1)
      If hkTWAIN_AquireToPDF(GetGadgetState(#Combo_ImageType) + 1, lQuality, 0)
        ;we have at least one page to save so enable the finish button
        DisableGadget(#Button_Finish, 0)
      EndIf
      DisableGadget(#Button_ScanNext, 0)
    
    ElseIf lGadgetID = #Button_Finish
      ;finalize and save pdf file
      sPdfFileName=GetGadgetText(#Text_FileLocation)
      If sPdfFileName=""
        sPdfFileName.s=SaveFileRequester("Save PDF file",GetHomeDirectory()+"My Documents\","PDF (*.pdf)|*.pdf",0)
        If LCase(GetExtensionPart(sPdfFileName))<> "pdf"
          sPdfFileName+".pdf"
        EndIf
      SetGadgetText(#Text_FileLocation, sPdfFileName)
      EndIf
      sPdfFileName=SaveFileRequester("Save PDF file",sPdfFileName,"PDF (*.pdf)|*.pdf",0)
      If sPdfFileName
        pdf_Save(sPdfFileName)
      EndIf
      If Not hkTWAIN_AquireToPDF(GetGadgetState(#Combo_ImageType) + 1, lQuality, 1)
        MessageRequester("Scan2PDF","Could not reset PDF Engine, please restart application.")
      EndIf
      SetGadgetText(#Button_ScanNext,"Scan Next Page")
      DisableGadget(#Button_Finish, 1)
    
    ElseIf lGadgetID = #Button_Cancel
      lEvent = #PB_Event_CloseWindow
    
    ElseIf lGadgetID = #Button_Select_Source
      TWAIN_SelectImageSource(WindowID(#Window_Scan2PDF))
    
    EndIf
    
  EndIf  
Until lEvent = #PB_Event_CloseWindow ; End of the event loop

;save preferences on exit
If CreatePreferences(sCfgFileName)
  WritePreferenceInteger("Color Index", GetGadgetState(#Combo_ImageType))
  WritePreferenceInteger("Quality Index", GetGadgetState(#Combo_ImageQuality))
  WritePreferenceString("Destination File", GetGadgetText(#Text_FileLocation))
  ClosePreferences()
  Else
  MessageRequester("Scan2PDF", "Could not save program preferences file " + sCfgFileName)
EndIf

End
I'm not sure if the EZTwain.pbi file changed much but here's the last version of that.

Code: Select all

EnableExplicit

; XDefs translation of \EZTwain\VC\eztwain.h
;-----------------------------------------------------------------
; EZTWAIN.H - interface to Easy TWAIN library
; (DLL=eztw32.dll)
;
; 1.15     2006.05.09 Fix: If user closed the scan dialog during an Acquire,
;                     the last DIB handle, if any, was returned!
;                     Added VB\Eztwain.bas to package.
; 1.14     2004.08.06 Set XFERMECH=NATIVE as soon as DS is opened.
;                     trying to deal with scanners that default to memory xfer.
; 1.13     1999.09.08 Documented correct return codes of AcquireToFilename.
;                     - No code changes -
; 1.12     1998.09.14 Added Fix32ToFloat, allow MSG_OPENDS triplet.
;                     Added SetXferMech, XferMech.
; 1.11     1998.08.17 Added ToFix32, SetContrast, SetBrightness.
;                     Modified TWAIN_ToFix32 to round away-from-zero.
; 1.09beta 1998.07.27 Reverted from 1.08 to 1.06 and worked forward again.
; 1.06     1997.08.21 correction to message hook, fixed 32-bit exports
; 1.05     1996.11.06 32-bit conversion
; 1.04     1995.05.03 added: WriteNativeToFile, WriteNativeToFilename,
;                         FreeNative, SetHideUI, GetHideUI, SetCurrentUnits,
;                         GetCurrentUnits, SetCurrentResolution, SetBitDepth,
;                         SetCurrentPixelType, SetCapOneValue.
; 1.0a      1994.06.23 first alpha version
; 0.0      1994.05.11 created
;
; EZTWAIN 1.x is not a product, and is not the work of any company involved
; in promoting or using the TWAIN standard.  This code is sample code,
; provided without charge, and you use it entirely at your own risk.
; No rights or ownership is claimed by the author, or by any company
; or organization.  There are no restrictions on use or (re)distribution.
;
; Download from:    www.dosadi.com
;
; Support contact:  support@dosadi.com
;

; PB include file for EZTwain
; Ported from VB source by Harkon
;  provided by the good people at EZTwain www.dosadi.com

; Use these values for wPixTypes
#TWAIN_BW=1
#TWAIN_GRAY=2
#TWAIN_RGB=4
#TWAIN_PALETTE=8
#TWAIN_ANYTYPE=0

;Use these for TWAIN_State
#TWAIN_PRESESSION = 1
#TWAIN_SM_LOADED = 2
#TWAIN_SM_OPEN = 3
#TWAIN_SOURCE_OPEN = 4
#TWAIN_SOURCE_ENABLED = 5
#TWAIN_TRANSFER_READY = 6
#TWAIN_TRANSFERRING = 7

;use these for Xtransfer Mode
#TWAIN_XFERMECH_NATIVE = 0
#TWAIN_XFERMECH_FILE = 1
#TWAIN_XFERMECH_MEMORY = 2  

Import "EZTW32.LIB"

  TWAIN_AcquireNative.l(hwndApp.l, wPixTypes.l) As "_TWAIN_AcquireNative@8"
    ; The minimal use of EZTWAIN.DLL is to just call this routine, with 0 for
    ; both params.  EZTWAIN creates a window if hwndApp is 0.
    ; 
    ; Acquires a single image, from the currently selected Data Source, using
    ; Native-mode transfer. It waits until the source closes (if it's modal) Or
    ; forces the source closed if not.  The return value is a handle to the
    ; acquired image.  Only one image can be acquired per call.
    ; 
    ; Under Windows, the return value is a global memory handle - applying
    ; GlobalLock to it will return a (huge) pointer to the DIB, which
    ; starts with a BITMAPINFOHEADER.
    ; NOTE: You are responsible for disposing of the returned DIB - these things
    ; can eat up your Windows memory fast!  See TWAIN_FreeNative below.
    ; 
    ; The image type can be restricted using the following masks.  A mask of 0
    ; means 'any pixel type is welcome'.
    ; Caution: You should not assume that the source will honor a pixel type
    ; restriction!  If you care, check the parameters of the DIB.  
    
  TWAIN_FreeNative(hdib.l) As "_TWAIN_FreeNative@4"
    ; Release the memory allocated to a native format image, as returned by
    ; TWAIN_AcquireNative. (If you are coding in C or C++, this is just a call
    ; to GlobalFree.)
    ; If you use TWAIN_AcquireNative and don't free the returned image handle,
    ; it stays around taking up Windows (virtual) memory until your application
    ; terminates.  Memory required per square inch:
    ;             1 bit B&W       8-bit grayscale     24-bit color
    ; 100 dpi      1.25KB              10KB               30KB
    ; 200 dpi        5KB               40KB              120KB
    ; 300 dpi      11.25KB             90KB              270KB
    ; 400 dpi       20KB              160KB              480KB
    ; 
    
  TWAIN_AcquireToClipboard.l(hwndApp.l, wPixTypes.l) As "_TWAIN_AcquireToClipboard@8"
    ; Like AcquireNative, but puts the resulting image, if any, into the system
    ; clipboard.  Under Windows, this will put a CF_DIB item in the clipboard
    ; if successful.  If this call fails, the clipboard is either empty or
    ; contains the old contents.
    ; A return value of 1 indicates success, 0 indicates failure.
    ; 
    ; Useful for environments like Visual Basic where it is hard to make direct
    ; use of a DIB handle.  In fact, TWAIN_AcquireToClipboard uses
    ; TWAIN_AcquireNative for all the hard work.  

  TWAIN_AquireToFilename.l(hwndApp.l, sFile.s) As "_TWAIN_AcquireToFilename@8"
    ; Acquire an image and write it to a .BMP (Windows Bitmap) file.
    ; The file name and path in pszFile are used.  If pszFile is NULL or
    ; points to an empty string, the user is prompted with a Save File dialog.
    ; Return values:
    ; 0 success
    ; -1 Acquire failed OR user cancelled File Save dialog
    ; -2 file open error (invalid path or name, or access denied)
    ; -3 (weird) unable to lock DIB - probably an invalid handle.
    ; -4 writing BMP data failed, possibly output device is full  
  
  TWAIN_SelectImageSource.l(hwnd.l) As "_TWAIN_SelectImageSource@4"
    ; This is the routine to call when the user chooses the "Select Source..."
    ; menu command from your application's File menu.  Your app has one of
    ; these, right?  The TWAIN spec calls for this feature to be available in
    ; your user interface, preferably as described.
    ; Note: If only one TWAIN device is installed on a system, it is selected
    ; automatically, so there is no need for the user to do Select Source.
    ; You should not require your users to do Select Source before Acquire.
    ; '
    ; This function posts the Source Manager's Select Source dialog box.
    ; It returns after the user either OK's Or CANCEL's that dialog.
    ; A return of 1 indicates OK, 0 indicates one of the following:
    ;   a) The user cancelled the dialog
    ;   b) The Source Manager found no data sources installed
    ;   c) There was a failure before the Select Source dialog could be posted
    ; -- details --
    ; Only sources that can return images (that are in the DG_IMAGE group) are
    ; displayed.  The current default source will be highlighted initially.
    ; In the standard implementation of "Select Source...", your application
    ; doesn't need To do anything except make this one call.
    ; '
    ; If you want to be meticulous, disable your "Acquire" and "Select Source"
    ; menu items or buttons if TWAIN_IsAvailable() returns 0 - see below.  

 ; --- TWAIN Basic Inquiries ---
  TWAIN_IsAvailable.l() As "_TWAIN_IsAvailable@0"
    ; Call this function any time to find out if TWAIN is installed on the
    ; system.  It takes a little time on the first call, after that it's fast,
    ; just testing a flag.  It returns 1 if the TWAIN Source Manager is
    ; installed & can be loaded, 0 otherwise.
 
  TWAIN_EasyVersion.l() As "_TWAIN_EasyVersion@0"
    ; Returns the version number of EZTWAIN.DLL, multiplied by 100.
    ; So e.g. version 2.01 will return 201 from this call.
  
  TWAIN_State.l() As "_TWAIN_State@0"
    ;Returns the TWAIN Protocol State per the spec.
  
 ; --- DIB Handling ___ 
  TWAIN_DibDepth.l(hdib.l) As "_TWAIN_DibDepth@4"
    ; Depth of DIB, in bits i.e. bits per pixel.
  TWAIN_DibWidth.l(hdib.l) As "_TWAIN_DibWidth@4"
    ; Width of DIB, in pixels (columns)
  TWAIN_DibHeight.l(hdib.l) As "_TWAIN_DibHeight@4"
    ; Height of DIB, in lines (rows)
  TWAIN_DibNumColors.l(hdib) As "_TWAIN_DibNumColors@4"
    ; Number of colors in color table of DIB
  DIB_RowBytes.l(hdib.l) As "_DIB_RowBytes@4"
    ; Number of bytes consumed by one row
  DIB_ReadRow(hdib.l, nRow.l, *prow) As "_DIB_ReadRow@12"
      ; Read row n of the given DIB into buffer at prow.
      ; Caller is responsible for ensuring buffer is large enough.
      ; Row 0 is the *top* row of the image, as it would be displayed.
  TWAIN_CreateDibPalette.l(hdib.l) As "_TWAIN_CreateDibPalette@4"
      ; Create and return a logical palette to be used for drawing the DIB.
      ; For 1, 4, and 8-bit DIBs the palette contains the DIB color table.
      ; For 24-bit DIBs, a default halftone palette is returned.
  TWAIN_DrawDibToDC(hDC.l, dx.l, dy.l, w.l, h.l, hdib.l, sx.l, sy.l) As "_TWAIN_DrawDibToDC@32"
      ; Draws a DIB on a device context.
      ; You should call CreateDibPalette, select that palette
      ; into the DC, and do a RealizePalette(hDC) first.
  
  ; ---  BMP File Utilities ---
  TWAIN_WriteNativeToFilename.l(hdib.l, sFile.s) As "_TWAIN_WriteNativeToFilename@8"
      ; Writes a DIB handle to a .BMP file
      ; 
      ; hdib      = DIB handle, as returned by TWAIN_AcquireNative
      ; pszFile   = far pointer to NUL-terminated filename
      ; If pszFile is NULL or points to a null string, prompts the user
      ; for the filename with a standard file-save dialog.
      ; 
      ; Return values:
      ;    0  success
      ;   -1  user cancelled File Save dialog
      ;   -2  file open error (invalid path or name, or access denied)
      ;   -3  (weird) unable to lock DIB - probably an invalid handle.
      ;   -4  writing BMP data failed, possibly output device is full  
   TWAIN_WriteNativeToFile.l(hdib.l, fh.l) As "_TWAIN_WriteNativeToFile@8"
      ; Writes a DIB to a file in .BMP format.
      ; 
      ; hdib      = DIB handle, as returned by TWAIN_AcquireNative
      ; fh        = file handle, as returned by _open, _lopen or OpenFile
      ; 
      ; Return value as for TWAIN_WriteNativeToFilename
   TWAIN_LoadNativeFromFilename.l(sFile.s) As "_TWAIN_LoadNativeFromFilename@4"
      ; Load a .BMP file and return a DIB handle (as from AcquireNative.)
      ; Accepts a filename (including path & extension).
      ; If pszFile is NULL or points to a null string, the user is prompted.
      ; Returns a DIB handle if successful, otherwise NULL.
  TWAIN_LoadNativeFromFile.l(fh.l) As "_TWAIN_LoadNativeFromFile@4"
      ; Like LoadNativeFromFilename, but takes an already open file handle.  
  TWAIN_SetHideUI(fHide.l) As "_TWAIN_SetHideUI@4"
  TWAIN_GetHideUI.l() As "_TWAIN_GetHideUI@0"
      ; These functions control the 'hide source user Interface' flag.
      ; This flag is cleared initially, but if you set it non-zero, then when
      ; a source is enabled it will be asked to hide its user interface.
      ; Note that this is only a request - some sources will ignore it!
      ; This affects AcquireNative, AcquireToClipboard, and EnableSource.
      ; If the user interface is hidden, you will probably want to set at least
      ; some of the basic acquisition parameters yourself - see
      ; SetCurrentUnits, SetBitDepth, SetCurrentPixelType and
      ; SetCurrentResolution below.
      
  ; --- Appliucation Registration ---
  TWAIN_RegisterApp(nMajorNum.l, nMinorNum.l, nLanguage.l, nCountry.l, lpszVersion.s, lpszMfg.s, lpszFamily.s, lpszProduct.s) As "_TWAIN_RegisterApp@32"
    ; TWAIN_RegisterApp can be called *AS THE FIRST CALL*, to register the
    ; application. If this function is not called, the application is given a
    ; 'generic' registration by EZTWAIN.
    ; Registration only provides this information to the Source Manager and any
    ; sources you may open - it is used for debugging, and (frankly) by some
    ; sources to give special treatment to certain applications.

  ; --- Error Analysis and Reporting ---
  TWAIN_GetResultCode.l() As "_TWAIN_GetResultCode@0"
    ; Return the result code (TWRC_xxx) from the last triplet sent to TWAIN
  TWAIN_GetConditionCode.l() As "_TWAIN_GetConditionCode@0"
    ; Return the condition code from the last triplet sent to TWAIN.
    ; (To be precise, from the last call to TWAIN_DS below)
  TWAIN_ErrorBox(sMsg.s) As "_TWAIN_ErrorBox@4"
    ; Post an error message dialog with an exclamation mark, OK button,
    ; and the title 'TWAIN Error'.
    ; pszMsg points to a null-terminated message string.
  TWAIN_ReportLastError(sMsg.s) As "_TWAIN_ReportLastError@4"
    ; Like TWAIN_ErrorBox, but if some details are available from
    ; TWAIN about the last failure, they are included in the message box.  
  
  ; --- TWAIN State Control ---
  TWAIN_LoadSourceManager.l() As "_TWAIN_LoadSourceManager@0"
    ; Finds and loads the Data Source Manager, TWAIN.DLL.
    ; If Source Manager is already loaded, does nothing and returns TRUE.
    ; This can fail if TWAIN.DLL is not installed (in the right place), or
    ; if the library cannot load for some reason (insufficient memory?) or
    ; if TWAIN.DLL has been corrupted.  
  TWAIN_OpenSourceManager.l(hwnd.l) As "_TWAIN_OpenSourceManager@4"
    ; Opens the Data Source Manager, if not already open.
    ; If the Source Manager is already open, does nothing and returns TRUE.
    ; This call will fail if the Source Manager is not loaded.  
  TWAIN_OpenDefaultSource.l() As "_TWAIN_OpenDefaultSource@0"
    ; This opens the source selected in the Select Source dialog.
    ; If a source is already open, does nothing and returns TRUE.
    ; Fails if the source manager is not loaded and open.
  TWAIN_EnableSource.l(hwnd.l) As "_TWAIN_EnableSource@4"
    ; Enables the open Data Source. This posts the source's user Interface
    ; And allows image acquisition To begin.  If the source is already enabled,
    ; this call does nothing And returns TRUE.
  TWAIN_DisableSource .l() As "_TWAIN_DisableSource@0"
    ; Disables the open Data Source, if any.
    ; This closes the source's user Interface.
    ; If there is Not an enabled source, does nothing And returns TRUE.
  TWAIN_CloseSource.l() As "_TWAIN_CloseSource@0"
    ; Closes the open Data Source, if any.
    ; If the source is enabled, disables it first.
    ; If there is Not an open source, does nothing And returns TRUE.
  TWAIN_CloseSourceManager.l(hwnd.l) As "_TWAIN_CloseSourceManager@4"
    ; Closes the Data Source Manager, If it is open.
    ; If a source is open, disables And closes it As needed.
    ; If the Source Manager is Not open, does nothing And returns TRUE.
  TWAIN_UnloadSourceManager.l() As "_TWAIN_UnloadSourceManager@0"
    ; Unloads the Data Source Manager i.e. TWAIN.DLL - releasing
    ; any associated memory Or resources.
    ; This call will fail If the Source Manager is open, otherwise
    ; it always succeeds And returns TRUE.
  TWAIN_WaitForNativeXfer.l(hwnd.l) As "_TWAIN_WaitForNativeXfer@4"
  TWAIN_ModalEventLoop() As "_TWAIN_ModalEventLoop@0"
    ; Process messages Until termination, source disable, Or image transfer.
  TWAIN_EndXfer.l() As "_TWAIN_EndXfer@0"
  TWAIN_AbortAllPendingXfers.l() As "_TWAIN_AbortAllPendingXfers@0"
  
  
  ; --- High-level Capability Negotiation Functions ---
  TWAIN_NegotiateXferCount.l(nXfers.l) As "_TWAIN_NegotiateXferCount@4"
    ; Negotiate With open Source the number of images application will accept.
    ; This is only allowed in State 4 (TWAIN_SOURCE_OPEN)
    ; nXfers = -1 means any number
  TWAIN_NegotiatePixelTypes.l(wPixTypes.l) As "_TWAIN_NegotiatePixelTypes@4"
    ; Negotiate with the source to restrict pixel types that can be acquired.
    ; This tries To restrict the source To a *set* of pixel types,
    ; See TWAIN_AcquireNative above For some mask constants.
    ; --> This is only allowed in State 4 (TWAIN_SOURCE_OPEN)
    ; A parameter of 0 (TWAIN_ANYTYPE) causes no negotiation & no restriction.
    ; You should Not assume that the source will honor your restrictions, even
    ; If this call succeeds!
  TWAIN_GetCurrentUnits.l() As "_TWAIN_GetCurrentUnits@0"
    ; Ask the source what its current unit of measure is.
    ; If anything goes wrong, this function just returns TWUN_INCHES (0).
  TWAIN_SetCurrentUnits.l(nUnits.l) As "_TWAIN_SetCurrentUnits@4"
    ; Set the current unit of measure for the source.
    ; Unit of measure codes are in TWAIN.H, but TWUN_INCHES is 0.
  TWAIN_GetBitDepth.l() As "_TWAIN_GetBitDepth@0"
    ; Get the current bitdepth, which can depend on the current PixelType.
    ; Bit depth is per color channel e.g. 24-bit RGB has bit depth 8.
    ; If anything goes wrong, this function returns 0.
  TWAIN_SetBitDepth.l(nBits.l) As "_TWAIN_SetBitDepth@4"
    ; (Try to) set the current bitdepth (for the current pixel type).
  TWAIN_GetPixelType.l() As "_TWAIN_GetPixelType@0"
    ; Ask the source For the current pixel type.
    ; If anything goes wrong (it shouldn't), this function returns 0 (TWPT_BW).
  TWAIN_SetCurrentPixelType.l(nPixType.l) As "_TWAIN_SetCurrentPixelType@4"
    ; (Try To) set the current pixel type For acquisition.
    ; This is only allowed in State 4 (TWAIN_SOURCE_OPEN)
    ; The source may Select this pixel type, but don't assume it will.
  TWAIN_GetCurrentResolution.d() As "_TWAIN_GetCurrentResolution@0"
    ; Ask the source For the current (horizontal) resolution.
    ; Resolution is in dots per current unit! (See TWAIN_GetCurrentUnits above)
    ; If anything goes wrong (it shouldn't) this function returns 0.0
  TWAIN_GetYResolution.d() As "_TWAIN_GetYResolution@0"
    ; Returns the current vertical resolution, in dots per *current unit*.
    ; In the event of failure, returns 0.0.
  TWAIN_SetCurrentResolution.l(dRes.d) As "_TWAIN_SetCurrentResolution@8"
    ; (Try To) set the current resolution For acquisition.
    ; Resolution is in dots per current unit! (See TWAIN_GetCurrentUnits above)
    ; This is only allowed in State 4 (TWAIN_SOURCE_OPEN)
    ; Note: The source may Select this resolution, but don't assume it will.
  TWAIN_SetContrast.l(dCon.d) As "_TWAIN_SetContrast@8"
    ; (Try To) set the current contrast For acquisition.
    ; The TWAIN standard says that the range For this cap is -1000 ... +1000
  TWAIN_SetBrightness.l(dBri.d) As "_TWAIN_SetBrightness@8"
    ; (Try To) set the current brightness For acquisition.
    ; The TWAIN standard says that the range For this cap is -1000 ... +1000
  TWAIN_SetXferMech.l(mech.l) As "_TWAIN_SetXferMech@4"
  TWAIN_XferMech.l() As "_TWAIN_XferMech@0"
    ; (Try To) set Or get the transfer mode - one of the following:
    ; #TWAIN_XFERMECH_NATIVE = 0
    ; #TWAIN_XFERMECH_FILE = 1
    ; #TWAIN_XFERMECH_MEMORY = 2  

  ; --- Low-level Capability Negotiation Functions ---
    ; Setting a capability is valid only in State 4 (TWAIN_SOURCE_OPEN)
    ; Getting a capability is valid in State 4 Or any higher state.
  TWAIN_SetCapOneValue.l(Cap.l, ItemType.l, ItemVal.l) As "_TWAIN_SetCapOneValue@12"
    ; Do a DAT_CAPABILITY/MSG_SET, on capability 'Cap' (e.g. ICAP_PIXELTYPE,
    ; CAP_AUTOFEED, etc.) using a TW_ONEVALUE container With the given item type
    ; And value.  The item value must fit into 32 bits.
    ; Returns TRUE (1) If successful, FALSE (0) otherwise.
  TWAIN_GetCapCurrent.l(Cap.l, ItemType.l, *pVal) As "_TWAIN_GetCapCurrent@12"
    ; Do a DAT_CAPABILITY/MSG_GETCURRENT on capability 'Cap'.
    ; Copy the current value out of the returned container into *pVal.
    ; If the operation fails (the source refuses the request), Or If the
    ; container is Not a ONEVALUE Or Enumeration, Or If the item type of the
    ; returned container is incompatible With the expected TWTY_ type in nType,
    ; returns FALSE.  If this function returns FALSE, *pVal is Not touched.
  TWAIN_ToFix32.l(d.d) As "_TWAIN_ToFix32@8"
    ; Convert a floating-point value To a 32-bit TW_FIX32 value that can be passed
    ; To e.g. TWAIN_SetCapOneValue
  TWAIN_Fix32ToFloat.d(nfix.l) As "_TWAIN_Fix32ToFloat@4"
    ; Convert a TW_FIX32 value (As returned from some capability inquiries)
    ; To a double (floating point) value.  
  
  ; --- Lowest-level functions For TWAIN protocol ---


  TWAIN_DS.l(DG.l, DAT.l, MSG.l, *pData) As "_TWAIN_DS@16"
    ; Passes the triplet (DG, DAT, MSG, pData) To the open Data source If any.
    ; Returns 1 (TRUE) If the result code is TWRC_SUCCESS, 0 (FALSE) otherwise.
    ; The last result code can be retrieved With TWAIN_GetResultCode(), And the corresponding
    ; condition code can be retrieved With TWAIN_GetConditionCode().
    ; If no source is open this call will fail, result code TWRC_FAILURE, condition code TWCC_NODS.

  TWAIN_Mgr.l(DG.l, DAT.l, MSG.l, *pData) As "_TWAIN_Mgr@16"
    ; Passes a triplet To the Data Source Manager (DSM).
    ; Returns 1 (TRUE) If the result code is TWRC_SUCCESS, 0 (FALSE) otherwise.
    ; The last result code can be retrieved With TWAIN_GetResultCode(), And the corresponding
    ; condition code can be retrieved With TWAIN_GetConditionCode().
    ; If the Source Manager is Not open, this call will fail, And set the result code To TWRC_FAILURE,
    ; With a condition code of TWCC_SEQERROR (triplet out of sequence).
  
EndImport

UseJPEGImageEncoder()
UsePNGImageEncoder()

Procedure.l hkTWAIN_WriteNativeToPNG(hDib.l, sPNGFileName.s)
  Define lpDib.l
  Define StructSize.l
  Define Width.l 
  Define Height.l
  Define Planes.w
  Define BitCount.w
  Define Compression.l
  Define SizeImage.l
  Define XPix_Meter.l
  Define YPix_Meter.l
  Define ColorsUsed.l
  Define ColorsImportant.l
  Define PalletteSize.l
  Define BMPOffset.l
  Define BMPSize.l
  Define ImageNumber.l
  Define *BMPBuffer
  
  lpDib=GlobalLock_(hDib)
  
  StructSize=PeekL(lpDib)
  Width=PeekL(lpDib+4)
  Height=PeekL(lpDib+8)
  Planes=PeekW(lpDib+12)
  BitCount=PeekW(lpDib+14)
  Compression=PeekL(lpDib+16)
  SizeImage=PeekL(lpDib+20)
  XPix_Meter=PeekL(lpDib+24)
  YPix_Meter=PeekL(lpDib+28)
  ColorsUsed=PeekL(lpDib+32)
  ColorsImportant=PeekL(lpDib+36)
 
  ;make BMP for CatchImage
  If BitCount<24
    If ColorsUsed=0 And BitCount<=8
      PalletteSize=(1<<BitCount)*4  ;4 is the size og the RGBQUAD data type (makes total in bytes

    EndIf
  EndIf
  ;BMPFileHeader is
  ;"BM",BMPSize.l,Reserved.w, Reserved.w, Offset.l where bmp data starts
  ; the offset is after the pallette info
  ; pallette info is RGB+1 byte for each color in the pallette
  ; unless it's 24bit then there's no pallette
  BMPOffset=14+StructSize+PalletteSize
  BMPSize=BMPOffset+SizeImage
  
  *BMPBuffer=AllocateMemory(BMPSize)
  PokeS(*BMPBuffer,"BM")
  PokeL(*BMPBuffer+2,BMPSize)
  PokeL(*BMPBuffer+10,BMPOffset)
  CopyMemory(lpDib, *BMPBuffer+14, BMPSize-14)
  
  GlobalUnlock_(hDib)
  
  ImageNumber=CatchImage(#PB_Any,*BMPBuffer,BMPSize)
  
  If sPNGFileName=""
    sPNGFileName=SaveFileRequester("Save PNG file",GetHomeDirectory()+"My Documents\","PNG (*.png)|*.png",0)
  EndIf

  ;Just write BMP
  CreateFile(0,"temp.bmp")
  WriteData(0,*BMPBuffer,BMPSize)
  CloseFile(0)
  ProcedureReturn SaveImage(ImageNumber,sPNGFileName,#PB_ImagePlugin_PNG)
  FreeImage(ImageNumber)
  FreeMemory(*BMPBuffer)
  

EndProcedure

Procedure.l hkTWAIN_WriteNativeToJPG(hDib.l, sJPGFileName.s, lQuality.l)
  Define lpDib.l
  Define StructSize.l
  Define Width.l 
  Define Height.l
  Define Planes.w
  Define BitCount.w
  Define Compression.l
  Define SizeImage.l
  Define XPix_Meter.l
  Define YPix_Meter.l
  Define ColorsUsed.l
  Define ColorsImportant.l
  Define PalletteSize.l
  Define BMPOffset.l
  Define BMPSize.l
  Define ImageNumber.l
  Define *BMPBuffer

  lpDib=GlobalLock_(hDib)
  
  StructSize=PeekL(lpDib)
  Width=PeekL(lpDib+4)
  Height=PeekL(lpDib+8)
  Planes=PeekW(lpDib+12)
  BitCount=PeekW(lpDib+14)
  Compression=PeekL(lpDib+16)
  SizeImage=PeekL(lpDib+20)
  XPix_Meter=PeekL(lpDib+24)
  YPix_Meter=PeekL(lpDib+28)
  ColorsUsed=PeekL(lpDib+32)
  ColorsImportant=PeekL(lpDib+36)
 
  ;make BMP for CatchImage
  If BitCount<24
    If ColorsUsed=0 And BitCount<=8
      PalletteSize=(1<<BitCount)*4  ;4 is the size og the RGBQUAD data type (makes total in bytes
    EndIf
  EndIf
  ;BMPFileHeader is
  ;"BM",BMPSize.l,Reserved.w, Reserved.w, Offset.l where bmp data starts
  ; the offset is after the pallette info
  ; pallette info is RGB+1 byte for each color in the pallette
  ; unless it's 24bit then there's no pallette
  BMPOffset=14+StructSize+PalletteSize
  BMPSize=BMPOffset+SizeImage
  
  *BMPBuffer=AllocateMemory(BMPSize)
  PokeS(*BMPBuffer,"BM")
  PokeL(*BMPBuffer+2,BMPSize)
  PokeL(*BMPBuffer+10,BMPOffset)
  CopyMemory(lpDib, *BMPBuffer+14, BMPSize-14)
  
  GlobalUnlock_(hDib)
  
  ImageNumber=CatchImage(#PB_Any,*BMPBuffer,BMPSize)
  
  If sJPGFileName=""
    sJPGFileName.s=SaveFileRequester("Save JPG file",GetHomeDirectory()+"My Documents\","JPG (*.jpg)|*.jpg",0)
  EndIf
  
  ;lQuality is spcified in terms of a number between 1 and 10, 10 being the best quality lowest compression
  ; if not specified and the quality is 0 or invalid, we will default to 7
  If lQuality<1 Or lQuality>10
    lQuality=7
  EndIf  
  ProcedureReturn SaveImage(ImageNumber,sJPGFileName,#PB_ImagePlugin_JPEG, lQuality)
  FreeImage(ImageNumber)
  FreeMemory(*BMPBuffer)
  
EndProcedure

DisableExplicit
I hope this will work a bit better for you.
Oh, and don't forget this was all done with PB 4.41 so you may have to update some of the code to compile properly
*EDIT*
Just compiled with 4.60 and it compiles fine using v2.2 of PurePDF.

Sorry I just keep adding stuff, but I also added 2 bits of code to register the app for TWAIN events so that the app shows up in control panel.

WinReg as TWAIN app

Code: Select all

Procedure.s GetValue(topKey.l, sKeyName.s, sValueName.s, ComputerName.s) 
   GetHandle.l 
   hKey.l 
   lpData.s 
   lpDataDWORD.l 
   lpcbData.l 
   lType.l 
   lReturnCode.l 
   lhRemoteRegistry.l 
   Shared GetValue.s 
    
   If Left(sKeyName, 1) = "\" 
       sKeyName = Right(sKeyName, Len(sKeyName) - 1) 
   EndIf 
    
   If ComputerName = "" 
       GetHandle = RegOpenKeyEx_(topKey, sKeyName, 0, #KEY_ALL_ACCESS, @hKey) 
   Else 
       lReturnCode = RegConnectRegistry_(ComputerName, topKey, @lhRemoteRegistry) 
       GetHandle = RegOpenKeyEx_(lhRemoteRegistry, sKeyName, 0, #KEY_ALL_ACCESS, @hKey) 
   EndIf 
            
   If GetHandle = #ERROR_SUCCESS 
       lpcbData = 255 
       lpData = Space(255) 
        
       GetHandle = RegQueryValueEx_(hKey, sValueName, 0, @lType, @lpData, @lpcbData) 
            
       If GetHandle = #ERROR_SUCCESS 
           Select lType 
               Case #REG_SZ 
                   GetHandle = RegQueryValueEx_(hKey, sValueName, 0, @lType, @lpData, @lpcbData) 
                
                   If GetHandle = 0 
                       GetValue = Left(lpData, lpcbData - 1) 
                   Else 
                       GetValue = "" 
                   EndIf 
                    
               Case #REG_DWORD 
                   GetHandle = RegQueryValueEx_(hKey, sValueName, 0, @lpType, @lpDataDWORD, @lpcbData) 
                    
                   If GetHandle = 0 
                       GetValue = Str(lpDataDWORD) 
                   Else 
                       GetValue = "0" 
                   EndIf 
                
           EndSelect 
       EndIf 
   EndIf 
   RegCloseKey_(hKey) 
   ProcedureReturn GetValue
EndProcedure 

Procedure.l SetValue(topKey.l, sKeyName.s, sValueName.s, vValue.s, lType.l, ComputerName.s) 
  GetHandle.l 
  hKey.l 
  lType.l 
  lpcbData.l 
  lpData.s 
  lReturnCode.l 
  lhRemoteRegistry.l 
  
  If Left(sKeyName, 1) = "\" 
    sKeyName = Right(sKeyName, Len(sKeyName) - 1) 
  EndIf 
  
  If ComputerName = "" 
    GetHandle = RegOpenKeyEx_(topKey, sKeyName, 0, #KEY_ALL_ACCESS, @hKey) 
  Else 
    lReturnCode = RegConnectRegistry_(ComputerName, topKey, @lhRemoteRegistry) 
    GetHandle = RegOpenKeyEx_(lhRemoteRegistry, sKeyName, 0, #KEY_ALL_ACCESS, @hKey) 
  EndIf 

  If GetHandle = #ERROR_SUCCESS 
    lpcbData = 255 
    lpData = Space(255) 
      
    Select lType 
      Case #REG_SZ 
        GetHandle = RegSetValueEx_(hkey, sValueName, 0, #REG_SZ, @vValue, Len(vValue) + 1) 
      Case #REG_DWORD 
        lValue = Val(vValue) 
        GetHandle = RegSetValueEx_(hKey, sValueName, 0, #REG_DWORD, @lValue, 4) 
    EndSelect 
      
    RegCloseKey_(hkey) 
    ergebnis = 1 
    ProcedureReturn ergebnis 
  Else 
    MessageRequester("Registry Error", "There was an error in writing a value to the registry!", 0) 
    RegCloseKey_(hKey) 
    ergebnis  = 0 
    ProcedureReturn ergebnis 
  EndIf 
EndProcedure 

topkey.l=#HKEY_LOCAL_MACHINE
sKeyName.s="SOFTWARE\Microsoft\Windows\CurrentVersion\StillImage\Registered Applications"
sValueName.s="Scan2PDF"
sComputerName.s=""

sKeyVal.s=GetPathPart(ProgramFilename()) + "Scan2PDF.exe  /StiDevice:%1 /StiEvent:%2"


If GetValue(topKey, sKeyName, sValueName, sComputerName) = sKeyVal
  ;already done
  MessageRequester("Scan2PDF", "Scan2PDF is already registered as a TWAIN target")
  ElseIf SetValue(topKey, sKeyName, sValueName, sKeyVal, #REG_SZ, sComputerName)
    MessageRequester("Scan2PDF", "Scan2PDF is now registered as a TWAIN target")
EndIf
  
WinUnreg as TWAIN app

Code: Select all

Procedure.l DeleteValue(topKey.l, sKeyName.s, sValueName.s, ComputerName.s) 
    GetHandle.l 
    hKey.l 
    lReturnCode.l 
    lhRemoteRegistry.l 
    
    If Left(sKeyName, 1) = "\" 
        sKeyName = Right(sKeyName, Len(sKeyName) - 1) 
    EndIf 
    
    If ComputerName = "" 
        GetHandle = RegOpenKeyEx_(topKey, sKeyName, 0, #KEY_ALL_ACCESS, @hKey) 
    Else 
        lReturnCode = RegConnectRegistry_(ComputerName, topKey, @lhRemoteRegistry) 
        GetHandle = RegOpenKeyEx_(lhRemoteRegistry, sKeyName, 0, #KEY_ALL_ACCESS, @hKey) 
    EndIf 

    If GetHandle = #ERROR_SUCCESS 
        GetHandle = RegDeleteValue_(hKey, @sValueName) 
        If GetHandle = #ERROR_SUCCESS 
            DeleteValue = #True 
        Else 
            DeleteValue = #False 
        EndIf 
    EndIf 
    RegCloseKey_(hKey) 
    ProcedureReturn DeleteValue 
EndProcedure 

topkey.l=#HKEY_LOCAL_MACHINE
sKeyName.s="SOFTWARE\Microsoft\Windows\CurrentVersion\StillImage\Registered Applications"
sValueName.s="Scan2PDF"
sComputerName.s=""

If DeleteValue(topKey, sKeyName, sValueName, sComputerName)
  MessageRequester("Scan2PDF", "Scan2PDF is now no longer registered as a TWAIN target")
  Else
    MessageRequester("Scan2PDF","Scan2PDF was not registered, no action taken")
EndIf
Missed it by that much!!
HK
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4747
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: Scan2PDF

Post by Fangbeast »

This just keeps getting better and better, thanks Harkon. And you too Num3.

I have to learn more but also have to go out shopping damn it.

Hoping I can do multi-page PDF eventually and one day, scan line by line into an image gadget so my wife can see the scan happening. Program is only for the family after all:):)
Amateur Radio, D-STAR/VK3HAF
harkon
Enthusiast
Enthusiast
Posts: 217
Joined: Wed Nov 23, 2005 5:48 pm

Re: Scan2PDF

Post by harkon »

Not a problem. It's been a few years since I've had to work with this as it works well for me as is. As it goes, many of the details are forgotten. I still use the application almost every day. Once this machine fails and I have to move up to Windows 7 I may have to rewrite, but as far as available time goes, I'll have to cross that bridge another time.
Missed it by that much!!
HK
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4747
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: Scan2PDF

Post by Fangbeast »

harkon wrote:Not a problem. It's been a few years since I've had to work with this as it works well for me as is. As it goes, many of the details are forgotten. I still use the application almost every day. Once this machine fails and I have to move up to Windows 7 I may have to rewrite, but as far as available time goes, I'll have to cross that bridge another time.
Yahooey!! I'm on windows 7 and have been playing with this code for 4 days or so. Using your code as a base and InfraTec's smart proportional image resizing to see the scanned image after it's done, I have a nice little proof-of-concept working.

As I get older, I find that it takes longer and longer to get things done or understood but I get there.

Thanks for your great help and examples. Anyone who wants my fangled up version, yell.
Amateur Radio, D-STAR/VK3HAF
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4747
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: Scan2PDF

Post by Fangbeast »

This is my version of Scan2PDF with my local variables and preview strip etc. All works great. until I add the code-as-is to my docman manager and they it crashes in module hkTWAIN_WriteNativeToPNG(hDib.l, sPNGFileName.s) at SaveImage(ImageNumber, sPNGFileName, #PB_ImagePlugin_PNG) saying that ImageNumber isn't initialised when it is.

The code below works and is EXACTLY the same in my document manager, it is called UNCHANGED yet it crashes at that line. ARRGHGHGHGH!

Code: Select all

; Copyrights, disclaimers etc
; 
; Most of the code is based on Scan2PDF by Harkon and originally written under PureBasic 4.41 and requires the following
; files below from the links he provided. EZTwain.pbi is written by Harkon to be an interface to PurePDF. Slightly
; modified by me to use my directory variables.
; 
; EZTW32.dll, EZTW32.LIB
; both are available for download at http://www.dosadi.com/ as EZTwain Classic (this is a free download)
;
; PurePDF by ABBKlaus (appropriate version at http://www.purebasicpower.de/?PurePDF)
; 
; The ShowScan() routine was based entirely on Infratec's smart proportional image resizing as I wanted to see that the
; images were being correctly scanned.
; 
; Everything is tested to be working under PB4.60
; 
; Need a unicode aware version of the API directory creator

Import "shell32.lib"
  SHCreateDirectory(*hwnd, pszPath.p-unicode)
EndImport

; Load our image decoders

UseJPEGImageDecoder()
UsePNGImageDecoder()

Enumeration 1
  #Window_SmegScan
EndEnumeration

#WindowIndex  = #PB_Compiler_EnumerationValue

Enumeration 1
  ;Window_SmegScan
  #Gadget_SmegScan_sPreview
  #Gadget_SmegScan_Area18
  #Gadget_SmegScan_cScan
  #Gadget_SmegScan_Area20
  #Gadget_SmegScan_cControl
  #Gadget_SmegScan_Area22
  #Gadget_SmegScan_ScannedImage
  #Gadget_SmegScan_Scan
  #Gadget_SmegScan_Save
  #Gadget_SmegScan_PageNumber
  #Gadget_SmegScan_ImageType
  #Gadget_SmegScan_ImageQuality
  #Gadget_SmegScan_FileLocation
  #Gadget_SmegScan_Exit
EndEnumeration

#GadgetIndex = #PB_Compiler_EnumerationValue

Enumeration 1
  #Image_SmegScan_Temp      ; Temporary image variable for full scan
  #Image_SmegScan_Temp1     ; Temporary image variable for preview
  #Image_SmegScan_Scan      ; Scanner image for button
  #Image_SmegScan_ScanNext
  #Image_SmegScan_Save
  #Image_SmegScan_Exit
EndEnumeration

#ImageIndex = #PB_Compiler_EnumerationValue

CatchImage(#Image_SmegScan_Scan,      ?_MGB_SmegScan_Scan)
CatchImage(#Image_SmegScan_ScanNext,  ?_MGB_SmegScan_ScanNext)
CatchImage(#Image_SmegScan_Save,      ?_MGB_SmegScan_Save)
CatchImage(#Image_SmegScan_Exit,      ?_MGB_SmegScan_Exit)

DataSection
  _MGB_SmegScan_Scan      : IncludeBinary "Images\Scanner32x32-n.ico"
  _MGB_SmegScan_ScanNext  : IncludeBinary "Images\ScannerNext32x32-n.ico"
  _MGB_SmegScan_Save      : IncludeBinary "Images\Save32x32-n.ico"
  _MGB_SmegScan_Exit      : IncludeBinary "Images\Exit32x32-n.ico"
EndDataSection

Procedure.l Window_SmegScan()
  If OpenWindow(#Window_SmegScan,47,74,950,665,"",#PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_Invisible)
    SetWindowColor(#Window_SmegScan,$A2A2A2)
      ScrollAreaGadget(#Gadget_SmegScan_sPreview,5,5,145,575,143,575,5,#PB_ScrollArea_Flat|#PB_ScrollArea_BorderLess)
        SetGadgetColor(#Gadget_SmegScan_sPreview,#PB_Gadget_BackColor,$BFBFBF)
      CloseGadgetList()
      ContainerGadget(#Gadget_SmegScan_cScan,155,5,790,575,#PB_Container_Flat|#PB_Container_BorderLess)
        SetGadgetColor(#Gadget_SmegScan_cScan,#PB_Gadget_BackColor,$BFBFBF)
      CreateImage(#Image_SmegScan_Temp, 774,557)            ; Create blank image, the size of the imagegadget
      If StartDrawing(ImageOutput(#Image_SmegScan_Temp))    ; Start drawing on the temporary image
        Box(0, 0, 774,557, $DBDBDB)                         ; Draw a grey box in the temporary image
        StopDrawing()                                       ; Stop drawing on the temporary image
      EndIf                                                 ; Set the grey box to the imagegadget below
      ImageGadget(#Gadget_SmegScan_ScannedImage,5,5,780,565,ImageID(#Image_SmegScan_Temp))
      SetGadgetState(#Gadget_SmegScan_ScannedImage,0)
      CloseGadgetList()
      ContainerGadget(#Gadget_SmegScan_cControl,5,585,940,75,#PB_Container_Flat|#PB_Container_BorderLess)
        SetGadgetColor(#Gadget_SmegScan_cControl,#PB_Gadget_BackColor,$BFBFBF)
      ButtonImageGadget(#Gadget_SmegScan_Scan,10,10,45,45,ImageID(#Image_SmegScan_Scan))
      ButtonImageGadget(#Gadget_SmegScan_Save,55,10,45,45,ImageID(#Image_SmegScan_Save))
      TextGadget(#Gadget_SmegScan_PageNumber,10,55,90,15,"Scan Page #1")
        SetGadgetColor(#Gadget_SmegScan_PageNumber,#PB_Gadget_BackColor,$BFBFBF)
        SetGadgetFont(#Gadget_SmegScan_PageNumber,LoadFont(#Gadget_SmegScan_PageNumber,"Comic Sans MS",8,0))
      ComboBoxGadget(#Gadget_SmegScan_ImageType,105,30,110,25)
        SetGadgetColor(#Gadget_SmegScan_ImageType,#PB_Gadget_BackColor,$DBDBDB)
        SetGadgetFont(#Gadget_SmegScan_ImageType,LoadFont(#Gadget_SmegScan_ImageType,"Comic Sans MS",10,0))
      ComboBoxGadget(#Gadget_SmegScan_ImageQuality,215,30,110,25)
        SetGadgetColor(#Gadget_SmegScan_ImageQuality,#PB_Gadget_BackColor,$DBDBDB)
        SetGadgetFont(#Gadget_SmegScan_ImageQuality,LoadFont(#Gadget_SmegScan_ImageQuality,"Comic Sans MS",10,0))
      StringGadget(#Gadget_SmegScan_FileLocation,325,30,555,25,"",#PB_String_ReadOnly)
        SetGadgetColor(#Gadget_SmegScan_FileLocation,#PB_Gadget_BackColor,$DBDBDB)
        SetGadgetFont(#Gadget_SmegScan_FileLocation,LoadFont(#Gadget_SmegScan_FileLocation,"Comic Sans MS",10,0))
      ButtonImageGadget(#Gadget_SmegScan_Exit,885,10,45,45,ImageID(#Image_SmegScan_Exit))
      CloseGadgetList()
      HideWindow(#Window_SmegScan,0)
      ProcedureReturn WindowID(#Window_SmegScan)
  EndIf
EndProcedure

Structure ProgramData
  QuitFlag.i          ; Program quit semaphore
  ConfigFile.s        ; Path and name of the configuration file
  ProgramVersion.s    ; Keep track of program version and compile version
  CurrentDir.s        ; The current directory we are working in
  DocumentDir.s       ; Scanned documents directory 
  TemporaryDir.s      ; Temporary documents directory
  PdfFileName.s       ; Name of the PDF file
  ScanQuality.l       ; DPI quality of the scan
  PreviewTop.i        ; Keep track of the horizontal preview position per image
  PreviewLeft.i       ; Keep track of the Preview left position
  AvailableScroll.i   ; Available scroll length to add gadgets to
  ImageCounter.i      ; Number of preview images in the scroll area before having to expand area
  PreviewWidth.i      ; Width of the preview image
  PreviewHeight.i     ; Height of the preview image
EndStructure
  
Global Program.ProgramData

Program\CurrentDir      = GetCurrentDirectory()

Program\DocumentDir     = Program\CurrentDir  + "Documents\"
Program\TemporaryDir    = Program\CurrentDir  + "Temporary\"
Program\ConfigFile      = Program\CurrentDir  + "SmegScan.cfg"
Program\ProgramVersion  = "SmegScan, Version 0.5." + Str(#PB_Editor_CompileCount) + "." + Str(#PB_Editor_BuildCount)
Program\PdfFileName     = Program\DocumentDir + "Smegitty.pdf"

; 

Program\PreviewTop      =  5      ; Initial position from top
Program\PreviewLeft     = 10      ; Preview left position
Program\AvailableScroll = 575     ; Total initial scroll space
Program\PreviewWidth    = 105     ; Preview image width
Program\PreviewHeight   = 105     ; Preview image height

; Create the directories if you can

If  SHCreateDirectory(#Null, Program\DocumentDir) = 0
  Debug "Couldn't create " + Program\DocumentDir 
EndIf
  
If SHCreateDirectory(#Null, Program\TemporaryDir) = 0
  Debug "Couldn't create " + Program\TemporaryDir
EndIf

;IncludeFile "EZTwain.pbi"  This is normally in an include file, all the below to DisableExplicit

EnableExplicit

; XDefs translation of \EZTwain\VC\eztwain.h
;-----------------------------------------------------------------
; EZTWAIN.H - interface to Easy TWAIN library
; (DLL=eztw32.dll)
;
; 1.15     2006.05.09 Fix: If user closed the scan dialog during an Acquire,
;                     the last DIB handle, if any, was returned!
;                     Added VB\Eztwain.bas to package.
; 1.14     2004.08.06 Set XFERMECH=NATIVE as soon as DS is opened.
;                     trying to deal with scanners that default to memory xfer.
; 1.13     1999.09.08 Documented correct return codes of AcquireToFilename.
;                     - No code changes -
; 1.12     1998.09.14 Added Fix32ToFloat, allow MSG_OPENDS triplet.
;                     Added SetXferMech, XferMech.
; 1.11     1998.08.17 Added ToFix32, SetContrast, SetBrightness.
;                     Modified TWAIN_ToFix32 to round away-from-zero.
; 1.09beta 1998.07.27 Reverted from 1.08 to 1.06 and worked forward again.
; 1.06     1997.08.21 correction to message hook, fixed 32-bit exports
; 1.05     1996.11.06 32-bit conversion
; 1.04     1995.05.03 added: WriteNativeToFile, WriteNativeToFilename,
;                         FreeNative, SetHideUI, GetHideUI, SetCurrentUnits,
;                         GetCurrentUnits, SetCurrentResolution, SetBitDepth,
;                         SetCurrentPixelType, SetCapOneValue.
; 1.0a      1994.06.23 first alpha version
; 0.0      1994.05.11 created
;
; EZTWAIN 1.x is not a product, and is not the work of any company involved
; in promoting or using the TWAIN standard.  This code is sample code,
; provided without charge, and you use it entirely at your own risk.
; No rights or ownership is claimed by the author, or by any company
; or organization.  There are no restrictions on use or (re)distribution.
;
; Download from:    www.dosadi.com
;
; Support contact:  support@dosadi.com
;
; PB include file for EZTwain
; Ported from VB source by Harkon
; provided by the good people at EZTwain www.dosadi.com


; Use these values for wPixTypes

#TWAIN_BW       = 1
#TWAIN_GRAY     = 2
#TWAIN_RGB      = 4
#TWAIN_PALETTE  = 8
#TWAIN_ANYTYPE  = 0

; Use these for TWAIN_State

#TWAIN_PRESESSION     = 1
#TWAIN_SM_LOADED      = 2
#TWAIN_SM_OPEN        = 3
#TWAIN_SOURCE_OPEN    = 4
#TWAIN_SOURCE_ENABLED = 5
#TWAIN_TRANSFER_READY = 6
#TWAIN_TRANSFERRING   = 7

; Use these for Xtransfer Mode

#TWAIN_XFERMECH_NATIVE  = 0
#TWAIN_XFERMECH_FILE    = 1
#TWAIN_XFERMECH_MEMORY  = 2 

Import "EZTW32.LIB"

  TWAIN_AcquireNative.l(hwndApp.l, wPixTypes.l) As "_TWAIN_AcquireNative@8"
  
  ; The minimal use of EZTWAIN.DLL is to just call this routine, with 0 for
  ; both params.  EZTWAIN creates a window if hwndApp is 0.
  ;
  ; Acquires a single image, from the currently selected Data Source, using
  ; Native-mode transfer. It waits until the source closes (if it's modal) Or
  ; forces the source closed if not.  The return value is a handle to the
  ; acquired image.  Only one image can be acquired per call.
  ;
  ; Under Windows, the return value is a global memory handle - applying
  ; GlobalLock to it will return a (huge) pointer to the DIB, which
  ; starts with a BITMAPINFOHEADER.
  ; NOTE: You are responsible for disposing of the returned DIB - these things
  ; can eat up your Windows memory fast!  See TWAIN_FreeNative below.
  ;
  ; The image type can be restricted using the following masks.  A mask of 0
  ; means 'any pixel type is welcome'.
  ; Caution: You should not assume that the source will honor a pixel type
  ; restriction!  If you care, check the parameters of the DIB. 
  
  TWAIN_FreeNative(hdib.l) As "_TWAIN_FreeNative@4"
  
  ; Release the memory allocated to a native format image, as returned by
  ; TWAIN_AcquireNative. (If you are coding in C or C++, this is just a call
  ; to GlobalFree.)
  ; If you use TWAIN_AcquireNative and don't free the returned image handle,
  ; it stays around taking up Windows (virtual) memory until your application
  ; terminates.  Memory required per square inch:
  ;             1 bit B&W       8-bit grayscale     24-bit Colour
  ; 100 dpi      1.25KB              10KB               30KB
  ; 200 dpi        5KB               40KB              120KB
  ; 300 dpi      11.25KB             90KB              270KB
  ; 400 dpi       20KB              160KB              480KB
  ;
  
  TWAIN_AcquireToClipboard.l(hwndApp.l, wPixTypes.l) As "_TWAIN_AcquireToClipboard@8"
  
  ; Like AcquireNative, but puts the resulting image, if any, into the system
  ; clipboard.  Under Windows, this will put a CF_DIB item in the clipboard
  ; if successful.  If this call fails, the clipboard is either empty or
  ; contains the old contents.
  ; A return value of 1 indicates success, 0 indicates failure.
  ;
  ; Useful for environments like Visual Basic where it is hard to make direct
  ; use of a DIB handle.  In fact, TWAIN_AcquireToClipboard uses
  ; TWAIN_AcquireNative for all the hard work. 
  
  TWAIN_AquireToFilename.l(hwndApp.l, sFile.s) As "_TWAIN_AcquireToFilename@8"
  
  ; Acquire an image and write it to a .BMP (Windows Bitmap) file.
  ; The file name and path in pszFile are used.  If pszFile is NULL or
  ; points to an empty string, the user is prompted with a Save File dialog.
  ; Return values:
  ; 0 success
  ; -1 Acquire failed OR user cancelled File Save dialog
  ; -2 file open error (invalid path or name, or access denied)
  ; -3 (weird) unable to lock DIB - probably an invalid handle.
  ; -4 writing BMP data failed, possibly output device is full 
  
  TWAIN_SelectImageSource.l(hwnd.l) As "_TWAIN_SelectImageSource@4"
  
  ; This is the routine to call when the user chooses the "Select Source..."
  ; menu command from your application's File menu.  Your app has one of
  ; these, right?  The TWAIN spec calls for this feature to be available in
  ; your user interface, preferably as described.
  ; Note: If only one TWAIN device is installed on a system, it is selected
  ; automatically, so there is no need for the user to do Select Source.
  ; You should not require your users to do Select Source before Acquire.
  ; '
  ; This function posts the Source Manager's Select Source dialog box.
  ; It returns after the user either OK's Or CANCEL's that dialog.
  ; A return of 1 indicates OK, 0 indicates one of the following:
  ;   a) The user cancelled the dialog
  ;   b) The Source Manager found no data sources installed
  ;   c) There was a failure before the Select Source dialog could be posted
  ; -- details --
  ; Only sources that can return images (that are in the DG_IMAGE group) are
  ; displayed.  The current default source will be highlighted initially.
  ; In the standard implementation of "Select Source...", your application
  ; doesn't need To do anything except make this one call.
  ; '
  ; If you want to be meticulous, disable your "Acquire" and "Select Source"
  ; menu items or buttons if TWAIN_IsAvailable() returns 0 - see below. 
  
  ; --- TWAIN Basic Inquiries ---
  
  TWAIN_IsAvailable.l() As "_TWAIN_IsAvailable@0"
  
  ; Call this function any time to find out if TWAIN is installed on the
  ; system.  It takes a little time on the first call, after that it's fast,
  ; just testing a flag.  It returns 1 if the TWAIN Source Manager is
  ; installed & can be loaded, 0 otherwise.
  
  TWAIN_EasyVersion.l() As "_TWAIN_EasyVersion@0"
  
  ; Returns the version number of EZTWAIN.DLL, multiplied by 100.
  ; So e.g. version 2.01 will return 201 from this call.
  
  TWAIN_State.l() As "_TWAIN_State@0"
  
  ;Returns the TWAIN Protocol State per the spec.
  
  ; --- DIB Handling ___
  
  TWAIN_DibDepth.l(hdib.l) As "_TWAIN_DibDepth@4"
  
  ; Depth of DIB, in bits i.e. bits per pixel.
  
  TWAIN_DibWidth.l(hdib.l) As "_TWAIN_DibWidth@4"
  
  ; Width of DIB, in pixels (columns)
  
  TWAIN_DibHeight.l(hdib.l) As "_TWAIN_DibHeight@4"
  
  ; Height of DIB, in lines (rows)
  
  TWAIN_DibNumColors.l(hdib) As "_TWAIN_DibNumColors@4"
  
  ; Number of colors in color table of DIB
  
  DIB_RowBytes.l(hdib.l) As "_DIB_RowBytes@4"
  
  ; Number of bytes consumed by one row
  
  DIB_ReadRow(hdib.l, nRow.l, *prow) As "_DIB_ReadRow@12"
  
  ; Read row n of the given DIB into buffer at prow.
  ; Caller is responsible for ensuring buffer is large enough.
  ; Row 0 is the *top* row of the image, as it would be displayed.
  
  TWAIN_CreateDibPalette.l(hdib.l) As "_TWAIN_CreateDibPalette@4"
  
  ; Create and return a logical palette to be used for drawing the DIB.
  ; For 1, 4, and 8-bit DIBs the palette contains the DIB color table.
  ; For 24-bit DIBs, a default halftone palette is returned.
  
  TWAIN_DrawDibToDC(hDC.l, dx.l, dy.l, w.l, h.l, hdib.l, sx.l, sy.l) As "_TWAIN_DrawDibToDC@32"
  
  ; Draws a DIB on a device context.
  ; You should call CreateDibPalette, select that palette
  ; into the DC, and do a RealizePalette(hDC) first.
  
  ; ---  BMP File Utilities ---
  
  TWAIN_WriteNativeToFilename.l(hdib.l, sFile.s) As "_TWAIN_WriteNativeToFilename@8"
  
  ; Writes a DIB handle to a .BMP file
  ;
  ; hdib      = DIB handle, as returned by TWAIN_AcquireNative
  ; pszFile   = far pointer to NUL-terminated filename
  ; If pszFile is NULL or points to a null string, prompts the user
  ; for the filename with a standard file-save dialog.
  ;
  ; Return values:
  ;    0  success
  ;   -1  user cancelled File Save dialog
  ;   -2  file open error (invalid path or name, or access denied)
  ;   -3  (weird) unable to lock DIB - probably an invalid handle.
  ;   -4  writing BMP data failed, possibly output device is full 
  
  TWAIN_WriteNativeToFile.l(hdib.l, fh.l) As "_TWAIN_WriteNativeToFile@8"
  
  ; Writes a DIB to a file in .BMP format.
  ;
  ; hdib      = DIB handle, as returned by TWAIN_AcquireNative
  ; fh        = file handle, as returned by _open, _lopen or OpenFile
  ;
  ; Return value as for TWAIN_WriteNativeToFilename
  
  TWAIN_LoadNativeFromFilename.l(sFile.s) As "_TWAIN_LoadNativeFromFilename@4"
  
  ; Load a .BMP file and return a DIB handle (as from AcquireNative.)
  ; Accepts a filename (including path & extension).
  ; If pszFile is NULL or points to a null string, the user is prompted.
  ; Returns a DIB handle if successful, otherwise NULL.
  
  TWAIN_LoadNativeFromFile.l(fh.l) As "_TWAIN_LoadNativeFromFile@4"
  
  ; Like LoadNativeFromFilename, but takes an already open file handle. 
  
  TWAIN_SetHideUI(fHide.l) As "_TWAIN_SetHideUI@4"
  
  TWAIN_GetHideUI.l() As "_TWAIN_GetHideUI@0"
  
  ; These functions control the 'hide source user Interface' flag.
  ; This flag is cleared initially, but if you set it non-zero, then when
  ; a source is enabled it will be asked to hide its user interface.
  ; Note that this is only a request - some sources will ignore it!
  ; This affects AcquireNative, AcquireToClipboard, and EnableSource.
  ; If the user interface is hidden, you will probably want to set at least
  ; some of the basic acquisition parameters yourself - see
  ; SetCurrentUnits, SetBitDepth, SetCurrentPixelType and
  ; SetCurrentResolution below.
  
  ; --- Appliucation Registration ---
  
  TWAIN_RegisterApp(nMajorNum.l, nMinorNum.l, nLanguage.l, nCountry.l, lpszVersion.s, lpszMfg.s, lpszFamily.s, lpszProduct.s) As "_TWAIN_RegisterApp@32"
  
  ; TWAIN_RegisterApp can be called *AS THE FIRST CALL*, to register the
  ; application. If this function is not called, the application is given a
  ; 'generic' registration by EZTWAIN.
  ; Registration only provides this information to the Source Manager and any
  ; sources you may open - it is used for debugging, and (frankly) by some
  ; sources to give special treatment to certain applications.
  
  ; --- Error Analysis and Reporting ---
  
  TWAIN_GetResultCode.l() As "_TWAIN_GetResultCode@0"
  
  ; Return the result code (TWRC_xxx) from the last triplet sent to TWAIN
  
  TWAIN_GetConditionCode.l() As "_TWAIN_GetConditionCode@0"
  
  ; Return the condition code from the last triplet sent to TWAIN.
  ; (To be precise, from the last call to TWAIN_DS below)
  
  TWAIN_ErrorBox(sMsg.s) As "_TWAIN_ErrorBox@4"
  
  ; Post an error message dialog with an exclamation mark, OK button,
  ; and the title 'TWAIN Error'.
  ; pszMsg points to a null-terminated message string.
  
  TWAIN_ReportLastError(sMsg.s) As "_TWAIN_ReportLastError@4"
  
  ; Like TWAIN_ErrorBox, but if some details are available from
  ; TWAIN about the last failure, they are included in the message box. 
  
  ; --- TWAIN State Control ---
  
  TWAIN_LoadSourceManager.l() As "_TWAIN_LoadSourceManager@0"
  
  ; Finds and loads the Data Source Manager, TWAIN.DLL.
  ; If Source Manager is already loaded, does nothing and returns TRUE.
  ; This can fail if TWAIN.DLL is not installed (in the right place), or
  ; if the library cannot load for some reason (insufficient memory?) or
  ; if TWAIN.DLL has been corrupted. 
  
  TWAIN_OpenSourceManager.l(hwnd.l) As "_TWAIN_OpenSourceManager@4"
  
  ; Opens the Data Source Manager, if not already open.
  ; If the Source Manager is already open, does nothing and returns TRUE.
  ; This call will fail if the Source Manager is not loaded. 
  
  TWAIN_OpenDefaultSource.l() As "_TWAIN_OpenDefaultSource@0"
  
  ; This opens the source selected in the Select Source dialog.
  ; If a source is already open, does nothing and returns TRUE.
  ; Fails if the source manager is not loaded and open.
  
  TWAIN_EnableSource.l(hwnd.l) As "_TWAIN_EnableSource@4"
  
  ; Enables the open Data Source. This posts the source's user Interface
  ; And allows image acquisition To begin.  If the source is already enabled,
  ; this call does nothing And returns TRUE.
  
  TWAIN_DisableSource .l() As "_TWAIN_DisableSource@0"
  
  ; Disables the open Data Source, if any.
  ; This closes the source's user Interface.
  ; If there is Not an enabled source, does nothing And returns TRUE.
  
  TWAIN_CloseSource.l() As "_TWAIN_CloseSource@0"
  
  ; Closes the open Data Source, if any.
  ; If the source is enabled, disables it first.
  ; If there is Not an open source, does nothing And returns TRUE.
  
  TWAIN_CloseSourceManager.l(hwnd.l) As "_TWAIN_CloseSourceManager@4"
  
  ; Closes the Data Source Manager, If it is open.
  ; If a source is open, disables And closes it As needed.
  ; If the Source Manager is Not open, does nothing And returns TRUE.
  
  TWAIN_UnloadSourceManager.l() As "_TWAIN_UnloadSourceManager@0"
  
  ; Unloads the Data Source Manager i.e. TWAIN.DLL - releasing
  ; any associated memory Or resources.
  ; This call will fail If the Source Manager is open, otherwise
  ; it always succeeds And returns TRUE.
  
  TWAIN_WaitForNativeXfer.l(hwnd.l) As "_TWAIN_WaitForNativeXfer@4"
  
  TWAIN_ModalEventLoop() As "_TWAIN_ModalEventLoop@0"
  
  ; Process messages Until termination, source disable, Or image transfer.
  
  TWAIN_EndXfer.l() As "_TWAIN_EndXfer@0"
  
  TWAIN_AbortAllPendingXfers.l() As "_TWAIN_AbortAllPendingXfers@0"
  
  ; --- High-level Capability Negotiation Functions ---
  
  TWAIN_NegotiateXferCount.l(nXfers.l) As "_TWAIN_NegotiateXferCount@4"
  
  ; Negotiate With open Source the number of images application will accept.
  ; This is only allowed in State 4 (TWAIN_SOURCE_OPEN)
  ; nXfers = -1 means any number
  
  TWAIN_NegotiatePixelTypes.l(wPixTypes.l) As "_TWAIN_NegotiatePixelTypes@4"
  
  ; Negotiate with the source to restrict pixel types that can be acquired.
  ; This tries To restrict the source To a *set* of pixel types,
  ; See TWAIN_AcquireNative above For some mask constants.
  ; --> This is only allowed in State 4 (TWAIN_SOURCE_OPEN)
  ; A parameter of 0 (TWAIN_ANYTYPE) causes no negotiation & no restriction.
  ; You should Not assume that the source will honor your restrictions, even
  ; If this call succeeds!
  
  TWAIN_GetCurrentUnits.l() As "_TWAIN_GetCurrentUnits@0"
  
  ; Ask the source what its current unit of measure is.
  ; If anything goes wrong, this function just returns TWUN_INCHES (0).
  
  TWAIN_SetCurrentUnits.l(nUnits.l) As "_TWAIN_SetCurrentUnits@4"
  
  ; Set the current unit of measure for the source.
  ; Unit of measure codes are in TWAIN.H, but TWUN_INCHES is 0.
  
  TWAIN_GetBitDepth.l() As "_TWAIN_GetBitDepth@0"
  
  ; Get the current bitdepth, which can depend on the current PixelType.
  ; Bit depth is per Colour channel e.g. 24-bit RGB has bit depth 8.
  ; If anything goes wrong, this function returns 0.
  
  TWAIN_SetBitDepth.l(nBits.l) As "_TWAIN_SetBitDepth@4"
  
  ; (Try to) set the current bitdepth (for the current pixel type).
  
  TWAIN_GetPixelType.l() As "_TWAIN_GetPixelType@0"
  
  ; Ask the source For the current pixel type.
  ; If anything goes wrong (it shouldn't), this function returns 0 (TWPT_BW).
  
  TWAIN_SetCurrentPixelType.l(nPixType.l) As "_TWAIN_SetCurrentPixelType@4"
  
  ; (Try To) set the current pixel type For acquisition.
  ; This is only allowed in State 4 (TWAIN_SOURCE_OPEN)
  ; The source may Select this pixel type, but don't assume it will.
  
  TWAIN_GetCurrentResolution.d() As "_TWAIN_GetCurrentResolution@0"
  
  ; Ask the source For the current (horizontal) resolution.
  ; Resolution is in dots per current unit! (See TWAIN_GetCurrentUnits above)
  ; If anything goes wrong (it shouldn't) this function returns 0.0
  
  TWAIN_GetYResolution.d() As "_TWAIN_GetYResolution@0"
  
  ; Returns the current vertical resolution, in dots per *current unit*.
  ; In the event of failure, returns 0.0.
  
  TWAIN_SetCurrentResolution.l(dRes.d) As "_TWAIN_SetCurrentResolution@8"
  
  ; (Try To) set the current resolution For acquisition.
  ; Resolution is in dots per current unit! (See TWAIN_GetCurrentUnits above)
  ; This is only allowed in State 4 (TWAIN_SOURCE_OPEN)
  ; Note: The source may Select this resolution, but don't assume it will.
  
  TWAIN_SetContrast.l(dCon.d) As "_TWAIN_SetContrast@8"
  
  ; (Try To) set the current contrast For acquisition.
  ; The TWAIN standard says that the range For this cap is -1000 ... +1000
  
  TWAIN_SetBrightness.l(dBri.d) As "_TWAIN_SetBrightness@8"
  
  ; (Try To) set the current brightness For acquisition.
  ; The TWAIN standard says that the range For this cap is -1000 ... +1000
  
  TWAIN_SetXferMech.l(mech.l) As "_TWAIN_SetXferMech@4"
  
  TWAIN_XferMech.l() As "_TWAIN_XferMech@0"
  
  ; (Try To) set Or get the transfer mode - one of the following:
  ; #TWAIN_XFERMECH_NATIVE = 0
  ; #TWAIN_XFERMECH_FILE = 1
  ; #TWAIN_XFERMECH_MEMORY = 2 
  
  ; --- Low-level Capability Negotiation Functions ---
  ; Setting a capability is valid only in State 4 (TWAIN_SOURCE_OPEN)
  ; Getting a capability is valid in State 4 Or any higher state.
  
  TWAIN_SetCapOneValue.l(Cap.l, ItemType.l, ItemVal.l) As "_TWAIN_SetCapOneValue@12"
  
  ; Do a DAT_CAPABILITY/MSG_SET, on capability 'Cap' (e.g. ICAP_PIXELTYPE,
  ; CAP_AUTOFEED, etc.) using a TW_ONEVALUE container With the given item type
  ; And value.  The item value must fit into 32 bits.
  ; Returns TRUE (1) If successful, FALSE (0) otherwise.
  
  TWAIN_GetCapCurrent.l(Cap.l, ItemType.l, *pVal) As "_TWAIN_GetCapCurrent@12"
  
  ; Do a DAT_CAPABILITY/MSG_GETCURRENT on capability 'Cap'.
  ; Copy the current value out of the returned container into *pVal.
  ; If the operation fails (the source refuses the request), Or If the
  ; container is Not a ONEVALUE Or Enumeration, Or If the item type of the
  ; returned container is incompatible With the expected TWTY_ type in nType,
  ; returns FALSE.  If this function returns FALSE, *pVal is Not touched.
  
  TWAIN_ToFix32.l(d.d) As "_TWAIN_ToFix32@8"
  
  ; Convert a floating-point value To a 32-bit TW_FIX32 value that can be passed
  ; To e.g. TWAIN_SetCapOneValue
  
  TWAIN_Fix32ToFloat.d(nfix.l) As "_TWAIN_Fix32ToFloat@4"
  
  ; Convert a TW_FIX32 value (As returned from some capability inquiries)
  ; To a double (floating point) value. 
  
  ; --- Lowest-level functions For TWAIN protocol ---
  
  TWAIN_DS.l(DG.l, DAT.l, MSG.l, *pData) As "_TWAIN_DS@16"
  
  ; Passes the triplet (DG, DAT, MSG, pData) To the open Data source If any.
  ; Returns 1 (TRUE) If the result code is TWRC_SUCCESS, 0 (FALSE) otherwise.
  ; The last result code can be retrieved With TWAIN_GetResultCode(), And the corresponding
  ; condition code can be retrieved With TWAIN_GetConditionCode().
  ; If no source is open this call will fail, result code TWRC_FAILURE, condition code TWCC_NODS.
  
  TWAIN_Mgr.l(DG.l, DAT.l, MSG.l, *pData) As "_TWAIN_Mgr@16"
  
  ; Passes a triplet To the Data Source Manager (DSM).
  ; Returns 1 (TRUE) If the result code is TWRC_SUCCESS, 0 (FALSE) otherwise.
  ; The last result code can be retrieved With TWAIN_GetResultCode(), And the corresponding
  ; condition code can be retrieved With TWAIN_GetConditionCode().
  ; If the Source Manager is Not open, this call will fail, And set the result code To TWRC_FAILURE,
  ; With a condition code of TWCC_SEQERROR (triplet out of sequence).
EndImport

UseJPEGImageEncoder()
UsePNGImageEncoder()

Procedure.l hkTWAIN_WriteNativeToPNG(hDib.l, sPNGFileName.s)
  Define BMPFileHandle.i
  Define lpDib.l
  Define StructSize.l
  Define Width.l
  Define Height.l
  Define Planes.w
  Define BitCount.w
  Define Compression.l
  Define SizeImage.l
  Define XPix_Meter.l
  Define YPix_Meter.l
  Define ColorsUsed.l
  Define ColorsImportant.l
  Define PalletteSize.l
  Define BMPOffset.l
  Define BMPSize.l
  Define ImageNumber.l
  Define *BMPBuffer
 
  lpDib = GlobalLock_(hDib)
 
  StructSize      = PeekL(lpDib)
  Width           = PeekL(lpDib +  4)
  Height          = PeekL(lpDib +  8)
  Planes          = PeekW(lpDib + 12)
  BitCount        = PeekW(lpDib + 14)
  Compression     = PeekL(lpDib + 16)
  SizeImage       = PeekL(lpDib + 20)
  XPix_Meter      = PeekL(lpDib + 24)
  YPix_Meter      = PeekL(lpDib + 28)
  ColorsUsed      = PeekL(lpDib + 32)
  ColorsImportant = PeekL(lpDib + 36)
 
  ; Make BMP for CatchImage
  
  If BitCount < 24
    If ColorsUsed = 0 And BitCount< = 8
      PalletteSize = (1 << BitCount) * 4  ; 4 is the size of the RGBQUAD data type (makes total in bytes
    EndIf
  EndIf
  
  ; BMPFileHeader is
  ; "BM",BMPSize.l,Reserved.w, Reserved.w, Offset.l where bmp data starts
  ; the offset is after the pallette info
  ; pallette info is RGB+1 byte for each color in the pallette
  ; unless it's 24bit then there's no pallette
  
  BMPOffset = 14 + StructSize + PalletteSize
  BMPSize   = BMPOffset + SizeImage
 
  *BMPBuffer  = AllocateMemory(BMPSize)
  
  PokeS(*BMPBuffer, "BM")
  PokeL(*BMPBuffer  + 2,  BMPSize)
  PokeL(*BMPBuffer  + 10, BMPOffset)
  CopyMemory(lpDib, *BMPBuffer  + 14, BMPSize - 14)
 
  GlobalUnlock_(hDib)
 
  ImageNumber = CatchImage(#PB_Any, *BMPBuffer, BMPSize)
  
  If sPNGFileName = ""
    sPNGFileName  = SaveFileRequester("Save PNG file",  Program\TemporaryDir,  "PNG (*.png)|*.png",  0)
  EndIf

  ; Just write BMP
  
  BMPFileHandle.i = CreateFile(#PB_Any, Program\TemporaryDir + "temp.bmp")
  
  If BMPFileHandle.i
    ;CreateFile(0, Program\TemporaryDir + "temp.bmp")
    WriteData(BMPFileHandle.i, *BMPBuffer, BMPSize)
    CloseFile(BMPFileHandle.i)
  EndIf
  
  ProcedureReturn SaveImage(ImageNumber, sPNGFileName, #PB_ImagePlugin_PNG)
  
  FreeImage(ImageNumber)
  FreeMemory(*BMPBuffer)
  
EndProcedure

Procedure.l hkTWAIN_WriteNativeToJPG(hDib.l, sJPGFileName.s, lQuality.l)
  Define lpDib.l
  Define StructSize.l
  Define Width.l
  Define Height.l
  Define Planes.w
  Define BitCount.w
  Define Compression.l
  Define SizeImage.l
  Define XPix_Meter.l
  Define YPix_Meter.l
  Define ColorsUsed.l
  Define ColorsImportant.l
  Define PalletteSize.l
  Define BMPOffset.l
  Define BMPSize.l
  Define ImageNumber.l
  Define *BMPBuffer

  lpDib = GlobalLock_(hDib)
 
  StructSize      = PeekL(lpDib)
  Width           = PeekL(lpDib +  4)
  Height          = PeekL(lpDib +  8)
  Planes          = PeekW(lpDib + 12)
  BitCount        = PeekW(lpDib + 14)
  Compression     = PeekL(lpDib + 16)
  SizeImage       = PeekL(lpDib + 20)
  XPix_Meter      = PeekL(lpDib + 24)
  YPix_Meter      = PeekL(lpDib + 28)
  ColorsUsed      = PeekL(lpDib + 32)
  ColorsImportant = PeekL(lpDib + 36)
 
  ; Make BMP for CatchImage
  
  If BitCount < 24
    If ColorsUsed = 0 And BitCount< = 8
      PalletteSize = (1 << BitCount) * 4  ; 4 is the size of the RGBQUAD data type (makes total in bytes
    EndIf
  EndIf
  
  ; BMPFileHeader is
  ; "BM",BMPSize.l,Reserved.w, Reserved.w, Offset.l where bmp data starts
  ; the offset is after the pallette info
  ; pallette info is RGB+1 byte for each Colour in the pallette
  ; unless it's 24bit then there's no pallette
  
  BMPOffset = 14  + StructSize  + PalletteSize
  BMPSize = BMPOffset  + SizeImage
 
  *BMPBuffer  = AllocateMemory(BMPSize)
  
  PokeS(*BMPBuffer, "BM")
  PokeL(*BMPBuffer  + 2,  BMPSize)
  PokeL(*BMPBuffer  + 10, BMPOffset)
  CopyMemory(lpDib, *BMPBuffer  + 14, BMPSize - 14)
 
  GlobalUnlock_(hDib)
 
  ImageNumber = CatchImage(#PB_Any, *BMPBuffer, BMPSize)
 
  If sJPGFileName = ""
    sJPGFileName.s  = SaveFileRequester("Save JPG file", Program\TemporaryDir, "JPG (*.jpg)|*.jpg", 0)
  EndIf
 
  ; lQuality is spcified in terms of a number between 1 and 10, 10 being the best quality lowest compression
  ; if not specified and the quality is 0 or invalid, we will default to 7
  
  If lQuality < 1 Or lQuality > 10
    lQuality = 10
  EndIf 
  
  ProcedureReturn SaveImage(ImageNumber, sJPGFileName, #PB_ImagePlugin_JPEG, lQuality)
  
  FreeImage(ImageNumber)
  FreeMemory(*BMPBuffer)
  
EndProcedure

DisableExplicit

; This is the end of the EZTWain.PBI include file

Declare.l hkTWAIN_AquireToPDF(wPixTypes.l, dResolution.d, reset.w)
Declare   ScanPage()
Declare   SaveScan()
Declare   ShowScan(sTempFileName.s)
Declare   ShowPreview(sTempFileName.s)

; Harkon

Procedure.l hkTWAIN_AquireToPDF(wPixTypes.l, dResolution.d, reset.w)
  Static PDFStarted.l
  Static PageNumber.l
  Define hDib.l
  Define sTempFileName.s
  If reset
    PDFStarted = 0
    PageNumber = 0
    ProcedureReturn 1
  EndIf
  If TWAIN_State() <> #TWAIN_SOURCE_OPEN
    TWAIN_OpenDefaultSource()
  EndIf
  If Not TWAIN_GetHideUI()
    TWAIN_SetHideUI(1)
  EndIf
  TWAIN_SetCurrentResolution(dResolution)
  If wPixTypes = 3      ; Colour should be 4 not 3 this is a hck fix
    wPixtypes = 4
  EndIf
  hDib = TWAIN_AcquireNative(0, wPixTypes)
  If hDib
    PageNumber + 1
    sTempFileName = Program\TemporaryDir + "tempscan" + RSet(Str(PageNumber), 3, "0") + ".png"
    hkTWAIN_WriteNativeToPNG(hdib, sTempFileName)
    TWAIN_FreeNative(hDib)
    If Not PDFStarted
      pdf_Create("P", "in", #PDF_PAGE_FORMAT_A4)
      pdf_SetTitle("Scan")
      PDFStarted = 1
    EndIf
    ShowScan(sTempFileName)                                     ; Fangy function to show scanned image
    ShowPreview(sTempFileName)                                  ; Fangy function to show scanned image preview
    pdf_AddPage("P", #PDF_PAGE_FORMAT_A4)
    pdf_Image(sTempFileName, 0, 0, 8.5, 0)
    ; DeleteFile(sTempFileName)                                  ; Delete temporary scan image
    ; Replace this code with in window code
    SetGadgetText(#Gadget_SmegScan_PageNumber, "Scan Page #" + Str(PageNumber + 1))
    ProcedureReturn 1
  EndIf
  ProcedureReturn 0 
EndProcedure

; Harkon

Procedure ScanPage()
  Select  GetGadgetState(#Gadget_SmegScan_ImageQuality)   ; Get dpi for page here. colour depth is in order of index + 1
    Case 0    : Program\ScanQuality  =   75
    Case 1    : Program\ScanQuality  =  100
    Case 2    : Program\ScanQuality  =  150
    Case 3    : Program\ScanQuality  =  300
    Case 4    : Program\ScanQuality  =  600
    Case 5    : Program\ScanQuality  = 1200
    Default   : Program\ScanQuality  =   75
  EndSelect
  DisableGadget(#Gadget_SmegScan_Scan, 1)                 ; Disable scan button
  DisableGadget(#Gadget_SmegScan_Save, 1)                 ; Disable save button
  If hkTWAIN_AquireToPDF(GetGadgetState(#Gadget_SmegScan_ImageType) + 1, Program\ScanQuality, 0)
    DisableGadget(#Gadget_SmegScan_Save, 0)               ; Enable the save button if at least 1 page ready
  EndIf
  DisableGadget(#Gadget_SmegScan_Scan, 0)                 ; Enable the scan button again
  SetGadgetAttribute(#Gadget_SmegScan_Scan, #PB_Button_Image, ImageID(#Image_SmegScan_ScanNext))
EndProcedure

; Fangy

Procedure SaveScan()
  Program\PdfFileName = GetGadgetText(#Gadget_SmegScan_FileLocation)        ; Finalize and save pdf file
  If Program\PdfFileName = ""
    Program\PdfFileName = SaveFileRequester("Save PDF file", Program\PdfFileName, "PDF (*.pdf)|*.pdf", 0)
    If LCase(GetExtensionPart(Program\PdfFileName)) <> "pdf"
      Program\PdfFileName + ".pdf"
    EndIf
    SetGadgetText(#Gadget_SmegScan_FileLocation, Program\PdfFileName)
  EndIf
  Program\PdfFileName = SaveFileRequester("Save PDF file", Program\PdfFileName, "PDF (*.pdf)|*.pdf", 0)
  If Program\PdfFileName
    pdf_Save(Program\PdfFileName)
  EndIf
  If Not hkTWAIN_AquireToPDF(GetGadgetState(#Gadget_SmegScan_ImageType) + 1, Program\ScanQuality, 1)
    MessageRequester("Scan2PDF","Could not reset PDF Engine, please restart application.")
  EndIf
  SetGadgetAttribute(#Gadget_SmegScan_Scan, #PB_Button_Image, ImageID(#Image_SmegScan_Scan))
  DisableGadget(#Gadget_SmegScan_Save, 1)
  ClearGadgetItems(#Gadget_SmegScan_sPreview)
  Program\PreviewTop      =  5      ; Initial position from top
  Program\AvailableScroll = 575     ; Total initial scroll space
  Program\ImageCounter    =   0
EndProcedure

;==========================================================================================================================
; Infratec
;==========================================================================================================================

Procedure ShowScan(sTempFileName.s)
  NewImageNumber.i = LoadImage(#PB_Any, sTempFileName.s)          ; Load the temp scan image from Temporary\ dir
  ImageGadgetWidth.i  = GadgetWidth(#Gadget_SmegScan_ScannedImage)
  ImageGadgetHeight.i = GadgetHeight(#Gadget_SmegScan_ScannedImage)
  If NewImageNumber
    NewImageWidth   = ImageWidth(NewImageNumber)
    NewImageHeight  = ImageHeight(NewImageNumber)
    If StartDrawing(ImageOutput(#Image_SmegScan_Temp))
      RatioW.f = 1.0
      RatioH.f = 1.0
      If NewImageWidth > ImageGadgetWidth.i
        RatioW = NewImageWidth / ImageGadgetWidth.i
      Else
        NewWidth = NewImageWidth
      EndIf
      If NewImageHeight > ImageGadgetHeight.i
        RatioH = NewImageHeight / ImageGadgetHeight.i
      Else
        NewHeight = NewImageHeight
      EndIf
      If RatioW <> RatioH
        If RatioW > RatioH
          NewWidth = ImageGadgetWidth.i
          NewHeight = NewImageHeight / RatioW
        Else
          NewWidth = NewImageWidth / RatioH
          NewHeight = ImageGadgetHeight.i
        EndIf
      Else
        NewWidth = NewImageWidth / RatioW
        NewHeight = NewImageHeight / RatioH
      EndIf
      DrawImage(ImageID(NewImageNumber), (ImageGadgetWidth.i - NewWidth) / 2, (ImageGadgetHeight.i - NewHeight) / 2, NewWidth, NewHeight)
      StopDrawing()
      SetGadgetState(#Gadget_SmegScan_ScannedImage, ImageID(#Image_SmegScan_Temp))
    Else
      Debug "Can't draw the new image to the imagegadget"
    EndIf
  Else
    Debug "Cannot get new image number"
  EndIf
EndProcedure

; 

Procedure ShowPreview(sTempFileName.s)
  ; Only proceed if we got a new picture
  If sTempFileName.s
    ; Create blank image, the size of the preview, grey
    CreateImage(#Image_SmegScan_Temp1, Program\PreviewWidth, Program\PreviewHeight)
    ; Start drawing on the temporary image
    If StartDrawing(ImageOutput(#Image_SmegScan_Temp1))
      ; Draw a grey box in the temporary image
      Box(0, 0, Program\PreviewWidth, Program\PreviewHeight, $BFBFBF)
      ; Stop drawing on the temporary image
      StopDrawing()
    EndIf
    ; Load a new picture into the image strip from any directory
    NewImageNumber.i = LoadImage(#PB_Any, sTempFileName.s)
    ; Only proceed if we got a new image handle
    If NewImageNumber
      ; Resize the image to the needed preview size
      ResizeImage(NewImageNumber, Program\PreviewWidth, Program\PreviewHeight)
      ; Now open the gadget list to add a new image preview to it
      OpenGadgetList(#Gadget_SmegScan_sPreview)
        ; Create a new imagegadget in the scrollarea list 5 across, Program\PreviewWidth down
        NewImageGadget.i = ImageGadget(#PB_Any, Program\PreviewLeft, Program\PreviewTop, Program\PreviewWidth, Program\PreviewHeight, ImageID(NewImageNumber))
        ; Create a new label gadget at the bottom of the picture
        NewLabelGadget.i =  TextGadget(#PB_Any, Program\PreviewLeft, Program\PreviewTop + Program\PreviewHeight, Program\PreviewWidth, 20, "", #PB_Text_Center)
        ; Add the scroll gadget colour as the label background
        SetGadgetColor(NewLabelGadget.i, #PB_Gadget_BackColor, $BFBFBF)
        ;SetGadgetFont(NewLabelGadget.i, LoadFont(#PB_Any, "Comic Sans MS", 8, 0))
        SetGadgetText(NewLabelGadget.i, "Scan Image #" + Str(Program\ImageCounter + 1))
        ; Increment the position counter for the next preview    ; Height of image plus space plus label plus space
        Program\PreviewTop + Program\PreviewHeight + 5 + 15
        ; Keep track of number of preview images in the scroll area. Add size of image after page 4 + space
        Program\ImageCounter + 1
        ; If there are 5 previews in the list, time to start expanding the view area
        If Program\ImageCounter > = 5
          ; Height of the last image plus space
          Program\AvailableScroll + Program\PreviewHeight + 5
          ; Expand the scrolled area
          SetGadgetAttribute(#Gadget_SmegScan_sPreview, #PB_ScrollArea_InnerHeight, Program\AvailableScroll)
          ; Always keep the last image visible added
          SetGadgetAttribute(#Gadget_SmegScan_sPreview, #PB_ScrollArea_Y, Program\AvailableScroll)
        EndIf
        ; Close the scroll area gadget list
      CloseGadgetList()
    Else
      Debug "Cannot get new image number"
    EndIf
  Else
    Debug "Cannot load a picture or user cancelled"
  EndIf
EndProcedure

; Before we actually do anything let's make sure the app isn't already running

If CreateMutex_(0, 1, "SmegScan") = 0 Or GetLastError_() <> 0
  End
EndIf

;- Startupcode

If Not TWAIN_IsAvailable()
  MessageRequester("SmegScan", "TWAIN scanner interface is not available!")
  End
EndIf

; 

If Window_SmegScan()
  
  ; Add colour choices
  
  AddGadgetItem(#Gadget_SmegScan_ImageType, -1, "Black & White")
  AddGadgetItem(#Gadget_SmegScan_ImageType, -1, "GrayScale")
  AddGadgetItem(#Gadget_SmegScan_ImageType, -1, "Colour (Photo)")
  
  ; Add dpi choices
  
  AddGadgetItem(#Gadget_SmegScan_ImageQuality,-1,   "75 dpi")
  AddGadgetItem(#Gadget_SmegScan_ImageQuality,-1,  "100 dpi")
  AddGadgetItem(#Gadget_SmegScan_ImageQuality,-1,  "150 dpi")
  AddGadgetItem(#Gadget_SmegScan_ImageQuality,-1,  "300 dpi")
  AddGadgetItem(#Gadget_SmegScan_ImageQuality,-1,  "600 dpi")
  AddGadgetItem(#Gadget_SmegScan_ImageQuality,-1, "1200 dpi")
  
  ; Set the values in the combo boxes from the config file

  If Not OpenPreferences(Program\ConfigFile)
    MessageRequester("SmegScan", "Could not find preferences file, using default values")
  Else
    SetGadgetState(#Gadget_SmegScan_ImageType,      ReadPreferenceInteger("Colour Index",      2))
    SetGadgetState(#Gadget_SmegScan_ImageQuality,   ReadPreferenceInteger("Quality Index",     0))
    SetGadgetText(#Gadget_SmegScan_FileLocation,    ReadPreferenceString("Destination File",  ""))
  EndIf
  
  ; Disable save button until we actually have a page to save
  
  DisableGadget(#Gadget_SmegScan_Save, 1)
  
  ; 
  
  SetWindowTitle(#Window_SmegScan, Program\ProgramVersion)

  ; 
  
  Program\QuitFlag  = 0
  
  ; 
  
  Repeat
    EventID  =  WaitWindowEvent()
    MenuID   =  EventMenu()
    GadgetID =  EventGadget()
    WindowID =  EventWindow()
    Select EventID
      Case #PB_Event_CloseWindow
        Select WindowID
          Case #Window_SmegScan       : Program\QuitFlag  = 1
        EndSelect
      Case #PB_Event_Gadget
        Select GadgetID
          Case #Gadget_SmegScan_Scan  : ScanPage()
          Case #Gadget_SmegScan_Save  : SaveScan()
          Case #Gadget_SmegScan_Exit  : Program\QuitFlag  = 1
        EndSelect
    EndSelect
  Until Program\QuitFlag
  
  ; Save preferences on exit
  
  If CreatePreferences(Program\ConfigFile)
    WritePreferenceInteger("Colour Index",     GetGadgetState(#Gadget_SmegScan_ImageType))
    WritePreferenceInteger("Quality Index",    GetGadgetState(#Gadget_SmegScan_ImageQuality))
    WritePreferenceString("Destination File",   GetGadgetText(#Gadget_SmegScan_FileLocation))
    ClosePreferences()
  Else
    MessageRequester("Scan2PDF", "Could not save program preferences file " + Program\ConfigFile)
  EndIf
  
  ; Close the program window
  
  CloseWindow(#Window_SmegScan)
  
  ; 
  
EndIf

; 

End
Amateur Radio, D-STAR/VK3HAF
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Scan2PDF

Post by IdeasVacuum »

saying that ImageNumber isn't initialised when it is
I have seen the same thing a few times, always found PB to be more than a bit confusing in this area. It smells like a bug but I'm not sure.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Fangbeast
PureBasic Protozoa
PureBasic Protozoa
Posts: 4747
Joined: Fri Apr 25, 2003 3:08 pm
Location: Not Sydney!!! (Bad water, no goats)

Re: Scan2PDF

Post by Fangbeast »

IdeasVacuum wrote:
saying that ImageNumber isn't initialised when it is
I have seen the same thing a few times, always found PB to be more than a bit confusing in this area. It smells like a bug but I'm not sure.
That's the weird thing, the code in my scan code above truly is EXACTLY the same as in my DocMan code, no changes whatsoever. Or I am going mad and need to be put into the home for feeble old hamster bunnies.

I put in heaps of debug statements around various things in the offending module and the raw hDib pointer was being generated every time but the problem must be further up at the *BMPBuffer as both the BMP and saved PNG file are empty when saved to disk.
Amateur Radio, D-STAR/VK3HAF
harkon
Enthusiast
Enthusiast
Posts: 217
Joined: Wed Nov 23, 2005 5:48 pm

Re: Scan2PDF

Post by harkon »

Looking at the code, in Procedure hkTWAIN_WriteNativeToPNG(),

At the end of the proc, the imagenumber and memory never get freed as the proc exits before that. That's one thing that should get fixed. It was a weak moment, sorry about that.

Thinking outside the box for a moment, if the procedure works in one codebase and not another, it would be time to look at what the new codebase is doing and how it would affect what's existing. Just giving a quick look, is the include file now inline. If so try as an include, maybe the order is messing with you.
Missed it by that much!!
HK
Post Reply