Files
lazarus-ccr/components/fpexif/tests/unittest/common/fetexifle.pas

728 lines
29 KiB
ObjectPascal
Raw Normal View History

unit fetExifLE;
{$IFDEF FPC}
{$mode objfpc}{$H+}
{$ENDIF}
interface
uses
Classes, SysUtils,
{$ifdef FPC}
fpcunit, testutils, testregistry;
{$else}
fetTestUtils, TestFrameWork;
{$endif}
const
// JPEG picture with Exif data
ExifJpegPic = '..\pictures\originals\with_exif.jpg';
WorkFile_JpegWithExif = '.\pictures\with_exif.jpg';
// TIFF pPicture with Exif data
ExifTiffPic = '..\pictures\originals\with_exif.tif';
WorkFile_TiffWithExif = '.\pictures\with_exif.tif';
// Picture without Exif data
NoExifPic = '..\pictures\originals\no_metadata.jpg';
WorkFile_NoExif = '.\pictures\no_exif.jpg';
type
TstExifLE = class(TTestCase)
protected
procedure SetUp; override;
procedure TearDown; override;
procedure Internal_CheckHasExif(AFileName: String; ExpectExif: Boolean);
published
procedure CheckForPictures;
procedure CheckCreateImgInfo;
procedure CheckHasExif;
procedure ReadExifTest_Jpeg;
procedure ReadExifTest_Tiff;
procedure ReadGPSTest;
procedure CreateExifTest;
procedure WriteExifTest_Jpeg;
procedure WriteGPSTest_Jpeg;
procedure ValidFileTest_Jpeg;
procedure CreateThumbnail_Jpeg;
end;
implementation
uses
{$IFDEF FPC}
Graphics, FileUtil,
{$ELSE}
Graphics, Jpeg,
{$ENDIF}
Math,
fpeGlobal, fpeTags, fpeUtils, fpeExifData, fpeMetadata;
procedure TstExifLE.SetUp;
var
dir: String;
begin
if FileExists(Workfile_JpegWithExif) then
DeleteFile(WorkFile_JpegWithExif);
if FileExists(Workfile_TiffWithExif) then
DeleteFile(WorkFile_TiffWithExif);
if FileExists(Workfile_NoExif) then
DeleteFile(Workfile_NoExif);
dir := ExtractFileDir(WorkFile_JpegWithExif);
if not DirectoryExists(dir) then
ForceDirectories(dir);
if not FileExists(WorkFile_JpegWithExif) then
if FileExists(ExifJpegPic) then
CopyFile(ExifJpegPic, WorkFile_JpegWithExif);
if not FileExists(WorkFile_TiffWithExif) then
if FileExists(ExifTiffPic) then
CopyFile(ExifTiffPic, WorkFile_TiffWithExif);
if not FileExists(WorkFile_NoExif) then
if FileExists(NoExifPic) then
CopyFile(NoExifPic, WorkFile_NoExif);
end;
procedure TstExifLE.TearDown;
begin
if FileExists(WorkFile_NoExif) then
DeleteFile(WorkFile_NoExif);
if FileExists(WorkFile_JpegWithExif) then
DeleteFile(WorkFile_JpegWithExif);
if FileExists(WorkFile_TiffWithExif) then
DeleteFile(WorkFile_TiffWithExif);
end;
procedure TstExifLE.CheckForPictures;
begin
CheckTrue(FileExists(ExifJpegPic), 'Original test picture file "' + ExifJpegPic + '" does not exist');
CheckTrue(FileExists(ExifTiffPic), 'Original test picture file "' + ExifTiffPic + '" does not exist');
CheckTrue(FileExists(NoExifPic), 'Original test picture file "' + NoExifPic + '" does not exist');
CheckTrue(FileExists(WorkFile_JpegWithExif), 'Test picture file "' + WorkFile_JpegWithExif + '" does not exist');
CheckTrue(FileExists(WorkFile_TiffWithExif), 'Test picture file "' + WorkFile_TiffWithExif + '" does not exist');
CheckTrue(FileExists(WorkFile_NoExif), 'Test picture file "' + WorkFile_NoExif + '" does not exist');
end;
procedure TstExifLE.CheckCreateImgInfo;
var
imgInfo: TImgInfo;
begin
imgInfo := TImgInfo.Create();
try
CheckIs(imgInfo, TImgInfo,'Is not TImgInfo');
finally
imgInfo.Free;
end;
end;
procedure TstExifLE.Internal_CheckHasExif(AFileName: String; ExpectExif: Boolean);
var
imgInfo: TImgInfo;
begin
imgInfo := TImgInfo.Create;
try
imgInfo.LoadFromFile(AFileName);
if ExpectExif then
CheckTrue(imgInfo.HasExif, 'Failure to detect EXIF in test picture file "' + AFileName + '"')
else
CheckFalse(imgInfo.HasExif, 'Unexpected EXIF in test picture file "' + AFileName + '" detected');
finally
imgInfo.Free;
end;
end;
procedure TstExifLE.CheckHasExif;
begin
Internal_CheckHasExif(WorkFile_JpegWithExif, true);
Internal_CheckHasExif(WorkFile_TiffWithExif, true);
Internal_CheckHasExif(WorkFile_NoExif, false);
end;
procedure TstExifLE.ReadExifTest_Jpeg;
{ Output of ExifTool for the jpeg test image with exif using this commandline:
exiftool -G -H -s with_exif.jpg > with_exif.txt
These values are checked
|
[ExifTool] - ExifToolVersion : 10.60
[File] - FileName : with_exif.jpg <--
[File] - Directory : .
[File] - FileSize : 5.0 kB <--
[File] - FileModifyDate : 2017:10:16 19:35:01+02:00
[File] - FileAccessDate : 2017:10:16 19:35:01+02:00
[File] - FileCreateDate : 2017:10:16 19:34:46+02:00
[File] - FilePermissions : rw-rw-rw-
[File] - FileType : JPEG
[File] - FileTypeExtension : jpg
[File] - MIMEType : image/jpeg
[File] - ExifByteOrder : Little-endian (Intel, II) <--
[File] - ImageWidth : 200 <--
[File] - ImageHeight : 150 <--
[File] - EncodingProcess : Baseline DCT, Huffman coding
[File] - BitsPerSample : 8
[File] - ColorComponents : 3
[File] - YCbCrSubSampling : YCbCr4:2:0 (2 2)
[EXIF] 0x010d DocumentName : Test image <--
[EXIF] 0x010e ImageDescription : This is just a test image <--
[EXIF] 0x0112 Orientation : Horizontal (normal) <--
[EXIF] 0x011a XResolution : 72 <--
[EXIF] 0x011b YResolution : 72 <--
[EXIF] 0x0128 ResolutionUnit : inches <--
[EXIF] 0x0131 Software : PhotoFiltre 7 <--
[EXIF] 0x0132 ModifyDate : 2017:10:14 23:35:07 <--
[EXIF] 0x9000 ExifVersion : 0210 <--
[EXIF] 0xa002 ExifImageWidth : 200 <--
[EXIF] 0xa003 ExifImageHeight : 150 <--
[EXIF] 0x0000 GPSVersionID : 2.3.0.0 <--
[EXIF] 0x0001 GPSLatitudeRef : South <--
[EXIF] 0x0003 GPSLongitudeRef : West <--
[Composite] - GPSLatitude : 51 deg 33' 48.28" S <-- fpExif coordinates without the S
[Composite] - GPSLongitude : 59 deg 49' 53.55" W <-- fpExif coordinates without the W
[Composite] - GPSPosition : 51 deg 33' 48.28" S, 59 deg 49' 53.55" W
[Composite] - ImageSize : 200x150
[Composite] - Megapixels : 0.030
}
var
imgInfo: TImgInfo;
lTag: TTag;
begin
imgInfo := TImgInfo.Create;
try
imgInfo.LoadFromFile(WorkFile_JpegWithExif);
// This is general information stored within imgImfInfo
CheckEquals('with_exif.jpg', ExtractFileName(imgInfo.FileName), 'Filename mismatch');
CheckEquals('5.0', Format('%.1f', [imgInfo.FileSize/1024], fpExifFmtSettings), 'File size mismatch');
// CheckEquals('2017:10:14 23:57:49', FormatDateTime(EXIF_DATETIME_FORMAT, imgInfo.FileDate), 'File access date mismatch');
CheckEquals(200, imgInfo.ImgWidth, 'jpeg image width mismatch');
CheckEquals(150, imgInfo.ImgHeight, 'jpeg image height mismatch');
// The following pieces of information are obtained from the EXIF segment
CheckFalse(imgInfo.ExifData.BigEndian, 'Exif byte order detection error');
lTag := imgInfo.ExifData.TagByName['DocumentName'];
CheckTrue(lTag <> nil, 'Tag "DocumentName" not found');
CheckEquals('Test image', lTag.AsString, 'Value mismatch of tag "DocumentName"');
lTag := imgInfo.ExifData.TagByName['ImageDescription'];
CheckTrue(lTag <> nil, 'Tag "ImageDescription" not found');
CheckEquals('This is just a test image', lTag.AsString, 'Value mismatch of tag "ImageDescription"');
lTag := imgInfo.ExifData.TagByName['Orientation'];
CheckTrue(lTag <> nil, 'Tag "Orientation" not found');
CheckEquals('Horizontal (normal)', lTag.AsString, 'Value mismatch of tag "Orientation"');
lTag := imgInfo.ExifData.TagByName['XResolution'];
CheckTrue(lTag <> nil, 'Tag "XResolution" not found');
CheckEquals('72', lTag.AsString, 'Value mismatch of tag "XResolution"');
CheckTrue(lTag is TNumericTag, 'Tag "XResolution" is no TNumericTag');
CheckEquals(72, lTag.AsInteger, 'Integer value mismatch of tag "XResolution"');
CheckTrue(lTag is TFloatTag, 'Tag "XResolution" is no TNumericTag');
CheckEquals(72.0, lTag.AsFloat, 'Float value mismatch of tag "XResolution"');
lTag := imgInfo.ExifData.TagByName['YResolution'];
CheckTrue(lTag <> nil, 'Tag "YResolution" not found');
CheckEquals('72', lTag.AsString, 'Value mismatch of tag "YResolution"');
lTag := imgInfo.ExifData.TagByName['ResolutionUnit'];
CheckTrue(lTag <> nil, 'Tag "ResolutionUnit" not found');
CheckEquals('inches', lTag.AsString, 'Value mismatch of tag "ResolutionUnit"');
lTag := imgInfo.ExifData.TagByName['Software'];
CheckTrue(lTag <> nil, 'Tag "Software" not found');
CheckEquals('PhotoFiltre 7', lTag.AsString, 'Value mismatch of tag "Software"');
lTag := imgInfo.ExifData.TagByName['DateTime'];
CheckTrue(lTag <> nil, 'Tag "DateTime" not found');
CheckTrue(lTag is TDateTimeTag, 'Tag "DateTime" is no TDateTimeTag');
TDateTimeTag(lTag).FormatStr := EXIF_DATETIME_FORMAT;
CheckEquals('2017:10:14 23:35:07', lTag.AsString, 'Value mismatch of tag "DateTime"');
lTag := imgInfo.ExifData.TagByName['ExifVersion'];
CheckTrue(lTag <> nil, 'Tag "ExifVersion" not found');
CheckTrue(lTag is TVersionTag, 'Tag "ExifVersion" is not TVersionTag');
CheckEquals('0210', lTag.AsString, 'Value mismatch of tag "ExifVersion"');
lTag := imgInfo.ExifData.TagByName['ExifImageWidth'];
CheckTrue(lTag <> nil, 'Tag "ExifImageWidth" not found');
CheckEquals('200', lTag.AsString, 'Value mismatch of tag "ExifImageWidth"');
lTag := imgInfo.ExifData.TagByName['ExifImageHeight'];
CheckTrue(lTag <> nil, 'Tag "ExifImageHeight" not found');
CheckEquals('150', lTag.AsString, 'Value mismatch of tag "ExifImageHeight"');
finally
imgInfo.Free;
end;
end;
procedure TstExifLE.ReadExifTest_Tiff;
{ Output of ExifTool for the tiff test image with exif using this commandline:
exiftool -G -H -s with_exif.tif > with_exif_tif.txt
These values are checked
|
[ExifTool] - ExifToolVersion : 10.60
[File] - FileName : with_exif.tif
[File] - Directory : .
[File] - FileSize : 88 kB
[File] - FileModifyDate : 2017:10:16 10:07:38+02:00
[File] - FileAccessDate : 2017:10:16 10:07:38+02:00
[File] - FileCreateDate : 2017:10:16 10:07:38+02:00
[File] - FilePermissions : rw-rw-rw-
[File] - FileType : TIFF
[File] - FileTypeExtension : tif
[File] - MIMEType : image/tiff
[File] - ExifByteOrder : Little-endian (Intel, II)
[EXIF] 0x0100 ImageWidth : 200
[EXIF] 0x0101 ImageHeight : 150
[EXIF] 0x0102 BitsPerSample : 8 8 8
[EXIF] 0x0103 Compression : Uncompressed
[EXIF] 0x0106 PhotometricInterpretation : RGB
[EXIF] 0x0111 StripOffsets : (Binary data 68 bytes, use -b option to extract)
[EXIF] 0x0115 SamplesPerPixel : 3
[EXIF] 0x0116 RowsPerStrip : 13
[EXIF] 0x0117 StripByteCounts : (Binary data 59 bytes, use -b option to extract)
[EXIF] 0x011a XResolution : 72
[EXIF] 0x011b YResolution : 72
[EXIF] 0x011c PlanarConfiguration : Chunky
[EXIF] 0x0128 ResolutionUnit : inches
[EXIF] 0x0131 Software : LIBFORMAT (c) Pierre-e Gougelet
[EXIF] 0x0132 ModifyDate : 2017:10:14 23:35:07
[EXIF] 0x9000 ExifVersion : 0210
[EXIF] 0xa002 ExifImageWidth : 200
[Composite] - ImageSize : 200x150
[Composite] - Megapixels : 0.030 }
var
imgInfo: TImgInfo;
lTag: TTag;
begin
imgInfo := TImgInfo.Create;
try
imgInfo.LoadFromFile(WorkFile_TiffWithExif);
// This is general information stored within imgImfInfo
CheckEquals('with_exif.tif', ExtractFileName(imgInfo.FileName), 'Filename mismatch');
CheckEquals('88', Format('%.0f', [imgInfo.FileSize/1024], fpExifFmtSettings), 'File size mismatch');
// The following pieces of information are obtained from the EXIF segment
CheckFalse(imgInfo.ExifData.BigEndian, 'Exif byte order detection error');
lTag := imgInfo.ExifData.TagByName['ImageWidth'];
CheckTrue(lTag <> nil, 'Tag "ImageWidth" not found');
CheckEquals(200, lTag.AsInteger, 'Value mismatch of tag "ImageWidth"');
lTag := imgInfo.ExifData.TagByName['ImageHeight'];
CheckTrue(lTag <> nil, 'Tag "ImageHeight" not found');
CheckEquals(150, lTag.AsInteger, 'Value mismatch of tag "ImageHeight"');
lTag := imgInfo.ExifData.TagByName['BitsPerSample'];
CheckTrue(lTag <> nil, 'Tag "BitsPerSample" not found');
CheckEquals('8,8,8', lTag.AsString, 'Value mismatch of tag "BitsPerSample"');
lTag := imgInfo.ExifData.TagByName['Compression'];
CheckTrue(lTag <> nil, 'Tag "Compression" not found');
CheckEquals('Uncompressed', lTag.AsString, 'Value mismatch of tag "Compression"');
lTag := imgInfo.ExifData.TagByName['PhotometricInterpretation'];
CheckTrue(lTag <> nil, 'Tag "PhotometricInterpretation" not found');
CheckEquals('RGB', lTag.AsString, 'Value mismatch of tag "PhotometricInterpretation"');
lTag := imgInfo.ExifData.TagByName['SamplesPerPixel'];
CheckTrue(lTag <> nil, 'Tag "SamplesPerPixel" not found');
CheckEquals(3, lTag.AsInteger, 'Value mismatch of tag "SamplesPerPixel"');
lTag := imgInfo.ExifData.TagByName['RowsPerStrip'];
CheckTrue(lTag <> nil, 'Tag "RowsPerStrip" not found');
CheckEquals(13, lTag.AsInteger, 'Value mismatch of tag "RowsPerStrip"');
lTag := imgInfo.ExifData.TagByName['XResolution'];
CheckTrue(lTag <> nil, 'Tag "XResolution" not found');
CheckEquals(72, lTag.AsInteger, 'Integer value mismatch of tag "XResolution"');
lTag := imgInfo.ExifData.TagByName['YResolution'];
CheckTrue(lTag <> nil, 'Tag "YResolution" not found');
CheckEquals(72, lTag.AsInteger, 'Value mismatch of tag "YResolution"');
lTag := imgInfo.ExifData.TagByName['PlanarConfiguration'];
CheckTrue(lTag <> nil, 'Tag "PlanarConfiguration" not found');
CheckEquals('Chunky', lTag.AsString, 'Value mismatch of tag "PlanarConfiguration"');
lTag := imgInfo.ExifData.TagByName['ResolutionUnit'];
CheckTrue(lTag <> nil, 'Tag "ResolutionUnit" not found');
CheckEquals('inches', lTag.AsString, 'Value mismatch of tag "ResolutionUnit"');
lTag := imgInfo.ExifData.TagByName['Software'];
CheckTrue(lTag <> nil, 'Tag "Software" not found');
CheckEquals('LIBFORMAT (c) Pierre-e Gougelet', lTag.AsString, 'Value mismatch of tag "Software"');
lTag := imgInfo.ExifData.TagByName['DateTime'];
CheckTrue(lTag <> nil, 'Tag "DateTime" not found');
CheckTrue(lTag is TDateTimeTag, 'Tag "DateTime" is no TDateTimeTag');
TDateTimeTag(lTag).FormatStr := EXIF_DATETIME_FORMAT;
CheckEquals('2017:10:14 23:35:07', lTag.AsString, 'Value mismatch of tag "DateTime"');
lTag := imgInfo.ExifData.TagByName['ExifVersion'];
CheckTrue(lTag <> nil, 'Tag "ExifVersion" not found');
CheckTrue(lTag is TVersionTag, 'Tag "ExifVersion" is not TVersionTag');
CheckEquals('0210', lTag.AsString, 'Value mismatch of tag "ExifVersion"');
lTag := imgInfo.ExifData.TagByName['ExifImageWidth'];
CheckTrue(lTag <> nil, 'Tag "ExifImageWidth" not found');
CheckEquals(200, lTag.AsInteger, 'Value mismatch of tag "ExifImageWidth"');
finally
imgInfo.Free;
end;
end;
{ This test read the GPS data contained in file "with_exif.jpg". The GPS
data were written there using the service https://www.geoimgr.com/
See expected values in comment of "ReadExifTest". }
procedure TstExifLE.ReadGPSTest;
var
imgInfo: TImgInfo;
lTag: TTag;
begin
imgInfo := TImgInfo.Create;
try
imgInfo.LoadFromFile(WorkFile_JpegWithExif);
lTag := imgInfo.ExifData.TagByName['GPSVersionID'];
CheckTrue(lTag <> nil, 'Tag "GPSVersionID" not found');
CheckTrue(lTag is TVersionTag, 'Tag "GPSVersionID" is not TVersionTag');
TVersionTag(lTag).Separator := '.';
CheckEquals('2.3.0.0', lTag.AsString, 'Value mismatch of tag "GPSVersionID"');
lTag := imgInfo.ExifData.TagByName['GPSLatitude'];
CheckTrue(lTag <> nil, 'Tag "GPSLatitude" not found');
CheckTrue(lTag is TGPSPositionTag, 'Tag "GPSLatitude" is not a TGpsPositionTag');
TGpsPositionTag(lTag).FormatStr := '%0:.0f deg %1:.0f'' %2:.2f"';
CheckEquals('51 deg 33'' 48.28"', lTag.AsString, 'Value mismatch of tag "GPSLatitude"');
lTag := imgInfo.ExifData.TagByName['GPSLatitudeRef'];
CheckTrue(lTag <> nil, 'Tag "GPSLatitudeRef" not found');
CheckEquals('South', lTag.AsString, 'Value mismatch of tag "GPSLatitudeRef"');
lTag := imgInfo.ExifData.TagByName['GPSLongitude'];
CheckTrue(lTag <> nil, 'Tag "GPSLongitude" not found');
CheckTrue(lTag is TGPSPositionTag, 'Tag "GPSLongitude" is not a TGpsPositionTag');
TGpsPositionTag(lTag).FormatStr := '%0:.0f deg %1:.0f'' %2:.2f"';
CheckEquals('59 deg 49'' 53.55"', lTag.AsString, 'Value mismatch of tag "GPSLongitude"');
lTag := imgInfo.ExifData.TagByName['GPSLongitudeRef'];
CheckTrue(lTag <> nil, 'Tag "GPSLongitudeRef" not found');
CheckEquals('West', lTag.AsString, 'Value mismatch of tag "GPSLongitudeRef"');
finally
imgInfo.Free;
end;
end;
{ This test creates a new empty exif structure, but does not write anything to
file. }
procedure TstExifLE.CreateExifTest;
var
imgInfo: TImgInfo;
begin
imgInfo := TImgInfo.Create;
try
CheckTrue(imgInfo.ExifData = nil, 'EXIF found, but not expected.');
imgInfo.CreateExifData;
CheckTrue(imgInfo.ExifData <> nil, 'EXIF not found.');
finally
imgInfo.Free;
end;
end;
{ This test creates an empty EXIF structure, fills it with some data and saves
it to the No_exif file. After writing the file is read back and compared
with the written data. }
procedure TstExifLE.WriteExifTest_Jpeg;
var
imgInfo: TImgInfo;
lTag: TTag;
begin
imgInfo := TImgInfo.Create;
try
// Create empty EXIF
imgInfo.CreateExifData;
// Add tags
lTag := imgInfo.ExifData.AddTagByName('Primary.DocumentName');
lTag.AsString := 'Test image';
lTag := imgInfo.ExifData.AddTagByName('ImageDescription');
lTag.AsString := 'This is just a test image';
lTag := imgInfo.ExifData.AddTagByName('Orientation');
CheckTrue(lTag <> nil, 'Tag "Orientation" not found for writing');
lTag.AsString := 'Horizontal (normal)';
lTag := imgInfo.ExifData.AddTagByName('XResolution');
CheckTrue(lTag <> nil, 'Tag "XResolution" not found for writing');
lTag.AsInteger := 72;
lTag := imgInfo.ExifData.AddTagByName('YResolution');
CheckTrue(lTag <> nil, 'Tag "YResolution" not found for writing');
lTag.AsInteger := 72;
lTag := imgInfo.ExifData.AddTagByName('ResolutionUnit');
CheckTrue(lTag <> nil, 'Tag "ResolutionUnit" not found for writing');
ltag.AsString := 'inches';
lTag := imgInfo.ExifData.AddTagByName('Software');
CheckTrue(lTag <> nil, 'Tag "Software" not found for writing');
lTag.AsString := 'FPC/fpExif';
lTag := imgInfo.ExifData.AddTagByName('DateTime');
CheckTrue(lTag <> nil, 'Tag "DateTime" not found for writing');
CheckTrue(lTag is TDateTimeTag, 'Tag "DateTime" is no TDateTimeTag');
TDateTimeTag(lTag).AsDateTime := EncodeDate(2017,10,14) + EncodeTime(23,35,07,0);
lTag := imgInfo.ExifData.AddTagByName('ExifVersion');
CheckTrue(lTag <> nil, 'Tag "ExifVersion" not found for writing');
CheckTrue(lTag is TVersionTag, 'Tag "ExifVersion" is not TVersionTag');
TVersionTag(lTag).AsString := '0210';
lTag := imgInfo.ExifData.AddTagByName('ExifImageWidth');
CheckTrue(lTag <> nil, 'Tag "ExifImageWidth" not found for writing');
lTag.AsInteger := 200;
lTag := imgInfo.ExifData.AddTagByName('ExifImageHeight');
CheckTrue(lTag <> nil, 'Tag "ExifImageHeight" not found for writing');
lTag.AsInteger := 150;
// Save to file;
// Takes the image data from WorkFile_WithExif, replaces its EXIF with the
// current EXIF structure and writes to WorkFile_NoExif.
imgInfo.SaveToFile(WorkFile_NoExif, Workfile_JpegWithExif);
finally
imgInfo.Free;
end;
// Read written file and check EXIF
imgInfo := TImgInfo.Create;
try
imgInfo.LoadFromFile(Workfile_NoExif);
// Now there should be EXIF
CheckTrue(imgInfo.ExifData <> nil, 'EXIF not found.');
lTag := imgInfo.ExifData.TagByName['DocumentName'];
CheckTrue(lTag <> nil, 'Tag "DocumentName" not found for reading');
CheckEquals('Test image', lTag.AsString, 'Value mismatch of tag "DocumentName"');
lTag := imgInfo.ExifData.TagByName['ImageDescription'];
CheckTrue(lTag <> nil, 'Tag "ImageDescription" not found for reading');
CheckEquals('This is just a test image', lTag.AsString, 'Value mismatch of tag "ImageDescription"');
lTag := imgInfo.ExifData.TagByName['Orientation'];
CheckTrue(lTag <> nil, 'Tag "Orientation" not found for reading');
CheckEquals('Horizontal (normal)', lTag.AsString, 'Value mismatch of tag "Orientation"');
lTag := imgInfo.ExifData.TagByName['XResolution'];
CheckTrue(lTag <> nil, 'Tag "XResolution" not found for reading');
CheckEquals(72, lTag.AsInteger, 'Integer value mismatch of tag "XResolution"');
lTag := imgInfo.ExifData.TagByName['YResolution'];
CheckTrue(lTag <> nil, 'Tag "YResolution" not found for reading');
CheckEquals('72', lTag.AsString, 'Value mismatch of tag "YResolution"');
lTag := imgInfo.ExifData.TagByName['ResolutionUnit'];
CheckTrue(lTag <> nil, 'Tag "ResolutionUnit" not found for reading');
CheckEquals('inches', lTag.AsString, 'Value mismatch of tag "ResolutionUnit"');
lTag := imgInfo.ExifData.TagByName['Software'];
CheckTrue(lTag <> nil, 'Tag "Software" not found for reading');
CheckEquals('FPC/fpExif', lTag.AsString, 'Value mismatch of tag "Software"');
lTag := imgInfo.ExifData.TagByName['DateTime'];
CheckTrue(lTag <> nil, 'Tag "DateTime" not found for reading');
CheckTrue(lTag is TDateTimeTag, 'Tag "DateTime" is no TDateTimeTag');
TDateTimeTag(lTag).FormatStr := ISO_DATETIME_FORMAT;
CheckEquals('2017-10-14 23:35:07', lTag.AsString, 'Value mismatch of tag "DateTime"');
lTag := imgInfo.ExifData.TagByName['ExifVersion'];
CheckTrue(lTag <> nil, 'Tag "ExifVersion" not found for reading');
CheckTrue(lTag is TVersionTag, 'Tag "ExifVersion" is not TVersionTag');
CheckEquals('0210', lTag.AsString, 'Value mismatch of tag "ExifVersion"');
lTag := imgInfo.ExifData.TagByName['ExifImageWidth'];
CheckTrue(lTag <> nil, 'Tag "ExifImageWidth" not found for reading');
CheckEquals('200', lTag.AsString, 'Value mismatch of tag "ExifImageWidth"');
lTag := imgInfo.ExifData.TagByName['ExifImageHeight'];
CheckTrue(lTag <> nil, 'Tag "ExifImageHeight" not found for reading');
CheckEquals('150', lTag.AsString, 'Value mismatch of tag "ExifImageHeight"');
finally
imgInfo.Free;
end;
end;
{ This test loads the With_Exif and changes the GPS data, saves to the same file,
reads back and checks validity of the GPS data. }
procedure TstExifLE.WriteGpsTest_Jpeg;
var
imgInfo: TImgInfo;
lTag: TTag;
begin
imgInfo := TImgInfo.Create;
try
imgInfo.LoadfromFile(Workfile_JpegWithExif);
// In spite of its name, the file must contain EXIF now, written in prev test.
CheckTrue(imgInfo.ExifData <> nil, 'EXIF not found.');
// Add tags
lTag := imgInfo.ExifData.AddTagByName('GPSVersionID');
CheckTrue(lTag <> nil, 'Tag "GPSVersionID" not found');
CheckTrue(lTag is TVersionTag, 'Tag "GPSVersionID" is not TVersionTag');
TVersionTag(lTag).Separator := '.';
lTag.AsString := '2.3.1.1';
lTag := imgInfo.ExifData.AddTagByName('GPSLatitude');
CheckTrue(lTag <> nil, 'Tag "GPSLatitude" not found');
CheckTrue(lTag is TGPSPositionTag, 'Tag "GPSLatitude" is not a TGpsPositionTag');
TGpsPositionTag(lTag).FormatStr := '%0:.0f deg %1:.0f'' %2:.2f"';
lTag.AsString := '45 deg 30'' 15.32"';
lTag := imgInfo.ExifData.AddTagByName('GPSLatitudeRef');
CheckTrue(lTag <> nil, 'Tag "GPSLatitudeRef" not found');
lTag.AsString := 'North';
lTag := imgInfo.ExifData.AddTagByName('GPSLongitude');
CheckTrue(lTag <> nil, 'Tag "GPSLongitude" not found');
CheckTrue(lTag is TGPSPositionTag, 'Tag "GPSLongitude" is not a TGpsPositionTag');
TGpsPositionTag(lTag).FormatStr := '%0:.0f deg %1:.0f'' %2:.2f"';
lTag.AsString := '15 deg 16'' 17.18"';
lTag := imgInfo.ExifData.AddTagByName('GPSLongitudeRef');
CheckTrue(lTag <> nil, 'Tag "GPSLongitudeRef" not found');
lTag.AsString := 'East';
// Save to file
imgInfo.SaveToFile(WorkFile_JpegWithExif);
finally
imgInfo.Free;
end;
imgInfo := TImgInfo.Create;
try
imgInfo.LoadFromFile(Workfile_JpegWithExif);
CheckTrue(imgInfo.ExifData <> nil, 'EXIF not found after writing.');
lTag := imgInfo.ExifData.TagByName['GPSVersionID'];
CheckTrue(lTag <> nil, 'Tag "GPSVersionID" not found');
CheckTrue(lTag is TVersionTag, 'Tag "GPSVersionID" is not TVersionTag');
TVersionTag(lTag).Separator := '.';
CheckEquals('2.3.1.1', lTag.AsString, 'Value mismatch of tag "GPSVersionID"');
lTag := imgInfo.ExifData.TagByName['GPSLatitude'];
CheckTrue(lTag <> nil, 'Tag "GPSLatitude" not found');
CheckTrue(lTag is TGPSPositionTag, 'Tag "GPSLatitude" is not a TGpsPositionTag');
TGpsPositionTag(lTag).FormatStr := '%0:.0f deg %1:.0f'' %2:.2f"';
CheckEquals('45 deg 30'' 15.32"', lTag.AsString, 'Value mismatch of tag "GPSLatitude"');
lTag := imgInfo.ExifData.TagByName['GPSLatitudeRef'];
CheckTrue(lTag <> nil, 'Tag "GPSLatitudeRef" not found');
CheckEquals('North', lTag.AsString, 'Value mismatch of tag "GPSLatitudeRef"');
lTag := imgInfo.ExifData.TagByName['GPSLongitude'];
CheckTrue(lTag <> nil, 'Tag "GPSLongitude" not found');
CheckTrue(lTag is TGPSPositionTag, 'Tag "GPSLongitude" is not a TGpsPositionTag');
TGpsPositionTag(lTag).FormatStr := '%0:.0f deg %1:.0f'' %2:.2f"';
CheckEquals('15 deg 16'' 17.18"', lTag.AsString, 'Value mismatch of tag "GPSLongitude"');
lTag := imgInfo.ExifData.TagByName['GPSLongitudeRef'];
CheckTrue(lTag <> nil, 'Tag "GPSLongitudeRef" not found');
CheckEquals('East', lTag.AsString, 'Value mismatch of tag "GPSLongitudeRef"');
finally
imgInfo.Free;
end;
end;
procedure TstExifLE.ValidFileTest_Jpeg;
var
jpg: TJpegImage;
fn: string;
bmp: TBitmap;
success: Boolean;
begin
// Modify the EXIF structure of WorkFile_WithExif;
fn := Workfile_JpegWithExif;
WriteExifTest_Jpeg;
success := false;
jpg := TJpegImage.Create;
try
jpg.LoadFromFile(fn);
bmp := TBitmap.Create;
try
bmp.Width := jpg.Width;
bmp.Height := jpg.Height;
bmp.Canvas.Draw(0, 0, jpg);
success := true;
finally
bmp.Free;
CheckTrue(success, 'Non-readable file');
end;
finally
jpg.Free;
end;
end;
procedure TstExifLE.CreateThumbnail_Jpeg;
const
THUMBSIZE = 120;
var
imgInfo: TImgInfo;
srcStream, destStream: TMemoryStream;
destStreamSize, currentThumbSize: Int64;
w, h: Integer;
begin
imgInfo := TImgInfo.Create;
try
imgInfo.LoadFromFile(Workfile_JpegWithExif);
CheckTrue(imgInfo.ExifData <> nil, 'EXIF not found.');
CheckFalse(imgInfo.ExifData.HasThumbnail, 'Presence of thumbnail not expected');
srcStream := TMemoryStream.Create;
destStream := TMemoryStream.Create;
try
srcStream.LoadFromFile(Workfile_JpegWithExif);
JPEGScaleImage(srcStream, destStream, THUMBSIZE);
destStreamSize := destStream.Size;
destStream.Position := 0;
imgInfo.ExifData.LoadThumbnailFromStream(deststream);
finally
destStream.Free;
srcStream.Free;
end;
CheckTrue(imgInfo.ExifData.HasThumbnail, 'Thumbnail not found.');
w := imgInfo.ExifData.TagByName['ThumbnailWidth'].AsInteger;
h := imgInfo.ExifData.TagByName['ThumbnailHeight'].AsInteger;
currentThumbSize := imgInfo.ExifData.TagByName['ThumbnailSize'].AsInteger;
CheckEquals(THUMBSIZE, Max(w, h), 'Thumbnailsize mismatch');
CheckEquals(destStreamSize, currentThumbSize, 'Thumbnail size mismatch');
finally
imgInfo.Free;
end;
end;
initialization
{$IFDEF FPC}
RegisterTest(TstExifLE);
{$ELSE}
TestFramework.RegisterTest(TstExifLE.Suite);
{$ENDIF}
end.