udfTIFPageCount
int|arr udfTIFPageCount (str, int)
;==========================================================================================================================================
; Create a list of all TIF files with their number of included image pages.
;
; Detlev Dalitz.20120412.20120413.20120414.
;==========================================================================================================================================

;------------------------------------------------------------------------------------------------------------------------------------------
#DefineFunction udfTIFPageCount (strFilename, intMode) ; Version 2.
IntControl (73, 1, @TRUE, "", 0) ; On error goto the label :WBERRORHANDLER and leave the function.
LastError ()
strByteOrder = ""
hdlBB = BinaryAlloc (4)
BinaryReadEx (hdlBB, 0, strFilename, 0, 2)
strByteOrder = BinaryPeekStr (hdlBB, 0, 2)
intByteOrder = BinaryPeek2 (hdlBB, 0)
Switch intByteOrder
Case 18761 ; "II" the file is little-endian byte order (Intel).
   BinaryReadEx (hdlBB, 0, strFilename, 2, 2)
   intFileID = BinaryPeek2 (hdlBB, 0) ; These 2 header bytes are the file identifier. Version number (always 42).
   If intFileID != 42 ; If this is not 42, it is not a valid tif image.
      intPageCount = -2
      Break
   EndIf
   intPageCount = 0
   BinaryReadEx (hdlBB, 0, strFilename, 4, 4)
   intPosIFD = BinaryPeek4 (hdlBB, 0) ; Offset to first IFD Image File Directory. This IFD can be located anywhere in the file. Every 'page' in a multi-page TIFF is represented by one IFD.
   While intPosIFD ; Break on the null pointer.
      BinaryReadEx (hdlBB, 0, strFilename, intPosIFD, 2)
      intTags = BinaryPeek2 (hdlBB, 0)   ; Number of tags in IFD. Read the first 2 bytes of the IFD header for the number of directory entries for this page.
      intPosNext = intPosIFD + 2 + (12 * intTags) ; Next pointer location is the number of entries at 12 bytes each plus 2 for the header. Offset to next IFD, if there is a next IFD, 0 otherwise.
      BinaryReadEx (hdlBB, 0, strFilename, intPosNext, 4)
      intPosIFD = BinaryPeek4 (hdlBB, 0) ; Read next pointer.
      intPageCount = intPageCount + 1    ; Increment page counter.
   EndWhile
   Break
Case 19789 ; "MM" the file is big-endian byte order (Motorola).
   BinaryReadEx (hdlBB, 0, strFilename, 2, 2)
   BinaryPokeHex (hdlBB, 0, BinaryPeekHex (hdlBB, 1, 1) : BinaryPeekHex (hdlBB, 0, 1)) ; Reverse byte order.
   intFileID = BinaryPeek2 (hdlBB, 0) ; These 2 header bytes are the file identifier. Version number (always 42).
   If intFileID != 42 ; If this is not 42, it is not a valid tif image.
      intPageCount = -2
      Break
   EndIf
   intPageCount = 0
   BinaryReadEx (hdlBB, 0, strFilename, 4, 4)
   BinaryPokeHex (hdlBB, 0, BinaryPeekHex (hdlBB, 3, 1) : BinaryPeekHex (hdlBB, 2, 1) : BinaryPeekHex (hdlBB, 1, 1) : BinaryPeekHex (hdlBB, 0, 1)) ; Reverse byte order.
   intPosIFD = BinaryPeek4 (hdlBB, 0) ; Offset to first IFD Image File Directory. This IFD can be located anywhere in the file. Every 'page' in a multi-page TIFF is represented by one IFD.
   While intPosIFD ; Break on the null pointer.
      BinaryReadEx (hdlBB, 0, strFilename, intPosIFD, 2)
      BinaryPokeHex (hdlBB, 0, BinaryPeekHex (hdlBB, 1, 1) : BinaryPeekHex (hdlBB, 0, 1)) ; Reverse byte order.
      intTags = BinaryPeek2 (hdlBB, 0)   ; Number of tags in IFD. Read the first 2 bytes of the IFD header for the number of directory entries for this page.
      intPosNext = intPosIFD + 2 + (12 * intTags) ; Next pointer location is the number of entries at 12 bytes each plus 2 for the header. Offset to next IFD, if there is a next IFD, 0 otherwise.
      BinaryReadEx (hdlBB, 0, strFilename, intPosNext, 4)
      BinaryPokeHex (hdlBB, 0, BinaryPeekHex (hdlBB, 3, 1) : BinaryPeekHex (hdlBB, 2, 1) : BinaryPeekHex (hdlBB, 1, 1) : BinaryPeekHex (hdlBB, 0, 1)) ; Reverse byte order.
      intPosIFD = BinaryPeek4 (hdlBB, 0) ; Read next pointer.
      intPageCount = intPageCount + 1    ; Increment page counter.
   EndWhile
   Break
