Page 1 of 1

Basic Read of EXIF data

Posted: Sun Mar 23, 2025 1:16 am
by Redlion
Hello All,

Here is a proof of concept code for reading EXIF information from Jpeg images. It is a bit long winded as not all image tags follow the EXIF standard so I wanted to be able to show the raw data as well as the decoded tag info.

There is still a lot to do, I would like to be able to edit a tag so I can develop my Image database ( I would like to be able to search images for locations (GPS) or info I have embedded into the image so that I could display all the images from Xmas 2024 or all pictures with animals etc.)
I would like to be able to read the info from images taken on my mobile phone but I have a few other thing to get right first. ( if anyone has already done that please let me know. )

The Preference file is just a text file with the name of the directory where your images are stored. ( easy to create )

I know that the math can be improved and that it could be optimised, as I said its a proof of concept. It works on SAM 460 and my PC albeit with some format issues.

Feel free to use it in your project but a bit of credit would be nice.

So here it is, have fun and help me improve it.

Code: Select all

/*** Information about this app *******************************************************************/
@APPTITLE "EXIF Reader"
@APPVERSION "$VER: 1.0.0  (02.01.25)"
@APPCOPYRIGHT "(C) 2025 by Leo den Hollander"
@APPAUTHOR "Leo den Hollander"
@APPDESCRIPTION "EXIF Reader"

;*** EXIF Tag references *************************************************************************************
; https://exiftool.org/TagNames/EXIF.html
; https://www.media.mit.edu/pia/Research/deepview/exif.html
; https://web.archive.org/web/20190624045241if_/http://www.cipa.jp:80/std/documents/e/DC-008-Translation-2019-E.pdf 

; *** Enable DPI-awareness so that our GUI looks sharp even on high DPI monitors *****************************
@OPTIONS {DPIAware = True}

; ***  Setup ESC to exit program *****************************************************************************
EscapeQuit(True)

; *** This script requires the RapaGUI plugin ****************************************************************
@REQUIRE "RapaGUI", {Link = True}

; ***  Define Global Variables *******************************************************************************
Global Next_OffSet
Global IDFOffSet
Global TName$
Global THex$
Global TDec$

; ***  Setup ESC EXIF tag Table ******************************************************************************
Dim TagData[32]

