Reading EXIF information requires a simple understanding of JPEG and EXIF format
JEGP format See Yunfeng's article http://blog.9cbs.net/glock18/Archive/2004/09/05/95268.aspx
EXIF format, please check the raptor's article http://dev.9cbs.net/Article/27/27594.shtm
A class that reads EXIF information is given below:
Const JPEG_BEGIN = $ FFD8; JPEG_APP1 = $ FFE1; JPEG_END = $ FFD9; JPEG_EXIF = (Byte ('E') SHL 24) (Byte ('I') SHL 8) Byte ('f'); byte_odr_i = (Byte ('I') SHL 8) Byte ('I'); Byte_ODR_M = (Byte ('m') SHL 8) Byte ('m');
TYPE U32 = DWORD; u16 = word; s32 = integer; u8 = byte; pifdentry = ^ yifdentry; yifdentry = record U16TAG: U16; U16TYPE: U16; U32Count: U32; U32Value: u32; u32rsize: u32; // data true size --- bytes u32maddr: pchar; // Data in memory SDEScript: STRING; END;
YifdtagInfo = Record Tag: U16; Mean: String;
YExifReader = class (TComponent) private FStream: TFileStream; FTagInfo: Array of YIFDTagInfo; FEntrys: Array of YIFDEntry; FExifBuf: PChar; // store all Exif value memory FBufused: U32; // FInfCount: S32; procedure GetEntryData (var ie : YIFDEntry; TiffHdrPos: U32); public constructor Create (AOwner: TComponent); override; destructor Destroy; override; function ReadExif (JpgFileName: string): string; function ExifInf (i: S32): string; property ExifInfoCount: S32 read FInfCount ;
IMPLEMentation
Uses winsock;
Const exif_attr_def_file = 'evifInf.ini'; CLRF = # 13 # 10;
// Calculate the true size of Entry data Procedure GetENTRYSIZE (VAR IE: Yifdentry); Begin Case IE.U16TYPE OF 1,2,7: i.u32rsize: = 1; 3: IE.U32rsize: = 2; 4, 9: IE.U32rsize: = 4; 5, 10: IE.U32rsize: = 8; Else IE.U32rsize: = 1; End; IE.U32rsize: = IE.U32rsize * IE.U32count; end; // Get an Entry meaning Procedure GeTRYDEScript (Var IE: Yifdentry; Const Iti: Array Of YifdtagInfo); VAR I: U16; B: Bool; Begin B: = False; for i: = 0 to Length (Iti) -1 Do Begin if IE.u16tag = ITI [i] .tag the beginning: = iti [i] .mean; b: = true; Break; end;
IF not b1n ie.sdescript: = format ('unknow tag:% .4x', [IE.u16tag]);
Constructor Yexifreader.create (Aowner: Tcomponent); var s: string; i, j: s32; lst: tstringlist; begin inherited create (aowner); fstream: = nil; fxifbuf: = allocmem (1024 * 100); // Allocate 100KB memory spare ... enough to store all information
S: = extractFilePath (paramstr (0)); if s [length (s)] <> '/' TEN S: = S '/';
LST: = TSTRINGLIST.CREATE; LST.LOADFROMFILE (S EXIF_ATTR_DEF_FILE);
For i: = 0 to lst.count-1 do begin s: = Lst.Strings [i]; if s [1] <> ';' Then Begin FTAGINFO, Length (ftagInfo) 1); J: = POS ('', s); ftaginfo [length (ftagInfo) -1] .tag: = start (COPY (S, 1, J-1)); ftaginfo [length (ftagInfo) -1] .mean: = COPY S, J 1, Length (s) -j); end; end;
DESTRUCTOR YEXIFReader.destroy; Begin IF FSTREAM <> NIL THEN FSTREAM.FREE; FreeMem (fEXIFBUF); inherited destroy;
procedure YExifReader.GetEntryData (var ie: YIFDEntry; TiffHdrPos: U32); begin ie.u32maddr: = FExifBuf FBufUsed; if ie.u32count <= 4 then begin Move (ie.u32value, ie.u32maddr ^, ie.u32rsize); INC (FBUFUSED, IE.U32RSIZE); EXIT; END;
FStream.Position: = TiffHDRPOS IE.U32Value; FStream.read (IE.U32maddr ^, IE.U32RSIZE); Inc (FBUFUSED, IE.U32RSIZE); END;
function YExifReader.ReadExif (JpgFileName: string): string; var i, w, TiffHeaderPos, IFD_0_EntryCount, IFD_1_EntryCount: U16; dw: U32; begin FStream: = TFileStream.Create (JpgFileName, fmOpenRead); FBufUsed: = 0; FInfCount: = 0;
// jepg file header fstream.read (w, sizeof (w)); if jpeg_begin <> ntohs (w) THEN BEGIN RESULT: = 'NOT A JPEG Image.' CLRF; FreeAndnil (FSTREAM); EXIT;
// Search Segment 'App1' --- Exif Attribute Information FStream.read (W, SIZEOF (W)); W: = NTOHS (W); While Jpeg_App1 <> W Do Begin FStream.read (W, Sizeof (W) FSTREAM.SEEK (NTOHS (W) -SizeOf (W), SofromCurrent; FSTREAM.READ (W, SIZEOF (W)); W: = NTOHS (W); if JPEG_END = W THEN BEGIN RESULT: = Result 'NO EXIF INFO.' CLRF; FreeAndnil (FSTREAM); EXIT; End;
Result: = Result 'Segment App1 Found.' CLRF; FSTREAM.READ (W: = NTOHS (W); Result: = Result 'Segment App1 Length:' Format ('% .4x ', [w]) CLRF;
// Search EXIF SIGN --- 'EXIF' FSTREAM.READ (DW, SIZEOF (DW)); DW: = NTOHL (DW); if JPEG_EXIF <> DW THEN BEGIN RESULT: = Result 'No EXIF IN THIS Segment. ' CLRF; FreeAndnil (FSTREAM); EXIT; End; Result: = Result ' Exif Sign Found ... ' CLRF; FSTREAM.READ (W, SizeOf (W)); // Two Zero
// Tiff header Tiffheaderpos: = fstream.position; fstream.read (w, sizeof (w)); if byte_odr_i = w The begin Result: = Result 'Tiff header byte Order is Intel.' CLRF; ELSE BEGIN / / If it is MoTOLORA format, it does not process ~~~~~~ `Result: = Result 'Tiff Header Byte Order Is Motolora.' CLRF; FreeAndnil (FStream); EXIT;
FStream.read (W, SIZEOF (W)); // Tiff file format flag, always 0x002A FSTREAM.READ (DW, SIZEOF (DW)); // The first IFD start position, its offset The calculation of the amount is the starting point FSTREAM.SEEK (DW-8, SOFROMCURRENT) of Tiff Header; // 8 == SIZE OF Image File Header FStream.read (W, SIZEOF (W)); // ifd.0 Entry Count Result : = Result 'Tiff IFD0 Entry Count:' Format ('%. 4x', [W]) CLRF;
IFD_0_ENTRYCOUNT: = W; setLength (FENTRYS, IFD_0_ENTRYCOUNT);
// read ifd entry for i: = 0 to ifd_0_entrycount-1 do begin fstream.read (FENTRYS [I] .U16TAG, SIZEOF (U16)); FSTREAM.READ (FENTRYS [i] .u16Type, Sizeof (U16)); FSTREAM.READ (FENTRYS [i] .u32count, sizeof (u32)); FSTREAM.READ (FENTRYS [I] .U32Value, SizeOf (U32)); GetENTRYSUE (FENTRYS [I]);
// read ifd value for i: = 0 TO IFD_0_ENTRYCOUNT-1 Do Begin GetEntryDescript (FENTRYS [I], FTAGINFO); GetEntryData (FENTRYS [I], TiffHeaderPos; END;
// Offset of Exif IFD = The last TIFF IFDEntry value TIFF header offset FStream.Position: = TiffHeaderPos FEntrys [IFD_0_EntryCount-1] .u32value; FStream.Read (w, sizeof (w)); // IFD.1 Entry Count result: = Result 'Tiff ifd1 Entry Count:' Format ('%. 4x', [w]) CLRF;
IFD_1_ENTRYCOUNT: = W; setlength (fentrys, IFD_0_ENTRYCOUNT IFD_1_ENTRYCOUNT);
For i: = ifd_0_entrycount to length (fentrys) -1 do begin fstream.read (Fentrys [i] .u16tag, sizeof (u16)); FSTREAM.READ (FENTRYS [i] .U16TYPE, SIZEOF (U16)); FSTREAM. Read (FENTRYS [I] .U32Count, SizeOf (U32)); FSTREAM.READ (FENTRYS [i] .u32value, sizeof (u32)); getENTRYSUE (FENTRYS [I]);
For i: = ifd_0_entrycount to length (fentrys) -1 do begin getENTRYDESCRIPT (FENTRYS [I], FTAGINFO); GeTENTRYDATA (FENTRYS [I], TiffHeaderpos;
FINFCOUNT: = Length (FENTRYS);
Freeandnil (FSTREAM); END;
Function Yexifreader.exifInf (I: S32): String; var s: string; p: pchar; begin if i> = length (fentrys) THEN BEGIN RESULT: = '; End;
Result: = FENTRYS [I] .sdescript ''; s: = '; if (FENTRYS [i] .u16type = 2) OR // ASCII or Exifversion or FlashPixVersion (FENTRYS [i] .u16tag = $ 9000) OR FENTRYS [I] .u16tag = $ a000) The begin setlength (s, fentrys [i] .u32rs); Move (FENTRYS [i] .u32maddr ^, s [1], FENTRYS [i] .u32rsize; end else Begin IF (FENTRYS [I] .U16TAG = $ 927c) or (FENTRYS [I] .U16TAG = $ 9286) THEN BEGIN / / MAKERNOTE OR UserComment S: = 'NOT READ.'; Result: = Result S; EXIT; END; P: = allocmem (FENTRYS [I] .U32rs Zize * 2 1); bintohex (FENTRYS [I] .u32maddr, p, fentrys [i] .u32rsize); s: = p; freemem (p); end; result: = Result S; end; end;
*********************************************************** ******************** ;; Tiff Rev. 6.0 Attribute Information ;; ******************** ************************************************* ;; A. Tags relating to image data structure; 256 100 Image width -257 101 Image 102 Number height -258 of bits per component -259 103 Compression -262 106 Pixel composition -274 112 Orientation of image -277 115 Number of components - 284 11C Image data arrangement -530 212 subsampling ratio of Y to C -531 213 Y and C positioning -282 11A Image resolution in width direct -283 11B Image resolution in height direct -296 128 Unit of X and Y resolution - ;; B Tags relating to recording offset; 273 111 image data location -278 116 Number of rows per strip -279 117 bytes per compressed strip -5132020201 Offset To JPEG SOI -514 202 Bytes of JPEG data - ;; C. Tags relating to image data characteristics; 301 12D Transfer function -318 13E White point chromaticity -319 13F Chromaticities of primaries -529 211 Color space transformation matrix -532 214 Pair of black and white reference - ;; D. Other tags; 306 132 File change date and time -270 10E Image Description -271 10F Image input equipment maker -272 110 Image input equipment model -305 131 Software used -315 13B Person who created the image -3432 8298 Copyright Holder - ;;; ************************************************** ************************* ;;
EXIF IFT Attribute Information V2.1 ;; *************************************************** **************************** ;; A. Tags relating to version; 36864 9000 EXIF VERSION-40960 A000 Supported FlashPix Version - ;; B. Tag Relating to Image Data Characteristics; 40961 A001 Color space information - ;; C. Tags Relating to Image Configuration; 37121 9101 Meaning of each component -37122 9102 Image compression mode -40962 A002 Valid image width -40963 A003 Valid image height - ;; D. Tags Relating to User Information; 37500 927C Manufacturer notes -37510 9286 User comments - ;; E. Tag Relating to Related File Information; 40964 A004 Related audio file - ;; F. Tags Relating to Date and Time; 36867 9003 Date and Time of Original Data -36868 9004 Date and Time of Digital Data-37520 9290 DateTime Subseconds -37521 9291 DateTimeoriginal Subseconds --37522 9292 datetimedigitized Subseconds - ;; g. tags relating to pie picture- taking conditions;