Case intByteOrder
   intPageCount = -1 ; Not a valid tif image.
EndSwitch
:WBERRORHANDLER
If LastError () Then intPageCount = -3 ; Not a valid tif image.
hdlBB = BinaryFree (hdlBB)
If !intMode Then Return intPageCount
Return Arrayize (intPageCount : "|" : strByteOrder, "|")
;..........................................................................................................................................
; This UDF "udfTIFPageCount" returns the integer number of all pages included in the given TIF file.
;
; intMode=0 ... Return the number of pages as integer value.
; intMode=1 ... Return an array of two values.
;               arrResult[0] ... the number of pages as integer value.
;               arrResult[1] ... the byte order mark as a two character string value.
;
; In case of an error ...
; ... with intMode=0 the integer return value resp. with intMode=1 the arrResult[0] contains a negative integer value:
;        -1 ... The TIF byte order mark "II" resp. "MM" cannot be found;
;        -2 ... The TIF version number (always 42) cannot be found;
;        -3 ... Any other error, possibly damage of the TIF file structure.
; ... with intMode=1 the arrResult[1] contains the first two characters from the current file, if any.
;
; See TIF specifications ...
; http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
; http://www.awaresystems.be/imaging/tiff.html
;
; Detlev Dalitz.20120412.20120413.20120414.
;..........................................................................................................................................
#EndFunction
;------------------------------------------------------------------------------------------------------------------------------------------