; *** Setup Graphics for Picture Display *********************************************************************
CreateBrush(2, 400, 300, #BLACK)

; *** Setup GUI **********************************************************************************************
moai.CreateApp([[<?xml version="1.0" encoding="iso-8859-1"?>
<application id="app">
  <window id="mainwin" title=" EXIF Veiwer " margin="0" width="1280" height="768" notify="closerequest">
	<vgroup color="#99D2CB">
	  <vgroup>
		<vspace height="5"/>
		<hgroup>
		  <hspace Width="5"/>
		  <vgroup frame="1">
			<text>Image Files</text>
			<listview Id="files" Notify="Active; DoubleClick">
			  <column Title=" File Name " width="100"/>
			  <column Title=" File Type " width="50"/>
			  <column Title= "File Size " width="70"/>
			</listview>
		  </vgroup>
		  <vspace height="5"/>
		  <vgroup frame="1">
			<text>Registered Tags</text>
			<vgroup weight="80">
			  <listview Id="tags">
				<column Title=" Tag No " width="50"/>
				<column Title=" Tag Name "/>
				<column Title=" Tag Data "/>
			  </listview>
			</vgroup>
			<text>Unregistered Tags ... </text>
			<vgroup weight="20">
			  <listview Id="noinfo">
				<column Title=" Tag Id " width="50"/>
				<column Title=" Tag No "/>
			  </listview>
			</vgroup>
		  </vgroup>
		  <vgroup Frame="1">
			<text>Image</text>
			<hgroup>
			  <hollywood display="1" width="400" Height="300"/>
			</hgroup>
			<vspace height="5"/>
			<text>File Load Tag Data Summary ... </text>
			<texteditor Id="filetype" height="400"></texteditor>
			<vspace height="5"/>
			<hgroup>
			  <button ID="clear"> Clear </button>
			  <hspace Width="5"/>
			  <button ID="preferences"> Preferences </button>
			</hgroup>
		  </vgroup>          
		</hgroup>   
		<vspace height="5"/>        
		</vgroup>    
	  </vgroup> 
  </window>

  <window Id="prefs" Title=" EXIF Veiwer Preferences" Margin="0" Width="700" Height="300" Closegadget="false" 
			  Sizegadget="false" Minimizegadget="false" Maximizegadget="false" Open="false">
	<vgroup color="#99D2CB" frame="1">
	  <hspace width="10"/>
	  <vgroup frame="1">
	   <vspace height="10"/> 
	   <text align="center"> Please select the Default Directory for Startup.</text>
	   <vspace height="10"/> 
		<hgroup> 
		  <hspace width="10"/>
		  <text Weight="10">Default Directory</text>
		  <poppath Id="examine"></poppath>
		  <hspace width="10"/>
		</hgroup> 
		<vspace height="10"/>      
	  </vgroup>   
	  <hgroup frame="1">
		<hspace width="10"/>
		<button ID="save"> Save Directory </button>
		<hspace Width="80"/>
		<button ID="restore"> Restore Directory </button>
		<hspace Width="80"/>
		<button ID="cancel"> Cancel Preferences </button>
		<hspace width="10"/>
		</hgroup> 
		<vspace height="5"/>
	</vgroup> 
  </window> 

</application>
]])

; *** Load Program Preferences *******************************************************************************
Function F_Load_Preferences()
	OpenFile(2, "Exif-Prefs.txt", #MODE_READ)
		Global ProgDir$ = ReadLine(2)
	CloseFile(2)
EndFunction

; *** Load Picture Files *************************************************************************************
Function F_Load_Pictures()
	dd = OpenDirectory(Nil, ProgDir$)
	e = NextDirectoryEntry(dd)
	While e <> Nil
		If e.type = #DOSTYPE_FILE
			If LowerStr(RightStr(e.name, 3)) = "jpg" Or LowerStr(RightStr(e.name, 3)) = "tif"
				moai.DoMethod("files", "Insert", "Bottom", e.name, RightStr(e.name, 3), e.size )
			EndIf
		EndIf
		e = NextDirectoryEntry(dd)
	Wend
	CloseDirectory(dd)
EndFunction

; *** Clear all Lists **********************************************************************************
Function F_Clear_All()
	moai.DoMethod("filetype", "Clear")
	moai.DoMethod("tags", "Clear")
	moai.DoMethod("noinfo", "Clear")
	DisplayBrush(2, 0, 0)
EndFunction

; *** Get Tag Name ***************************************************************************
Function Check_Tag()
  Switch TagNo
	Case 270
	  TagName = " Description: "
	  F_Get_2()
	Case 271
	  TagName =  " Make: "
	  F_Get_2()
	Case 272
	  TagName = " Model: "
	  F_Get_2()
	Case 273
	  TagName = " Strip Offsets: "
	  F_Get_2()
	Case 274
	  TagName =  " Orientation: "
	  TagData[0] = " Unknown" 
	  TagData[1] = " Horizontal (normal)" 
	  TagData[2] = " Mirror horizontal" 
	  TagData[3] = " Rotate 180" 
	  TagData[4] = " Mirror vertical" 
	  TagData[5] = " Mirror horizontal and rotate 270 CW" 
	  TagData[6] = " Rotate 90 CW"
	  TagData[7] = " Mirror horizontal and rotate 90 CW" 
	  TagData[8] = " Rotate 270 CW"
	  A$ = TagData[Dat]
	Case 277
	  TagName = " Sample Per Pixel: "
	  F_Get_2()
	Case 278
	  TagName = " Rows per Strip: "
	  F_Get_2()
	Case 282
	  TagName = " Res X: "
	  F_Get_5()
	Case 283
	  TagName = " Res Y: "
	  F_Get_5()
	Case 296
	  TagName = " Res-Units: "
	  TagData[0] = " Unknown" 
	  TagData[1] = " None" 
	  TagData[2] = " inches" 
	  TagData[3] = " cm"
	  A$ = TagData[Dat]
	Case 305
	  TagName = " CreatorTool: "
	  F_Get_2()
	Case 306
	  TagName = " ModifyDate: "
	  F_Get_2()
	Case 531
	  TagName = " YCbCrPositioning: "
	  TagData[0] = " Unknown"
	  TagData[1] = " Centered"
	  TagData[2] = " Co-sited"
	  A$ = TagData[Dat]
	Case 33434
	  TagName = " Exposure Time: "
	  Seek(1, dat + 12)
	  A = 0
	  D = 0
	  B1 = ReadByte(1, #ENCODING_UTF8)
	  B2 = ReadByte(1, #ENCODING_UTF8)
	  B3 = ReadByte(1, #ENCODING_UTF8)
	  B4 = ReadByte(1, #ENCODING_UTF8)
	  C1 = ReadByte(1, #ENCODING_UTF8)
	  C2 = ReadByte(1, #ENCODING_UTF8)
	  C3 = ReadByte(1, #ENCODING_UTF8)
	  C4 = ReadByte(1, #ENCODING_UTF8)
	  If ByteChr(P12) .. ByteChr(P13) = "MM"
		A = (B1*1024) + (B2*512) + (B3*256) + B4
		D = (C1*1024) + (C2*512) + (C3*256) + C4
		D$ = Val(D/A)
	  Else 
		A = (B4*1024) + (B3*512) + (B2*256) + B1
		D = (C4*1024) + (C3*512) + (C2*256) + C1
		D$ = Val(D/A)
	  EndIf
	  A$ =  "1/" .. D$
	Case 33437
	  TagName = " F Number: "
	  Seek(1, dat + 12)
	  A = 0
	  D = 0
	  B1 = ReadByte(1, #ENCODING_UTF8)
	  B2 = ReadByte(1, #ENCODING_UTF8)
	  B3 = ReadByte(1, #ENCODING_UTF8)
	  B4 = ReadByte(1, #ENCODING_UTF8)
	  C1 = ReadByte(1, #ENCODING_UTF8)
	  C2 = ReadByte(1, #ENCODING_UTF8)
	  C3 = ReadByte(1, #ENCODING_UTF8)
	  C4 = ReadByte(1, #ENCODING_UTF8)
	  A = (B4*1024) + (B3*512) + (B2*256) + B1
	  D = (C4*1024) + (C3*512) + (C2*256) + C1
	  D$ = Val(A/D)
	  A$ =  "1/" .. D$
	Case 34665
	  TagName = " EXIF Sub IFD Offset: "
	  F_Get_4()
	  IDFOffSet = Dat
	Case 34850
	  TagName = " Exposure Program: "
	  TagData[0] = " Not Defined" 
	  TagData[1] = " Manual" 
	  TagData[2] = " Program AE" 
	  TagData[3] = " Aperture-priority AE" 
	  TagData[4] = " Shutter speed priority AE" 
	  TagData[5] = " Creative (Slow speed)" 
	  TagData[6] = " Action (High speed)" 
	  TagData[7] = " Portrait" 
	  TagData[8] = " Landscape" 
	  TagData[9] = " Bulb"
	  A$ = TagData[Dat]
	Case 34855
	  TagName = " ISO Speed Rating: "
	  F_Get_3()
	Case 34864
	  TagName = " Sensitivity Type:"
	  TagData[0] = " Unknown"
	  TagData[1] = " Standard Output Sensitivity"
	  TagData[2] = " Recommended Exposure Index" 
	  TagData[3] = " ISO Speed" 
	  TagData[4] = " Standard Output Sensitivity and Recommended Exposure Index" 
	  TagData[5] = " Standard Output Sensitivity and ISO Speed" 
	  TagData[6] = " Recommended Exposure Index and ISO Speed"
	  TagData[7] = " Standard Output Sensitivity, Recommended Exposure Index and ISO Speed"
	  A$ = TagData[Dat]
	Case 36864
	  TagName = " Exif Version: "
	  F_Get_2()
	Case 36867
	  TagName = " Date Time Original: "
	  F_Get_2()
	Case 36868
	  TagName = " Date Time Digitized: "
	  F_Get_2()
	Case 37121
	  TagName = " Component Config: "
	  TagData[0] = "-"
	  TagData[1] = "Y" 
	  TagData[2] = "Cb" 
	  TagData[3] = "Cr"
	  TagData[4] = "R"
	  TagData[5] = "G" 
	  TagData[6] = "B"
	  A$ = TagData[T8] .. TagData[T9] .. TagData[T10] 
	Case 37122
	  TagName = " Compressed Bit Per Pixel: "
	  F_Get_5()
	  A$ =  A$ .. "/1"
	Case 37377
	  TagName = " Shutter Speed Value: "
	  Seek(1, dat + 12) 
	  A = 0
	  D = 0
	  B1 = ReadByte(1, #ENCODING_UTF8)
	  B2 = ReadByte(1, #ENCODING_UTF8)
	  B3 = ReadByte(1, #ENCODING_UTF8)
	  B4 = ReadByte(1, #ENCODING_UTF8)
	  C1 = ReadByte(1, #ENCODING_UTF8)
	  C2 = ReadByte(1, #ENCODING_UTF8)
	  C3 = ReadByte(1, #ENCODING_UTF8)
	  C4 = ReadByte(1, #ENCODING_UTF8)
	  A = (B4*1024) + (B3*512) + (B2*256) + B1
	  D = (C4*1024) + (C3*512) + (C2*256) + C1
	  D$ = Val(D/A)
	  A$ =  "1/" .. D$
	Case 37378
	  TagName = " Aperture Value: "
	  F_Get_10()
	Case 37379
	  TagName = " Brightness Value: "
	  F_Get_10()
	  A = TrimStr(A, "0", 1)
	  A = TrimStr(A, "0", 0)
	  If Val(A) <> 0
		A$ = "EV" .. A/10
	  Else
		A$ = "EV" .. "0.0"
	  EndIf
	Case 37380
	  TagName = " Exposure Bias Value: "
	  F_Get_10()
	  A = TrimStr(A, "0", 1)
	  A = TrimStr(A, "0", 0)
	  If Val(A) <> 0
		A$ = "EV" .. A/10
	  Else
		A$ = "EV" .. "0.0"
	  EndIf
	Case 37381
	  TagName = " Max Aperture Value: "
	  Seek(1, dat + 12)
	  A = 0
	  D = 0
	  B1 = ReadByte(1, #ENCODING_UTF8)
	  B2 = ReadByte(1, #ENCODING_UTF8)
	  B3 = ReadByte(1, #ENCODING_UTF8)
	  B4 = ReadByte(1, #ENCODING_UTF8)
	  C1 = ReadByte(1, #ENCODING_UTF8)
	  C2 = ReadByte(1, #ENCODING_UTF8)
	  C3 = ReadByte(1, #ENCODING_UTF8)
	  C4 = ReadByte(1, #ENCODING_UTF8)
	  A = (B4*1024) + (B3*512) + (B2*256) + B1
	  D = (C4*1024) + (C3*512) + (C2*256) + C1
	  D$ = Val(A/D)
	  A$ =  "1/" .. D$
	Case 37382
	  TagName = " Subject Distance: "
	  F_Get_3()
	Case 37383
	  TagName = " Metering Mode: "
	  TagData[0] = " Unknown" 
	  TagData[1] = " Average" 
	  TagData[2] = " Center-weighted average" 
	  TagData[3] = " Spot" 
	  TagData[4] = " Multi-spot" 
	  TagData[5] = " Multi-segment" 
	  TagData[6] = " Partial" 
	  TagData[7] = " 255 = Other"
	  If Dat > 7 Then Dat = 7
	  A$ = TagData[Dat]
	Case 37384
	  TagName =  " Light Source: "
	  TagData[0] = " Unknown" 
	  TagData[1] = " Daylight" 
	  TagData[2] = " Fluorescent" 
	  TagData[3] = " Tungsten" 
	  TagData[4] = " Flash"
	  TagData[5] = " Fine Weather: "
	  TagData[6] = " Cloudy" 
	  TagData[7] = " Shade" 
	  TagData[8] = " Daylight Fluorescent" 
	  TagData[9] = " Day White Fluorescent" 
	  TagData[10] = " Cool White Fluorescent"
	  TagData[11] = " White Fluorescent: "
	  TagData[12] = " Warm White Fluorescent" 
	  TagData[13] = " Standard Light A" 
	  TagData[14] = " Standard Light B" 
	  TagData[15] = " Standard Light C" 
	  TagData[16] = " Fluorescent"
	  If Dat > 167 Then Dat = 0
	  A$ = TagData[Dat]
	Case 37385
	  TagName = " Flash: "
	  TagData[0] = " No Flash" 
	  TagData[1] = " Fired" 
	  TagData[5] = " Fired, No Return" 
	  TagData[7] = " Fired, Return" 
	  TagData[8] = " On, Did not Fire"
	  TagData[9] = " On, Fired "
	  TagData[13] = " On, Return not detected" 
	  TagData[15] = " On, return Detected" 
	  TagData[16] = " Off, Did not Fire" 
	  TagData[17] = " Off, Did Not Fire" 
	  TagData[18] = " Auto, Did not Fire"
	  TagData[19] = " Auto, Fired "
	  A$ = TagData[Dat] 
	Case 37386
	   TagName = " Focal Length: "
	   F_Get_5()
	   A$ = A$ .."mm"
	Case 37396
	  TagName = " Subject Area: "
	  Seek(1, dat + 12)
	  A = 0
	  D = 0
	  B1 = ReadByte(1, #ENCODING_UTF8)
	  B2 = ReadByte(1, #ENCODING_UTF8)
	  B3 = ReadByte(1, #ENCODING_UTF8)
	  B4 = ReadByte(1, #ENCODING_UTF8)
	  C1 = ReadByte(1, #ENCODING_UTF8)
	  C2 = ReadByte(1, #ENCODING_UTF8)
	  C3 = ReadByte(1, #ENCODING_UTF8)
	  C4 = ReadByte(1, #ENCODING_UTF8)
	   A$ = (B2 * 256) + B1..","..(B4 * 256) + B3..","..(C2 * 256) + C1..","..(C4 * 256) + C3
	Case 40960
	  TagName = " Flash Pix Version: "
	  F_Get_7()
	Case 40961
	  TagName = " Colour Space: "
	  TagData[0] = " Unknown "
	  TagData[1] = " sRGB "
	  A$ = TagData[Dat]
	Case 40962
	  TagName = " Exif Image Width: "
	  A$ = Dat
	Case 40963
	  TagName = " Exif Image Height: "
	  A$ = Dat
	Case 40964
	  TagName = " Related Sound File: "
	  F_Get_2()
	Case 40965
	  TagName = " Exif Interoperablity Offset: "
	  F_Get_4()
	Case 41486
	  TagName = " Focal Plane X Resolution: "
	Case 41487
	  TagName = " Focal Plane Y Resolution: "
	Case 41488
	  TagName = " Focal plane Resolution Unit: "
	  TagData[0] = " None" 
	  TagData[1] = " inches" 
	  TagData[2] = " cm" 
	  TagData[3] = " mm" 
	  TagData[4] = " um"
	  A$ = TagData[Dat]
	Case 41495
	  TagName = " Sensing Method: "
	  TagData[0] = " Not defined" 
	  TagData[1] = " One-chip color area" 
	  TagData[2] = " Two-chip color area" 
	  TagData[3] = " Three-chip color area" 
	  TagData[4] = " Color sequential area" 
	  TagData[5] = " Trilinear"
	  TagData[6] = " Color sequential linear"
	  A$ = TagData[Dat]
	Case 41728
	  TagName = " File Source: "
	  TagData[0] = " Unknown" 
	  TagData[1] = " Film Scanner" 
	  TagData[2] = " Reflection Film Camera"
	  TagData[3] = " Digital Camera" 
	  If ByteChr(P12) .. ByteChr(P13) = "MM"
		Dat = Val((T9 * 256) + T8) 
	  EndIf
	  A$ = TagData[Dat]
	Case 41729
	  TagName = " Scene Type: "
	  TagData[0] = " Unknown" 
	  TagData[1] = " Directly Photographed"
	  If ByteChr(P12) .. ByteChr(P13) = "MM"
		Dat = Val((T9 * 256) + T8) 
	  EndIf
	  A$ = TagData[Dat]
	Case 41985
	  TagName = " Custom Rendered:"
	  TagData[0] =  " Unknown"
	  TagData[1] =  " Custom Process"
	  TagData[2] =  " Custom "
	  A$ = TagData[Dat]
	Case 41986
	  TagName = " Exposuer Mode:"
	  TagData[0] = " Auto"
	  TagData[1] = " Manual"
	  TagData[2] = " Auto Bracket"
	  A$ = TagData[Dat]
	Case 41987
	  TagName = " White balance:"
	  TagData[0] = " Auto"
	  TagData[1] = " Manual"
	  A$ = TagData[Dat]
	Case 41988
	  TagName = " Digital Zoom Ratio:"
	  Seek(1, dat + 12) 
	  A = 0
	  D = 0
	  B1 = ReadByte(1, #ENCODING_UTF8)
	  B2 = ReadByte(1, #ENCODING_UTF8)
	  B3 = ReadByte(1, #ENCODING_UTF8)
	  B4 = ReadByte(1, #ENCODING_UTF8)
	  C1 = ReadByte(1, #ENCODING_UTF8)
	  C2 = ReadByte(1, #ENCODING_UTF8)
	  C3 = ReadByte(1, #ENCODING_UTF8)
	  C4 = ReadByte(1, #ENCODING_UTF8)
	  A = (B4*1024) + (B3*512) + (B2*256) + B1
	  D = (C4*1024) + (C3*512) + (C2*256) + C1
	  D$ = Val(D)
	  A$ =  "0/" .. D$
	Case 41989
	  TagName = " Focal Length (35 mm Format):"
	  F_Get_3()
	  A$ = A$ .. "mm"
	Case 41990
	  TagName = " Scene Capture Type:"
	  TagData[0] = " Standard"
	  TagData[1] = " Landscape"
	  TagData[2] = " Portrait"
	  TagData[3] = " Night"
	  TagData[4] = " Other"
	  A$ = TagData[Dat]
	Case 41991
	  TagName = " Gain Control:"
	  TagData[0] = " None:"
	  TagData[1] = " Low gain up"
	  TagData[2] = " High gain up"
	  TagData[3] = " Low gain down"
	  TagData[4] = " High gain down"
	  A$ = TagData[Dat]
	Case 41992
	  TagName = " Contrast:"
	  TagData[0] = " Normal"
	  TagData[1] = " Low"
	  TagData[2] = " High"
	  A$ = TagData[Dat]
	Case 41993
	  TagName = " Saturation:"
	  TagData[0] = " Normal"
	  TagData[1] = " Low"
	  TagData[2] = " High"
	  A$ = TagData[Dat]
	Case 41994
	  TagName = " Sharpness:"
	  TagData[0] = " Normal"
	  TagData[1] = " Soft"
	  TagData[2] = " Hard"
	  A$ = TagData[Dat]
	Case 41995
	  TagName = " Device Setting Description:"
	Case 41996
	  TagName = " Subject Distance Range:"
	  TagData[0] = " Unknown"
	  TagData[1] = " Macro"
	  TagData[2] = " Close"
	  TagData[3] = " Distant"
	  A$ = TagData[Dat]
	Case 42016
	  TagName = " Image Unique ID:"
	Case 42032
	  TagName = " Owner Name:"
	Case 42033
	  TagName = " Body Serial Number:"
	Case 42034
	  TagName = " Lens Info:"
	Case 42035
	  TagName = " Lens Make:"
	Case 42036
	  TagName = " Lens Model:"
	Case 42037
	  TagName = " Lens Serial Number:"
	Case 42038
	  TagName = " Image Title:"
	Case 42039
	  TagName = " Photgrapher :"
	Case 42040
	  TagName = " Image Editor:"
	Case 50341
	  TagName = " PrintIM OffSet: "
	  F_Get_7()
	Default
	  moai.DoMethod("noinfo", "Insert", "Bottom", HexStr(TagNo), TagNo)
  EndSwitch
  
  If TagName <> ""
	moai.DoMethod("filetype", "Insert", Chr(13) .. "\27bTag " .. HexStr(TagNo) .. " (" .. TagNo .. ") :- " .. TagName$ ..
				"Type: " .. Type .. " Number: " .. Numb .. " Data: " .. Dat, "Bottom") 
	moai.DoMethod("tags", "Insert", "Bottom", TagNo, TagName, A$)
  EndIf
  TagName$ = ""
  A$ = ""       

EndFunction  

; *** Read Tag 2 *****************************************************************************
Function F_Get_2()
	If Numb > 5
	If Numb > 1024 Then Numb = 1024
		Seek(1, dat + 12)
		A$ = ""
		For t = 1 To numb
			B = ReadByte(1, #ENCODING_UTF8)
			B$ = ByteChr(B)
			A$ = A$ .. B$ 
		Next
	Else
		A$ = ByteChr(T8) .. ByteChr(T9) .. ByteChr(T10) .. ByteChr(T11)
	EndIf 
	D$ = A$ 
EndFunction 

; *** Read Tag 3 **********************************************************************************
Function F_Get_3()
	A$ = Dat        
EndFunction 

; *** Read Tag 4 **********************************************************************************
Function F_Get_4()
	A$ = Dat
EndFunction

; *** Read Tag 5 **********************************************************************************
Function F_Get_5()
  Seek(1, dat + 12)
  A = 0
  D = 0
  For Local t = 1 To (numb * 4)
	B = ReadByte(1, #ENCODING_UTF8)
	A = A + B
  Next
  A$ = Val(A)
  For Local t = 1 To (numb * 4)
	C = ReadByte(1, #ENCODING_UTF8)
	D = D + C
  Next
  C$ = Val(D)
  D$ = C$ .. "/" .. A$
EndFunction 

; *** Read Tag 7 **********************************************************************************
Function F_Get_7()
  Seek(1, dat + 12)
  A$ = ""
  AA = 0
  For Local t = 1 To numb
	 B = ReadByte(1, #ENCODING_UTF8)
	 AA = AA + Val(B)
  Next
  A$ = AA
EndFunction

; *** Read Tag Type 10 **********************************************************************************
Function F_Get_10()
	Seek(1, dat + 12)
	A = 0
	For t = 1 To 4
	   B = ReadByte(1, #ENCODING_UTF8)
	   B$ = ByteChr(B)
	   A = A .. B
	Next
	A$ = A
EndFunction

; *** Read EXIF Header Tags **********************************************************************************
Function F_Read_Exif(SelectedFile$)
	Counter = 0
	A = 0
	A$ = ""
	IDFOffSet = 0
	OpenFile(1, SelectedFile$)
		P0 = ReadByte(1) 
		P1 = ReadByte(1) 
		If P0 = 255 And P1 = 216
		  moai.DoMethod("filetype", "insert", "\27bSOI Marker found. " .. HexStr(P0) .. " " .. 
						  HexStr(P1) .. Chr(13), "Bottom") 
		  P2 = ReadByte(1) 
		  P3 = ReadByte(1) 
		  If P2 = 255 And P3 = 225
			moai.DoMethod("filetype", "insert", "\27bAPP1 Marker found. " .. 
						  HexStr(P2) .. " " .. HexStr(P3) .. Chr(13), "Bottom")
			P4 = ReadByte(1) 
			P5 = ReadByte(1) 
			moai.DoMethod("filetype", "insert", " APP1 Marker size :  " .. HexStr(P4) .. " " .. 
					  HexStr(P5) .. Chr(13), "Bottom")
			moai.DoMethod("filetype", "insert", "\27bExif Header Found. " .. Chr(13), "Bottom")
			P6 = ReadByte(1) 
			P7 = ReadByte(1) 
			P8 = ReadByte(1) 
			P9 = ReadByte(1) 
			moai.DoMethod("filetype", "Insert", " " .. ByteChr(P6) .. ByteChr(P7) .. ByteChr(P8) .. 
					  ByteChr(P9) .. " : ", "Bottom")
			moai.DoMethod("filetype", "Insert", " " .. HexStr(P6) .. HexStr(P7) .. HexStr(P8) .. 
					  HexStr(P9) .. Chr(13), "Bottom")
			P10 = ReadByte(1) 
			P11 = ReadByte(1)  
			moai.DoMethod("filetype", "insert", "\27bTIFF Header found. " .. Chr(13), "Bottom")
			counter = 12
			P12 = ReadByte(1) 
			P13 = ReadByte(1) 
			moai.DoMethod("filetype", "insert", " Align format : " .. Chr(P12) .. 
					  Chr(P13) .. " - ", "Bottom")
			If ByteChr(p12)..ByteChr(P13) = "MM"
			  moai.DoMethod("filetype", "Insert", " Motorola Format" .. Chr(13), "Bottom")
			  P14 = ReadByte(1) 
			  P15 = ReadByte(1) 
			  moai.DoMethod("filetype", "insert", " TAG Marker : " ..  P14 .. P15 .. " : " .. 
						  HexStr(P14) .. " " .. HexStr(P15) .. Chr(13), "Bottom")
			  P16 = ReadByte(1) 
			  P17 = ReadByte(1) 
			  Off_H = ReadByte(1) 
			  Off_L = ReadByte(1)
			  moai.DoMethod("filetype", "insert", " Offset to IFD0 : " ..  Off_H .. Off_L .. " : " .. 
						  HexStr(Off_H) .. " " .. HexStr(Off_l) .. Chr(13), "Bottom")
			ElseIf ByteChr(p12) .. ByteChr(P13) = "II"
			  moai.DoMethod("filetype", "Insert", " Intel Format ".. Chr(13), "Bottom")
			  P14 = ReadByte(1) 
			  P15 = ReadByte(1) 
			  moai.DoMethod("filetype", "insert", " TAG Marker : " ..  P15 .. P14 .. " : " .. 
						  HexStr(P15) .. " " .. HexStr(P14) .. Chr(13), "Bottom")
			  Off_L = ReadByte(1) 
			  Off_H = ReadByte(1)               
			  P18 = ReadByte(1) 
			  P19 = ReadByte(1) 
			  moai.DoMethod("filetype", "insert", " Offset to IFD0 : " ..  Off_H .. Off_L .. " : " .. 
						  HexStr(Off_H) .. " " .. HexStr(Off_L) .. Chr(13), "Bottom")
			EndIf
		
			Next_OffSet = Val((Off_H * 256) + Off_L)
			Counter = Counter + Next_OffSet
			Seek(1, Counter)
			moai.DoMethod("filetype", "Insert", Chr(13) .. "\27bIFD Tags:" .. Chr(13), "Bottom")
			If ByteChr(P12) .. ByteChr(P13) = "MM" 
			  NT_H = ReadByte(1) 
			  NT_L = ReadByte(1)           
			ElseIf ByteChr(P12) .. ByteChr(P13) = "II"
			  NT_L = ReadByte(1) 
			  NT_H = ReadByte(1) 
			EndIf
			No_Tags = Val((NT_H * 256) + NT_L)
			moai.DoMethod("filetype", "Insert","\27bNo. of Tags in IFD0 : " .. No_Tags .. 
					   " : \27n" .. HexStr(NT_H) .. HexStr(NT_L) .. Chr(13), "Bottom")
			Counter = Counter + 2

			For NT = 1 To No_Tags
			  T0 = ReadByte(1) 
			  T1 = ReadByte(1) 
			  T2 = ReadByte(1) 
			  T3 = ReadByte(1) 
			  T4 = ReadByte(1) 
			  T5 = ReadByte(1) 
			  T6 = ReadByte(1) 
			  T7 = ReadByte(1)
			  T8 = ReadByte(1) 
			  T9 = ReadByte(1) 
			  T10 = ReadByte(1) 
			  T11 = ReadByte(1)
			  If ByteChr(P12) .. ByteChr(P13) = "MM"
				TagNo = (T0 * 256) + T1 
				Type = Val((T2 * 256) + T3)
				Numb = Val((T6 * 256) + T7)
				Dat = Val((T8 * 256) + T9) + Val((T10 * 256) + T11)
			   ; Dat = Val((T10 * 256) + T11)
			  ElseIf ByteChr(P12) .. ByteChr(P13) = "II"
				TagNo = T0 + (T1 * 256)
				Type = Val(T2 + (T3 * 256))
				Numb = Val(T4 + (T5 * 256))
				Dat = Val(T8 + (T9 * 256))
			  EndIf
			  Check_Tag()
			  Counter = Counter + 12
			  Seek(1, Counter)
			Next
  
			If IDFOffSet > 0
			  F_Read_IFD0()
			EndIf            
			
		  Else
			moai.DoMethod("filetype", "insert", "\27b NO APP1 Marker found. " .. Chr(13), "Bottom")
		  EndIf
		Else
		  moai.DoMethod("filetype", "insert", "\27b NO SOI Marker found. " .. Chr(13), "Bottom")
		EndIf
EndFunction

; *** Read the Tags in IFD0 **********************************************************************
Function F_Read_IFD0()    
	If ByteChr(P12) .. ByteChr(P13) = "MM"
		Seek(1, IDFOffSet + 12)
		NT_H = ReadByte(1) 
		NT_L = ReadByte(1)
		No_Tags =  Val(NT_L + (NT_H * 256)) 
	ElseIf ByteChr(P12) .. ByteChr(P13) = "II"
		Seek(1, IDFOffSet + 12)
		NT_L = ReadByte(1) 
		NT_H = ReadByte(1) 
		No_Tags = Val(NT_L + (NT_H * 256))
	EndIf
	moai.DoMethod("filetype", "Insert",  Chr(13) ..  Chr(13) .. "\27bNo. of Tags in Sub IFD0 : " .. No_Tags .. 
					  " : \27n" .. HexStr(NT_H) .. HexStr(NT_L) .. Chr(13), "Bottom")
	Seek(1, IDFOffSet + 14)
	Counter = IDFOffSet + 14 
	For NT = 1 To No_Tags
	  T0 = ReadByte(1) 
	  T1 = ReadByte(1) 
	  T2 = ReadByte(1) 
	  T3 = ReadByte(1) 
	  T4 = ReadByte(1) 
	  T5 = ReadByte(1) 
	  T6 = ReadByte(1) 
	  T7 = ReadByte(1)
	  T8 = ReadByte(1) 
	  T9 = ReadByte(1) 
	  T10 = ReadByte(1) 
	  T11 = ReadByte(1)

	  If ByteChr(P12) .. ByteChr(P13) = "MM"
		TagNo = (T0 * 256) + T1 
		Type = Val((T2 * 256) + T3)
		Numb = Val((T6 * 256) + T7)
		Dat = Val((T8 * 256) + T9) + Val((T10 * 256) + T11)        
	  ElseIf ByteChr(P12) .. ByteChr(P13) = "II"
		TagNo = T0 + (T1 * 256)
		Type = Val(T2 + (T3 * 256))
		Numb = Val(T4 + (T5 * 256))
		Dat = Val(T8 + (T9 * 256))
	  EndIf
	  
	  Check_Tag()

	  Counter = Counter + 12
	  Seek(1, Counter)
	Next
EndFunction

; *** Read the Tags in PrintIM **********************************************************************
Function F_Read_PrintIM()    
	If ByteChr(P12) .. ByteChr(P13) = "MM"
		Seek(1, PCounter + PrintIMOffSet + 12)
		NT_H = ReadByte(1) 
		NT_L = ReadByte(1)
		No_Tags =  Val(NT_L + (NT_H * 256)) 
	ElseIf ByteChr(P12) .. ByteChr(P13) = "II"
		Seek(1, PCounter + PrintIMOffSet + 12)
		NT_L = ReadByte(1) 
		NT_H = ReadByte(1) 
		No_Tags = Val(NT_L + (NT_H * 256))
	EndIf
	moai.DoMethod("filetype", "Insert", "\27bNo. of Tags in PrintIM IFD : " .. No_Tags .. 
					  " : \27n" .. HexStr(NT_H) .. HexStr(NT_L) .. Chr(13), "Bottom")
	Seek(1, PintIMOffSet + 14)
	PCounter = PrintIMOffSet + 14 

	For NT = 1 To No_Tags
	  T0 = ReadByte(1) 
	  T1 = ReadByte(1) 
	  T2 = ReadByte(1) 
	  T3 = ReadByte(1) 
	  T4 = ReadByte(1) 
	  T5 = ReadByte(1) 
	  T6 = ReadByte(1) 
	  T7 = ReadByte(1)
	  T8 = ReadByte(1) 
	  T9 = ReadByte(1) 
	  T10 = ReadByte(1) 
	  T11 = ReadByte(1)

	  If ByteChr(P12) .. ByteChr(P13) = "MM"
		TagNo = (T0 * 256) + T1 
		Type = Val((T2 * 256) + T3)
		Numb = Val((T6 * 256) + T7)
		Dat = Val((T10 * 1024) + (T11 * 512)) + Val((T8 * 256) + T9) 
	  ElseIf ByteChr(P12) .. ByteChr(P13) = "II"
		TagNo = T0 + (T1 * 256)
		Type = Val(T2 + (T3 * 256))
		Numb = Val(T4 + (T5 * 256))
		Dat = Val(T8 + (T9 * 256)) 
	  EndIf
			
	  F_Get_Tag_Name()

	  moai.DoMethod("filetype", "insert", "\27b NO APP1 Marker found. " .. Chr(13), "Bottom")
	  moai.DoMethod("filetype", "insert", "\27b NO SOI Marker found. " .. Chr(13), "Bottom")
	Next
EndFunction

/***********************************************************************************************************/
/*** RAPAGUI Events ****************************************************************************************/
/***********************************************************************************************************/
Function Rapa_Events(msg)
  
	Switch msg.action
		Case "RapaGUI"
	 
		Switch msg.attribute
			Case "Pressed"
			
				Switch msg.id
					Case "clear"
						F_Clear_All()

					Case "preferences"
					  moai.Set("prefs", "Open", True)
					  moai.Set("examine", "Path", ProgDir$)

					Case "save"
					  ProgDir$ = moai.Get("examine", "Path")
					  OpenFile(2, "Exif-Prefs.txt", #MODE_WRITE)
						WriteLine(2, ProgDir$) 
					  CloseFile(2)
					  moai.DoMethod("Files", "Clear")
					  F_Load_Pictures()
					  moai.Set("prefs", "Open", False)

					Case "restore"
					  moai.Set("examine", "Path", "")
					  moai.Set("examine", "Path", ProgDir$)

					Case "cancel"
					  moai.Set("prefs", "Open", False)
				
				EndSwitch
			Case "Active":
				Switch msg.id
					Case "files"
						Pos = moai.Get("files", "Active")
						Col1$, Col2$, Clo3$ = moai.DoMethod("files", "GetEntry", Pos)                       
				EndSwitch

			Case "DoubleClick":
				Switch msg.id
					Case "files"
						Pos = moai.Get("files", "Active")
						SelectedFile$, Col2$, Col3$  = moai.DoMethod("files", "GetEntry", Pos)
						SelectedFile$ = ProgDir$ .. SelectedFile$
						F_Clear_All()
						F_Read_Exif(SelectedFile$)
						CloseFile(1)
						LoadBrush(1, SelectedFile$)
						DisplayBrush(1, 0, 0, {Width=400, Height=300})

				EndSwitch
				  
			Case "CloseRequest"
				Switch msg.id
					Case "mainwin"
						moai.Set("mainwin", "Open", False)
						End                     
			
				EndSwitch

		EndSwitch
	EndSwitch     
EndFunction

; *** Listen to these events *********************************************************************************
InstallEventHandler({RapaGUI = Rapa_Events})

; *** Program Setup ******************************************************************************************
F_Load_Preferences()
F_Load_Pictures()

; *** Main Loop **********************************************************************************************
Repeat
  WaitEvent
Forever
Cheers
Leo