;------------------------------------------------------------------------------------------------------------------------------------------
#DefineSubRoutine udfPathFold (strPath)
If !StrIndex (strPath, "\\", 0, @FWDSCAN) Then Return StrReplace (strPath, "\", "\" : @LF)
Return "\\" : StrReplace (StrSub (strPath, 3, -1), "\", "\" : @LF)
#EndSubRoutine
;------------------------------------------------------------------------------------------------------------------------------------------


; Test.

DirChange (DirScript ())

strFileThis = IntControl (1004, 0, 0, 0, 0)
strFileListIn = ItemReplace ("TIF.List.In.txt", -1, strFileThis, ".")
strFileListOut = ItemReplace ("TIF.List.Out.txt", -1, strFileThis, ".")

;----------------------------
:Step1 ; Collect TIF files.
;----------------------------

arrMasks = Arrayize ("*.tif|*.tiff", "|")
intMasksLast = ArrInfo (arrMasks, 1) - 1

arrDisks = Arrayize (DiskScan (2), @TAB)
intDisksLast = ArrInfo (arrDisks, 1) - 1


; File and Folder Finder.
; Load Appropriate Extender module.
If WinMetrics (-2) == 3 Then AddExtender ("WWFAF64I.DLL") ; 64-bit
   Else AddExtender ("WWFAF44I.DLL") ; 32-bit
   ;fafDefault = 0     ; Use extender defaults, same as not specifying a value for the parameter.
fafHidden = 1      ; Include hidden files.
;fafSystem = 2      ; Include system files.
;fafFolders = 4     ; Inspect  subfolder names also.
;fafFindFolders = 8 ; Only inspect subfolder names not file names.
fafRecurse = 16    ; Recurse through subfolders.
;fafNoPathInfo = 32 ; Do not return path information to files or folders found.
;fafNoRedirect = 64 ; Do not turn off file redirection for x64 windows.


strMsgTitle = 'Step 1 | TIF | Search for TIF files'
strMsgText = ""
BoxOpen (strMsgTitle, strMsgText)
WinPlace (200, 200, 600, 700, "")
BoxDataTag (1, 1)

intF = 0
arrFiles = ArrDimension (0)

For intD = 0 To intDisksLast
   strFolderRoot = arrDisks[intD] : "\"
   For intM = 0 To intMasksLast
      hdlFAF = fafOpen (strFolderRoot, arrMasks[intM], fafRecurse | fafHidden)
      While @TRUE
         strFileFullName = fafFind (hdlFAF)
         If strFileFullName == "" Then Break

         ArrayRedim (arrFiles, intF + 1)
         arrFiles[intF] = strFileFullName
         intF = intF + 1

         strMsgTitle1 = strMsgTitle : "|" : intF : "|" : FileBaseName (strFileFullName)
         strMsgText = "Files found: " : intF : @LF : @LF : udfPathFold (strFileFullName)
         BoxTitle (strMsgTitle1)
         BoxText (strMsgText)
         BoxDataClear (1, 1)
      EndWhile
      fafClose (hdlFAF)
   Next
Next

If !!ArrInfo (arrFiles, 1) Then ArraySort (arrFiles)
intBytesWritten = ArrayFilePut (strFileListIn, arrFiles)

Drop (arrFiles)

strMsgText = "Files found: " : intF : @LF : @LF : "Ready."

BoxButtonDraw (1, 1, "&OK", "50,780,950,950")
BoxText (strMsgText)
While !BoxButtonStat (1, 1)
   TimeDelay (0.2)
EndWhile
BoxButtonKill (1, 1)
BoxShut ()

Drop (strMsgTitle1, strMsgTitle, strMsgText, strFolderRoot, strFileFullName, intM, intF, intD)
Drop (intDisksLast, intMasksLast, intBytesWritten, hdlFAF, fafRecurse, fafHidden)
Drop (arrMasks, arrFiles, arrDisks, strPath)


;----------------------------
:Step2 ; Examine TIF files.
;----------------------------

IntControl (73, 2, @TRUE, "", 0) ; On error gosub the label :WBERRORHANDLER.

strMsgTitle = "Step 2 | TIF | PageCount | ByteOrder"
strMsgText = ""
BoxOpen (strMsgTitle, strMsgText)
WinPlace (200, 200, 600, 700, "")
BoxDataTag (1, 1)

arrFiles = ArrayFileGetCSV (strFileListIn, 0, "|")
intFiles = ArrInfo (arrFiles, 1)
intFilesLast = intFiles - 1

; Column 2 contains FullFilename.
ArrayInsert (arrFiles, 0, 2) ; Column 1 contains ByteOrder.
ArrayInsert (arrFiles, 0, 2) ; Column 0 contains PageCount.

For intF = 0 To intFilesLast
   arrResult = udfTIFPageCount (arrFiles[intF, 2], 1)
   arrFiles[intF, 0] = arrResult[0]
   arrFiles[intF, 1] = arrResult[1]

   strMsgText = intFiles : "/" : 1 + intF : @LF : @LF : "PageCount = " : arrFiles[intF, 0] : @LF : "ByteOrder = " : arrFiles[intF, 1] : @LF : "Filename = ..." : @LF : udfPathFold (arrFiles[intF, 2])
   BoxText (strMsgText)
   BoxDataClear (1, 1)
Next

intBytes = ArrayFilePutCSV (strFileListOut, arrFiles, "|")

BoxButtonDraw (1, 1, "&OK", "50,780,950,950")
BoxText (strMsgText)
While !BoxButtonStat (1, 1)
   TimeDelay (0.2)
EndWhile
BoxButtonKill (1, 1)
BoxShut ()

If intBytes Then Run (strFileListOut, "")

:CANCEL
Exit

:WBERRORHANDLER
IntControl (73, 2, @TRUE, "", 0) ; On error gosub the label :WBERRORHANDLER.
Return
;==========================================================================================================================================