fpexif: Add Delphi examples

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7328 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2020-02-17 23:17:39 +00:00
parent 9779c4beba
commit acb60ffd33
14 changed files with 2688 additions and 0 deletions

View File

@ -0,0 +1,93 @@
program console_demo;
{$APPTYPE CONSOLE}
{$DEFINE dExifNoJpeg}
{$R *.res}
uses
SysUtils,
fpeMetaData,
fpeTags;
var
imgInfo: TImgInfo;
tag: TTag;
const
FILENAME = '..\..\..\test-image.jpg';
begin
try
imgInfo := TImgInfo.Create;
try
// Read file
imgInfo.LoadFromFile(FILENAME);
// Check for EXIF
if imgInfo.HasExif then begin
// Write out some tags
// (1) date and time when the picture was taken
Write('Date/time: ':20);
tag := imgInfo.ExifData.TagByName['DateTime'];
if tag = nil then
WriteLn('--- not available in this file ---')
else
WriteLn(tag.AsString);
// (2) shutter speed used when taking the photo
tag := imgInfo.ExifData.TagByName['ShutterSpeed'];
if tag <> nil then
WriteLn('Shutter speed: ':20, tag.AsString)
else
begin
// (3) Sometimes alternative tags are availabe
tag := imgInfo.ExifData.TagByName['ExposureTime'];
if tag <> nil then
WriteLn('Exposure time: ':20, tag.AsString);
end;
// Focal length
tag := imgInfo.ExifData.TagByName['FocalLength'];
if tag <> nil then
WriteLn('Focal length: ':20, tag.AsString);
// Add user comment
imgInfo.ExifData.TagByName['UserComment'].AsString := 'This is my favorite photo.';
// Save to file
imgInfo.SaveToFile(ExtractFilePath(FILENAME) + 'edited_image.jpg');
end
else
WriteLn('No EXIF data in this file.');
// Check for IPTC
if imgInfo.HasIPTC then begin
// Write out IPTC key words
Write('IPTC Keywords: ':20);
tag := imgInfo.IptcData.TagByName['Keywords'];
if tag = nil then
WriteLn('--- not available in this file ---')
else
WriteLn(tag.AsString);
end
else
WriteLn('No IPTC data in this file.');
finally
imgInfo.Free;
end;
WriteLn;
WriteLn('Press ENTER to quit...');
ReadLn;
except
on E: Exception do begin
Writeln(E.ClassName, ': ', E.Message);
WriteLn;
WriteLn('Press ENTER to quit...');
ReadLn;
end;
end;
end.

View File

@ -0,0 +1,649 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{FAF25CF3-1AD3-4E91-AAE4-53A637D73E44}</ProjectGuid>
<ProjectVersion>18.6</ProjectVersion>
<FrameworkType>None</FrameworkType>
<MainSource>console_demo.dpr</MainSource>
<Base>True</Base>
<Config Condition="'$(Config)'==''">Debug</Config>
<Platform Condition="'$(Platform)'==''">Win32</Platform>
<TargetedPlatforms>1</TargetedPlatforms>
<AppType>Console</AppType>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Android' and '$(Base)'=='true') or '$(Base_Android)'!=''">
<Base_Android>true</Base_Android>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
<Base_Win32>true</Base_Win32>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''">
<Base_Win64>true</Base_Win64>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_1)'!=''">
<Cfg_1>true</Cfg_1>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''">
<Cfg_1_Win32>true</Cfg_1_Win32>
<CfgParent>Cfg_1</CfgParent>
<Cfg_1>true</Cfg_1>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_2)'!=''">
<Cfg_2>true</Cfg_2>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Base)'!=''">
<DCC_DcuOutput>.\$(Platform)\$(Config)</DCC_DcuOutput>
<DCC_ExeOutput>.\$(Platform)\$(Config)</DCC_ExeOutput>
<DCC_E>false</DCC_E>
<DCC_N>false</DCC_N>
<DCC_S>false</DCC_S>
<DCC_F>false</DCC_F>
<DCC_K>false</DCC_K>
<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace)</DCC_Namespace>
<SanitizedProjectName>console_demo</SanitizedProjectName>
<DCC_UnitSearchPath>..\..;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
<DCC_Define>dExifNoJpeg;$(DCC_Define)</DCC_Define>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Android)'!=''">
<DCC_UsePackage>DBXSqliteDriver;IndyIPCommon;RESTComponents;bindcompdbx;DBXInterBaseDriver;IndyIPServer;IndySystem;tethering;fmxFireDAC;FireDAC;bindcompfmx;FireDACSqliteDriver;ibmonitor;FMXTee;soaprtl;DbxCommonDriver;FmxTeeUI;ibxpress;fmx;FireDACIBDriver;xmlrtl;soapmidas;ibxbindings;rtl;DbxClientDriver;CustomIPTransport;dbexpress;IndyCore;bindcomp;dsnap;FireDACCommon;IndyIPClient;RESTBackendComponents;soapserver;dbxcds;bindengine;CloudService;dsnapxml;dbrtl;IndyProtocols;FireDACCommonDriver;inet;$(DCC_UsePackage)</DCC_UsePackage>
<Android_LauncherIcon36>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png</Android_LauncherIcon36>
<Android_LauncherIcon48>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png</Android_LauncherIcon48>
<Android_LauncherIcon72>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png</Android_LauncherIcon72>
<Android_LauncherIcon96>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png</Android_LauncherIcon96>
<Android_LauncherIcon144>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png</Android_LauncherIcon144>
<Android_SplashImage426>$(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png</Android_SplashImage426>
<Android_SplashImage470>$(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png</Android_SplashImage470>
<Android_SplashImage640>$(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png</Android_SplashImage640>
<Android_SplashImage960>$(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png</Android_SplashImage960>
<EnabledSysJars>android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services-ads-7.0.0.dex.jar;google-play-services-analytics-7.0.0.dex.jar;google-play-services-base-7.0.0.dex.jar;google-play-services-gcm-7.0.0.dex.jar;google-play-services-identity-7.0.0.dex.jar;google-play-services-maps-7.0.0.dex.jar;google-play-services-panorama-7.0.0.dex.jar;google-play-services-plus-7.0.0.dex.jar;google-play-services-wallet-7.0.0.dex.jar</EnabledSysJars>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win32)'!=''">
<DCC_UsePackage>DBXSqliteDriver;IndyIPCommon;RESTComponents;bindcompdbx;DBXInterBaseDriver;vcl;IndyIPServer;vclactnband;vclFireDAC;IndySystem;tethering;svnui;dsnapcon;FireDACADSDriver;FireDACMSAccDriver;fmxFireDAC;vclimg;TeeDB;FireDAC;vcltouch;vcldb;bindcompfmx;svn;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;inetdb;FMXTee;soaprtl;DbxCommonDriver;FmxTeeUI;ibxpress;fmx;FireDACIBDriver;fmxdae;xmlrtl;soapmidas;ibxbindings;fmxobj;vclwinx;vclib;rtl;Tee;DbxClientDriver;CustomIPTransport;vcldsnap;dbexpress;IndyCore;vclx;bindcomp;appanalytics;dsnap;FireDACCommon;IndyIPClient;bindcompvcl;RESTBackendComponents;TeeUI;VCLRESTComponents;soapserver;dbxcds;VclSmp;adortl;vclie;bindengine;DBXMySQLDriver;CloudService;dsnapxml;FireDACMySQLDriver;dbrtl;IndyProtocols;inetdbxpress;FireDACCommonODBC;FireDACCommonDriver;inet;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
<BT_BuildType>Debug</BT_BuildType>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
<VerInfo_Locale>1033</VerInfo_Locale>
<DCC_ConsoleTarget>true</DCC_ConsoleTarget>
<UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
<UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win64)'!=''">
<DCC_UsePackage>DBXSqliteDriver;IndyIPCommon;RESTComponents;bindcompdbx;DBXInterBaseDriver;vcl;IndyIPServer;vclactnband;vclFireDAC;IndySystem;tethering;dsnapcon;FireDACADSDriver;FireDACMSAccDriver;fmxFireDAC;vclimg;TeeDB;FireDAC;vcltouch;vcldb;bindcompfmx;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;inetdb;FMXTee;soaprtl;DbxCommonDriver;FmxTeeUI;ibxpress;fmx;FireDACIBDriver;fmxdae;xmlrtl;soapmidas;ibxbindings;fmxobj;vclwinx;vclib;rtl;Tee;DbxClientDriver;CustomIPTransport;vcldsnap;dbexpress;IndyCore;vclx;bindcomp;appanalytics;dsnap;FireDACCommon;IndyIPClient;bindcompvcl;RESTBackendComponents;TeeUI;VCLRESTComponents;soapserver;dbxcds;VclSmp;adortl;vclie;bindengine;DBXMySQLDriver;CloudService;dsnapxml;FireDACMySQLDriver;dbrtl;IndyProtocols;inetdbxpress;FireDACCommonODBC;FireDACCommonDriver;inet;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
<DCC_ConsoleTarget>true</DCC_ConsoleTarget>
<UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
<UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1)'!=''">
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
<DCC_DebugDCUs>true</DCC_DebugDCUs>
<DCC_Optimize>false</DCC_Optimize>
<DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
<DCC_DebugInfoInExe>true</DCC_DebugInfoInExe>
<DCC_RemoteDebug>true</DCC_RemoteDebug>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
<DCC_RemoteDebug>false</DCC_RemoteDebug>
<VerInfo_Locale>1033</VerInfo_Locale>
<Manifest_File>(Ohne)</Manifest_File>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
<DCC_DebugInformation>0</DCC_DebugInformation>
</PropertyGroup>
<ItemGroup>
<DelphiCompile Include="$(MainSource)">
<MainSource>MainSource</MainSource>
</DelphiCompile>
<BuildConfiguration Include="Release">
<Key>Cfg_2</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
<BuildConfiguration Include="Debug">
<Key>Cfg_1</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
</ItemGroup>
<ProjectExtensions>
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
<Borland.ProjectType>Application</Borland.ProjectType>
<BorlandProject>
<Delphi.Personality>
<Source>
<Source Name="MainSource">console_demo.dpr</Source>
</Source>
<Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k260.bpl">Microsoft Office 2000 Beispiele für gekapselte Komponenten für Automatisierungsserver</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dclofficexp260.bpl">Microsoft Office XP Beispiele für gekapselte Komponenten für Automation Server</Excluded_Packages>
</Excluded_Packages>
</Delphi.Personality>
<Deployment Version="3">
<DeployFile LocalName="$(BDS)\Redist\osx64\libcgsqlite3.dylib" Class="DependencyModule">
<Platform Name="OSX64">
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="$(BDS)\Redist\osx32\libcgunwind.1.0.dylib" Class="DependencyModule">
<Platform Name="OSX32">
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="$(BDS)\Redist\iossimulator\libcgunwind.1.0.dylib" Class="DependencyModule">
<Platform Name="iOSSimulator">
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="$(BDS)\Redist\iossimulator\libpcre.dylib" Class="DependencyModule">
<Platform Name="iOSSimulator">
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="$(BDS)\Redist\osx32\libcgsqlite3.dylib" Class="DependencyModule">
<Platform Name="OSX32">
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="Win32\Debug\console_demo.exe" Configuration="Debug" Class="ProjectOutput">
<Platform Name="Win32">
<RemoteName>console_demo.exe</RemoteName>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployClass Name="AdditionalDebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidClassesDexFile">
<Platform Name="Android">
<RemoteDir>classes</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidFileProvider">
<Platform Name="Android">
<RemoteDir>res\xml</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidGDBServer">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidLibnativeArmeabiFile">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidLibnativeMipsFile">
<Platform Name="Android">
<RemoteDir>library\lib\mips</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidServiceOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashImageDef">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStyles">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStylesV21">
<Platform Name="Android">
<RemoteDir>res\values-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_DefaultAppIcon">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon144">
<Platform Name="Android">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon36">
<Platform Name="Android">
<RemoteDir>res\drawable-ldpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon48">
<Platform Name="Android">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon72">
<Platform Name="Android">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon96">
<Platform Name="Android">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage426">
<Platform Name="Android">
<RemoteDir>res\drawable-small</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage470">
<Platform Name="Android">
<RemoteDir>res\drawable-normal</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage640">
<Platform Name="Android">
<RemoteDir>res\drawable-large</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage960">
<Platform Name="Android">
<RemoteDir>res\drawable-xlarge</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DependencyFramework">
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.framework</Extensions>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.framework</Extensions>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DependencyModule">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
<Extensions>.dll;.bpl</Extensions>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="DependencyPackage">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
<Extensions>.bpl</Extensions>
</Platform>
</DeployClass>
<DeployClass Name="File">
<Platform Name="Android">
<Operation>0</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>0</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>0</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>0</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\Resources\StartUp\</RemoteDir>
<Operation>0</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources\StartUp\</RemoteDir>
<Operation>0</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1024">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch1536">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2048">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch768">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch320">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch640">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch640x1136">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceResourceRules">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements">
<Platform Name="iOSDevice32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSInfoPList">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug">
<Platform Name="OSX64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXEntitlements">
<Platform Name="OSX32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXInfoPList">
<Platform Name="OSX32">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="Linux64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="OSX32" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="OSX64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/>
</Deployment>
<Platforms>
<Platform value="Android">False</Platform>
<Platform value="Win32">True</Platform>
<Platform value="Win64">False</Platform>
</Platforms>
</BorlandProject>
<ProjectFileVersion>12</ProjectFileVersion>
</ProjectExtensions>
<Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
<Import Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj" Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')"/>
<Import Project="$(MSBuildProjectName).deployproj" Condition="Exists('$(MSBuildProjectName).deployproj')"/>
</Project>

View File

@ -0,0 +1,74 @@
program console_demo;
{$mode objfpc}{$H+}
uses
Classes,
fpeMetaData, fpeTags;
var
imgInfo: TImgInfo;
tag: TTag;
begin
imgInfo := TImgInfo.Create;
try
// Read file
imgInfo.LoadFromFile('..\test-image.jpg');
// Check for EXIF
if imgInfo.HasExif then begin
// Write out some tags
// (1) date and time when the picture was taken
Write('Date/time: ':20);
tag := imgInfo.ExifData.TagByName['DateTime'];
if tag = nil then
WriteLn('--- not available in this file ---')
else
WriteLn(tag.AsString);
// (2) shutter speed used when taking the photo
tag := imgInfo.ExifData.TagByName['ShutterSpeed'];
if tag <> nil then
WriteLn('Shutter speed: ':20, tag.AsString)
else
begin
// (3) Sometimes alternative tags are availabe
tag := imgInfo.ExifData.TagByName['ExposureTime'];
if tag <> nil then
WriteLn('Exposure time: ':20, tag.AsString);
end;
// Add user comment
imgInfo.ExifData.TagByName['UserComment'].AsString := 'This is my favorite photo.';
// Save to file
imgInfo.SaveToFile('edited_image.jpg');
end
else
WriteLn('No EXIF data in this file.');
// Check for IPTC
if imgInfo.HasIPTC then begin
// Write out IPTC key words
Write('Keywords: ':20);
tag := imgInfo.IptcData.TagByName['Keywords'];
if tag = nil then
WriteLn('--- not available in this file ---')
else
WriteLn(tag.AsString);
end
else
WriteLn('No IPTC data in this file.');
finally
imgInfo.Free;
end;
WriteLn;
WriteLn('Press ENTER to quit...');
ReadLn;
end.

View File

@ -0,0 +1,15 @@
program MetadataViewer;
uses
Forms,
mdvMain in 'mdvMain.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
MainForm.BeforeRun;
Application.Run;
end.

View File

@ -0,0 +1,144 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{2958AB0F-1A2B-4CDE-B1F2-B6C5B3644858}</ProjectGuid>
<MainSource>MetadataViewer.dpr</MainSource>
<Base>True</Base>
<Config Condition="'$(Config)'==''">Debug</Config>
<TargetedPlatforms>1</TargetedPlatforms>
<AppType>Application</AppType>
<FrameworkType>VCL</FrameworkType>
<ProjectVersion>18.6</ProjectVersion>
<Platform Condition="'$(Platform)'==''">Win32</Platform>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
<Base_Win32>true</Base_Win32>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''">
<Base_Win64>true</Base_Win64>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_1)'!=''">
<Cfg_1>true</Cfg_1>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''">
<Cfg_1_Win32>true</Cfg_1_Win32>
<CfgParent>Cfg_1</CfgParent>
<Cfg_1>true</Cfg_1>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''">
<Cfg_2>true</Cfg_2>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''">
<Cfg_2_Win32>true</Cfg_2_Win32>
<CfgParent>Cfg_2</CfgParent>
<Cfg_2>true</Cfg_2>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Base)'!=''">
<DCC_E>false</DCC_E>
<DCC_F>false</DCC_F>
<DCC_K>false</DCC_K>
<DCC_N>false</DCC_N>
<DCC_S>false</DCC_S>
<DCC_ImageBase>00400000</DCC_ImageBase>
<SanitizedProjectName>MetadataViewer</SanitizedProjectName>
<DCC_Namespace>Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace)</DCC_Namespace>
<VerInfo_Locale>1031</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=</VerInfo_Keys>
<DCC_UnitSearchPath>..\..;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win32)'!=''">
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
<BT_BuildType>Debug</BT_BuildType>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys>
<VerInfo_Locale>1033</VerInfo_Locale>
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
<Icon_MainIcon>MetadataViewer_Icon.ico</Icon_MainIcon>
<AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
<UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
<UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win64)'!=''">
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
<Icon_MainIcon>MetadataViewer_Icon.ico</Icon_MainIcon>
<AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
<UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
<UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1)'!=''">
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
<DCC_DebugInformation>0</DCC_DebugInformation>
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
<AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
<DCC_Optimize>false</DCC_Optimize>
<DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2_Win32)'!=''">
<AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Locale>1033</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys>
<DCC_DebugDCUs>true</DCC_DebugDCUs>
</PropertyGroup>
<ItemGroup>
<DelphiCompile Include="$(MainSource)">
<MainSource>MainSource</MainSource>
</DelphiCompile>
<DCCReference Include="mdvMain.pas">
<Form>Form1</Form>
</DCCReference>
<BuildConfiguration Include="Debug">
<Key>Cfg_2</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
<BuildConfiguration Include="Release">
<Key>Cfg_1</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
</ItemGroup>
<ProjectExtensions>
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
<Borland.ProjectType/>
<BorlandProject>
<Delphi.Personality>
<Source>
<Source Name="MainSource">MetadataViewer.dpr</Source>
</Source>
<Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k260.bpl">Microsoft Office 2000 Beispiele für gekapselte Komponenten für Automatisierungsserver</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dclofficexp260.bpl">Microsoft Office XP Beispiele für gekapselte Komponenten für Automation Server</Excluded_Packages>
</Excluded_Packages>
</Delphi.Personality>
<Platforms>
<Platform value="Win32">True</Platform>
<Platform value="Win64">False</Platform>
</Platforms>
</BorlandProject>
<ProjectFileVersion>12</ProjectFileVersion>
</ProjectExtensions>
<Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
<Import Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj" Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')"/>
</Project>

View File

@ -0,0 +1,319 @@
object MainForm: TMainForm
Left = 430
Top = 141
Caption = 'Metadata viewer'
ClientHeight = 714
ClientWidth = 926
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = True
ShowHint = True
OnCreate = FormCreate
OnDestroy = FormDestroy
PixelsPerInch = 96
TextHeight = 13
object Splitter2: TSplitter
Left = 274
Top = 0
Width = 5
Height = 691
end
object ShellPanel: TPanel
Left = 0
Top = 0
Width = 274
Height = 691
Align = alLeft
BevelOuter = bvNone
TabOrder = 0
object Splitter1: TSplitter
Left = 0
Top = 269
Width = 274
Height = 5
Cursor = crVSplit
Align = alTop
end
object PreviewImage: TImage
Left = 0
Top = 547
Width = 274
Height = 144
Hint = 'Thumbnail image embedded in the image file'
Align = alBottom
Center = True
Proportional = True
Stretch = True
end
object ShellTreeView: TDirectoryOutline
Left = 0
Top = 0
Width = 274
Height = 269
Hint = 'Navigate to the folder with your images.'
Align = alTop
ItemHeight = 13
Options = [ooDrawFocusRect]
PictureLeaf.Data = {
46030000424D460300000000000036000000280000000E0000000E0000000100
2000000000001003000000000000000000000000000000000000800080008000
8000800080008000800080008000800080008000800080008000800080008000
8000800080008000800080008000800080008000800080008000800080008000
8000800080008000800080008000800080008000800080008000800080008000
8000800080008000800080008000800080008000800080008000800080008000
8000800080008000800080008000800080008000800080008000800080008000
8000800080000000000000000000000000000000000000000000000000000000
00000000000000000000000000008000800080008000800080000000000000FF
FF00FFFFFF0000FFFF00FFFFFF0000FFFF00FFFFFF0000FFFF00FFFFFF0000FF
FF000000000080008000800080008000800000000000FFFFFF0000FFFF00FFFF
FF0000FFFF00FFFFFF0000FFFF00FFFFFF0000FFFF00FFFFFF00000000008000
800080008000800080000000000000FFFF00FFFFFF0000FFFF00FFFFFF0000FF
FF00FFFFFF0000FFFF00FFFFFF0000FFFF000000000080008000800080008000
800000000000FFFFFF0000FFFF00FFFFFF0000FFFF00FFFFFF0000FFFF00FFFF
FF0000FFFF00FFFFFF00000000008000800080008000800080000000000000FF
FF00FFFFFF0000FFFF00FFFFFF0000FFFF00FFFFFF0000FFFF00FFFFFF0000FF
FF000000000080008000800080008000800000000000FFFFFF0000FFFF00FFFF
FF0000FFFF00FFFFFF0000FFFF00FFFFFF0000FFFF00FFFFFF00000000008000
8000800080008000800000000000000000000000000000000000000000000000
0000000000000000000000000000000000008000800080008000800080008000
80008000800000000000FFFFFF0000FFFF00FFFFFF0000FFFF00000000008000
8000800080008000800080008000800080008000800080008000800080008080
8000000000000000000000000000000000008080800080008000800080008000
8000800080008000800080008000800080008000800080008000800080008000
8000800080008000800080008000800080008000800080008000800080008000
80008000800080008000}
TabOrder = 0
OnChange = ShellTreeViewChange
Data = {10}
end
object ShellListView: TFileListBox
Left = 0
Top = 274
Width = 274
Height = 260
Hint = 'Select the image for which you want to see the metadata'
Align = alClient
Mask = '*.jpg;*.jpeg;*.jpe;*.tiff;*.tif'
ShowGlyphs = True
TabOrder = 2
OnChange = ShellListViewChange
end
object Panel4: TPanel
Left = 0
Top = 534
Width = 274
Height = 13
Align = alBottom
AutoSize = True
BevelOuter = bvNone
TabOrder = 1
object Label1: TLabel
Left = 8
Top = 0
Width = 79
Height = 13
Caption = 'Thumbnail image'
Color = clBtnFace
ParentColor = False
end
end
end
object Panel2: TPanel
Left = 279
Top = 0
Width = 647
Height = 691
Align = alClient
BevelOuter = bvNone
TabOrder = 2
object Splitter3: TSplitter
Left = 0
Top = 571
Width = 647
Height = 5
Cursor = crVSplit
Align = alBottom
ExplicitTop = 562
end
object Panel3: TPanel
Left = 0
Top = 0
Width = 647
Height = 21
Align = alTop
AutoSize = True
BevelOuter = bvNone
BorderWidth = 4
TabOrder = 0
object FilenameInfo: TLabel
Left = 4
Top = 4
Width = 23
Height = 13
Caption = 'File: '
Color = clBtnFace
ParentColor = False
end
end
object PageControl1: TPageControl
Left = 0
Top = 21
Width = 647
Height = 550
ActivePage = PgMetadata
Align = alClient
TabOrder = 1
OnChange = PageControl1Change
object PgMetadata: TTabSheet
Caption = 'Meta data'
ExplicitLeft = 0
ExplicitTop = 0
ExplicitWidth = 0
ExplicitHeight = 0
object TagListView: TListView
Left = 0
Top = 0
Width = 639
Height = 503
Align = alClient
Columns = <
item
Caption = 'Group'
Width = 120
end
item
Caption = 'Tag ID'
Width = 60
end
item
Caption = 'Property'
Width = 220
end
item
AutoSize = True
Caption = 'Value'
end>
HideSelection = False
ReadOnly = True
RowSelect = True
SortType = stText
TabOrder = 0
ViewStyle = vsReport
OnCompare = TagListViewCompare
OnSelectItem = TagListViewSelectItem
end
object Panel1: TPanel
Left = 0
Top = 503
Width = 639
Height = 19
Align = alBottom
AutoSize = True
BevelOuter = bvNone
TabOrder = 1
ExplicitTop = 501
object CbDecodeMakerNotes: TCheckBox
Left = 0
Top = 0
Width = 127
Height = 19
Hint = 'Try to decode information in the MakerNote tag if possible'
Caption = 'Decode MakerNotes'
Checked = True
State = cbChecked
TabOrder = 0
end
end
end
object PgImage: TTabSheet
Caption = 'Image'
ExplicitLeft = 0
ExplicitTop = 0
ExplicitWidth = 0
ExplicitHeight = 0
object Image: TImage
Left = 0
Top = 0
Width = 639
Height = 522
Align = alClient
Center = True
Proportional = True
Stretch = True
ExplicitHeight = 545
end
end
end
object Messages: TMemo
Left = 0
Top = 576
Width = 647
Height = 90
Align = alBottom
TabOrder = 2
end
object DateTimePanel: TPanel
Left = 0
Top = 666
Width = 647
Height = 25
Align = alBottom
AutoSize = True
BevelOuter = bvNone
TabOrder = 3
object LblChangeDate: TLabel
Left = 4
Top = 5
Width = 124
Height = 13
Caption = 'Change EXIF date/time to'
Color = clBtnFace
ParentColor = False
end
object EdChangeDate: TEdit
Left = 144
Top = 1
Width = 152
Height = 21
Hint = 'New date to be assigned to the selected image'
TabOrder = 0
end
object BtnChangeDate: TButton
Left = 304
Top = 0
Width = 67
Height = 25
Hint = 'Replaces the image date.'
Caption = 'Execute'
TabOrder = 1
OnClick = BtnChangeDateClick
end
end
end
object StatusBar1: TStatusBar
Left = 0
Top = 691
Width = 926
Height = 23
Panels = <
item
Width = 150
end
item
Width = 150
end
item
Width = 250
end
item
Width = 150
end
item
Width = 100
end>
end
end

View File

@ -0,0 +1,605 @@
unit mdvMain;
{$IFDEF FPC}
!!! THIS PROGRAM IS INTENDED FOR DELPHI ONLY
{$ENDIF}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs,
ExtCtrls, ComCtrls, StdCtrls, DirOutLn, FileCtrl, jpeg,
fpeGlobal, fpeMetadata, fpeMakerNote,
Grids, Outline;
type
{ TMainForm }
TMainForm = class(TForm)
BtnChangeDate: TButton;
CbDecodeMakerNotes: TCheckBox;
EdChangeDate: TEdit;
FilenameInfo: TLabel;
Image: TImage;
Label1: TLabel;
LblChangeDate: TLabel;
Messages: TMemo;
PageControl1: TPageControl;
Panel1: TPanel;
Panel2: TPanel;
Panel3: TPanel;
DateTimePanel: TPanel;
Panel4: TPanel;
PreviewImage: TImage;
Splitter3: TSplitter;
StatusBar1: TStatusBar;
PgMetadata: TTabSheet;
PgImage: TTabSheet;
TagListView: TListView;
ShellPanel: TPanel;
ShellTreeView: TDirectoryOutline;
ShellListView: TFileListbox;
Splitter1: TSplitter;
Splitter2: TSplitter;
procedure BtnChangeDateClick(Sender: TObject);
procedure CbShowTagIDsChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure PageControl1Change(Sender: TObject);
(*
procedure ShellListViewSelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
procedure ShellTreeViewGetImageIndex(Sender: TObject; Node: TTreeNode);
procedure ShellTreeViewSelectionChanged(Sender: TObject);
*)
procedure TagListViewCompare(Sender: TObject; Item1, Item2: TListItem;
Data: Integer; var Compare: Integer);
procedure TagListViewSelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
procedure ShellTreeViewChange(Sender: TObject);
procedure ShellListViewChange(Sender: TObject);
private
FFileName: String;
FImgInfo: TImgInfo;
FImageLoaded: Boolean;
FImageOrientation: TExifOrientation;
procedure LoadFile(const AFileName: String);
procedure LoadFromIni;
procedure SaveToIni;
procedure UpdateCaption;
public
procedure BeforeRun;
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
uses
Types, IniFiles, Math, StrUtils, DateUtils,
fpeTags, fpeExifData, fpeIptcData;
const
TAG_ID_CAPTION = 'Tag ID';
LineEnding = #13#10;
function CalcIniName: String;
begin
Result := ChangeFileExt(Application.ExeName, '.ini');
end;
procedure RotateBitmap(const ABitmap: TBitmap; AOrientation: TExifOrientation);
{
Var
bmp: TBitmap;
srcImg, dstImg: TLazIntfImage;
imgHandle, imgMaskHandle: HBitmap;
i, j: integer;
w1, h1: Integer; // Input bitmap width and height diminished by 1
}
Begin
exit;
(*
Assert(ABitmap <> nil, 'RotateBitmap: Input bitmap is expected not to be nil.');
if (AOrientation = eoUnknown) or (AOrientation = eoNormal) then
exit;
w1 := ABitmap.Width - 1;
h1 := ABitmap.Height - 1;
srcImg := TLazIntfImage.Create(0, 0);
try
srcImg.LoadFromBitmap(ABitmap.Handle, ABitmap.MaskHandle);
bmp := TBitmap.Create;
try
dstImg := TLazIntfImage.Create(0, 0);
try
if AOrientation in [eoRotate90, eoRotate270, eoMirrorHorRot90, eoMirrorHorRot270] then
begin
bmp.SetSize(ABitmap.Height, ABitmap.Width);
dstImg.LoadFromBitmap(bmp.Handle, bmp.MaskHandle);
case AOrientation of
eoRotate90:
for i:=0 to w1 do
for j:=0 to h1 do
dstImg.Colors[h1-j, i] := srcImg.Colors[i, j];
eoRotate270:
for i:=0 to w1 do
for j:=0 to h1 do
dstImg.Colors[j, w1-i] := srcImg.Colors[i, j];
eoMirrorHorRot90:
for i:=0 to w1 do
for j:=0 to h1 do
dstImg.Colors[h1-j, w1-i] := srcImg.Colors[i, j];
eoMirrorHorRot270:
for i:=0 to w1 do
for j:=0 to h1 do
dstImg.Colors[j, i] := srcImg.Colors[i, j];
end;
end else
if AOrientation in [eoRotate180, eoMirrorHor, eoMirrorVert] then
begin
bmp.SetSize(ABitmap.Width, ABitmap.Height);
dstImg.LoadFromBitmap(bmp.Handle, bmp.MaskHandle);
case AOrientation of
eoRotate180:
for i:=0 to w1 do
for j:=0 to h1 do
dstImg.Colors[w1-i, h1-j] := srcImg.Colors[i, j];
eoMirrorHor:
for j:=0 to h1 do
for i:=0 to w1 do
dstImg.Colors[w1-i, j] := srcImg.Colors[i, j];
eoMirrorVert:
for i:=0 to w1 do
for j:=0 to h1 do
dstImg.Colors[i, h1-j] := srcImg.Colors[i, j];
end;
end;
dstImg.CreateBitmaps(imgHandle, imgMaskHandle, false);
bmp.Handle := ImgHandle;
bmp.MaskHandle := ImgMaskHandle;
finally
dstImg.Free;
end;
ABitmap.Assign(bmp);
finally
bmp.Free;
end;
finally
srcImg.Free;
end;
*)
end;
{ TMainForm }
procedure TMainForm.BeforeRun;
begin
LoadFromIni;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
//ShellListView.Parent.DoubleBuffered := true;
end;
procedure TMainForm.BtnChangeDateClick(Sender: TObject);
var
lTag: TTag;
dt: TDateTime;
fn: String;
begin
if (FImgInfo = nil) or (FImgInfo.ExifData = nil) then
exit;
if not TryStrToDateTime(EdChangeDate.Text, dt) then begin
MessageDlg('No valid date/time. Use your locale settings.', mtError, [mbOK], 0);
exit;
end;
lTag := FImgInfo.ExifData.TagByName['DateTimeOriginal'];
if lTag <> nil then
TDateTimeTag(lTag).AsDateTime := dt;
lTag := FImgInfo.ExifData.TagByName['DateTimeDigitized'];
if lTag <> nil then
TDateTimeTag(lTag).AsDateTime := dt;
lTag := FImgInfo.ExifData.TagByName['DateTime'];
if lTag <> nil then
TDateTimeTag(lTag).AsDateTime := dt;
fn := FImgInfo.FileName;
fn := ChangeFileExt(fn, '') + '_modified' + ExtractFileExt(fn);
FImgInfo.SaveToFile(fn);
end;
procedure TMainForm.CbShowTagIDsChange(Sender: TObject);
var
c: TListColumn;
i: Integer;
begin
TagListView.Items.BeginUpdate;
try
c := nil;
for i:=0 to TagListView.Columns.Count-1 do
if TagListView.Columns[i].Caption = TAG_ID_CAPTION then begin
c := TagListView.Columns[i];
break;
end;
{
if c <> nil then
c.Visible := CbShowTagIDs.Checked;;
}
finally
TagListView.Items.EndUpdate;
end;
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
try
SaveToIni;
except
end;
FImgInfo.Free;
end;
type
TMyPicture = class(TPicture);
procedure TMainForm.LoadFile(const AFileName: String);
var
lTag: TTag;
item: TListItem;
i: Integer;
ms: TMemoryStream;
suffix: String;
crs: TCursor;
jpg: TJpegImage;
begin
FImageLoaded := false;
FFileName := AFileName;
Image.Picture.Assign(nil);
PreviewImage.Picture.Assign(nil);
TagListView.Items.BeginUpdate;
try
TagListView.Clear;
FImgInfo.Free;
FImgInfo := TImgInfo.Create;
try
try
if CbDecodeMakerNotes.Checked then
FImgInfo.MetadataKinds := FImgInfo.MetadataKinds + [mdkExif] - [mdkExifNoMakerNotes]
else
FImgInfo.MetadataKinds := FImgInfo.MetadataKinds - [mdkExif] + [mdkExifNoMakerNotes];
FImgInfo.LoadFromFile(AFileName);
Messages.Hide;
except
on E:EFpExif do begin
Messages.Lines.Text := E.Message;
Messages.Show;
end;
end;
if FImgInfo.HasExif then begin
FImageOrientation := FImgInfo.ExifData.ImgOrientation;
FImgInfo.ExifData.ExportOptions := FImgInfo.ExifData.ExportOptions + [eoTruncateBinary];
for i := 0 to FImgInfo.ExifData.TagCount-1 do begin
lTag := FImgInfo.ExifData.TagByIndex[i];
if lTag is TMakerNoteStringTag then
suffix := ':' + IntToStr(TMakerNoteStringTag(lTag).Index)
else if lTag is TMakerNoteIntegerTag then
suffix := ':' + IntToStr(TMakerNoteIntegerTag(lTag).Index)
else if lTag is TMakerNoteFloatTag then
suffix := ':' + IntToStr(TMakerNoteFloatTag(lTag).Index)
else
suffix := '';
if lTag is TVersionTag then
TVersionTag(lTag).Separator := '.';
item := TagListView.Items.Add;
item.Data := lTag;
item.Caption := 'EXIF.' + NiceGroupNames[lTag.Group];
item.SubItems.Add(Format('$%.04x:$%.04x%s', [lTag.TagIDRec.Parent, lTag.TagIDRec.Tag, suffix]));
item.SubItems.Add(lTag.Description);
item.SubItems.Add(lTag.AsString);
end;
lTag := FImgInfo.ExifData.TagByName['DateTimeOriginal'];
if lTag <> nil then
EdChangeDate.Text := DateTimeToStr(TDateTimeTag(lTag).AsDateTime)
else
EdChangeDate.Text := '';
DateTimePanel.Show;
end else
DateTimePanel.Hide;
if FImgInfo.HasIptc then begin
for i := 0 to FImgInfo.IptcData.TagCount-1 do begin
lTag := FImgInfo.IptcData.TagByIndex[i];
item := TagListView.Items.Add;
item.Data := lTag;
item.Caption := 'IPTC';
item.SubItems.Add(lTag.Description);
item.SubItems.Add(lTag.AsString);
end;
end;
if FImgInfo.HasThumbnail then begin
ms := TMemoryStream.Create;
try
try
FImgInfo.SaveThumbnailToStream(ms);
ms.Position := 0;
jpg := TJpegImage.Create;
try
jpg.LoadFromStream(ms);
PreviewImage.Picture.Assign(jpg);
finally
jpg.Free;
end;
// TMyPicture(PreviewImage.Picture).LoadFromStream(ms); // cast needed for old Delphi
//RotateBitmap(PreviewImage.Picture.Bitmap, FImageOrientation);
except
on E:Exception do Messages.Lines.Add(E.Message);
end;
finally
ms.Free;
end;
end else
;
//PreviewImage.Picture.Clear;
if FImgInfo.HasWarnings then begin
Messages.Lines.Text := FImgInfo.Warnings;
Messages.Show;
end;
except
on E:Exception do begin
FreeAndNil(FImgInfo);
Messages.Lines.Text := E.Message;
Messages.Show;
end;
end;
if PageControl1.ActivePage = PgImage then begin
crs := Screen.Cursor;
try
Screen.Cursor := crHourglass;
Image.Picture.LoadFromFile(AFileName);
{
if Assigned(FImgInfo) and Assigned(FImgInfo.ExifData) then
RotateBitmap(Image.Picture.Bitmap, FImageOrientation);
}
FImageLoaded := true;
finally
Screen.Cursor := crs;
end;
end;
UpdateCaption;
finally
TagListView.Items.EndUpdate;
//TagListView.Sort;
end;
end;
procedure TMainForm.LoadFromIni;
var
ini: TCustomIniFile;
i, W, H, L, T: Integer;
rct: TRect;
s: String;
b: Boolean;
begin
ini := TIniFile.Create(CalcIniName);
try
L := ini.ReadInteger('MainForm', 'Left', Left);
T := ini.ReadInteger('MainForm', 'Top', Top);
W := ini.ReadInteger('MainForm', 'Width', Width);
H := ini.ReadInteger('MainForm', 'Height', Height);
rct := Screen.DesktopRect;
if W > rct.Right - rct.Left then W := rct.Right - rct.Left;
if H > rct.Bottom - rct.Top then H := rct.Bottom - rct.Top;
if L < rct.Left then L := rct.Left;
if T < rct.Top then T := rct.Top;
if L+W > rct.Right then L := rct.Right - W;
if T+H > rct.Bottom then T := rct.Bottom - H;
SetBounds(L, T, W, H);
s := ini.ReadString('MainForm', 'Path', '');
if s <> '' then begin
ShellTreeView.Directory := s;
ShellListView.Directory := s;
end;
w := ini.ReadInteger('MainForm', 'LeftPanelWidth', 0);
if w <> 0 then ShellPanel.Width := w;
h := ini.ReadInteger('MainForm', 'TreeHeight', 0);
if h <> 0 then ShellTreeView.Height := h;
for i:=0 to TagListView.Columns.Count-1 do begin
w := ini.ReadInteger('TagList', 'ColWidth'+IntToStr(i), 0);
if w <> 0 then
TagListView.Columns[i].Width := w;
end;
finally
ini.Free;
end;
end;
procedure TMainForm.PageControl1Change(Sender: TObject);
var
crs: TCursor;
begin
if not FImageLoaded then begin
crs := Screen.Cursor;
try
Screen.Cursor := crHourglass;
Image.Picture.LoadFromFile(FFileName);
FImageLoaded := true;
finally
Screen.Cursor := crs;
end;
end;
end;
procedure TMainForm.SaveToIni;
var
ini: TCustomIniFile;
i: Integer;
begin
ini := TIniFile.Create(CalcIniName);
try
if WindowState = wsNormal then begin
ini.WriteInteger('MainForm', 'Left', Left);
ini.WriteInteger('MainForm', 'Top', Top);
ini.WriteInteger('MainForm', 'Width', Width);
ini.WriteInteger('MainForm', 'Height', Height);
end;
ini.WriteString('MainForm', 'Path', ShellTreeView.Directory);
ini.WriteInteger('MainForm', 'LeftPanelWidth', ShellPanel.Width);
ini.WriteInteger('MainForm', 'TreeHeight', ShellTreeView.Height);
for i:=0 to TagListView.Columns.Count-1 do
ini.WriteInteger('TagList', 'ColWidth'+IntToStr(i), TagListView.Columns[i].Width);
finally
ini.Free;
end;
end;
procedure TMainForm.ShellListViewChange(Sender: TObject);
var
fn: String;
begin
fn := ShellListView.FileName;
if fn <> '' then begin
ShellTreeView.Directory := ExtractFilePath(fn);
LoadFile(fn);
end;
end;
procedure TMainForm.ShellTreeViewChange(Sender: TObject);
begin
ShellListview.Directory := ShellTreeView.Directory;
end;
(*
procedure TMainForm.ShellListViewSelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
var
dir, fn: String;
begin
if Selected then
begin
dir := ShellTreeView.GetPathFromNode(ShellTreeView.Selected);
fn := Item.Caption;
LoadFile(dir + fn);
end;
end;
procedure TMainForm.ShellTreeViewGetImageIndex(Sender: TObject; Node: TTreeNode);
begin
if Node = nil then
exit;
if Node.Level = 0 then
Node.ImageIndex := 0
else
if Node.Expanded then
Node.ImageIndex := 2
else
Node.ImageIndex := 1;
Node.SelectedIndex := Node.ImageIndex;
end;
procedure TMainForm.ShellTreeViewSelectionChanged(Sender: TObject);
begin
TagListView.Items.Clear;
PreviewImage.Picture.Assign(nil);
ShellTreeViewGetImageIndex(nil, ShellTreeView.Selected);
FreeAndNil(FImgInfo);
UpdateCaption;
end;
*)
procedure TMainForm.TagListViewCompare(Sender: TObject; Item1, Item2: TListItem;
Data: Integer; var Compare: Integer);
var
tag1, tag2: TTag;
begin
tag1 := TTag(Item1.Data);
tag2 := TTag(Item2.Data);
Compare := CompareValue(ord(tag1.Group), ord(tag2.Group));
if Compare = 0 then
Compare := CompareText(Item1.SubItems[0], Item2.SubItems[0]);
end;
procedure TMainForm.TagListViewSelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
const
{ TTagType:
ttUInt8 = 1, ttString, ttUInt16, ttUInt32, ttURational,
ttSInt8, ttBinary, ttSInt16, ttSInt32, ttSRational,
ttSingle, ttDouble,
ttIFD // rarely used, in Olympus maker notes
}
TAGTYPE_NAMES: array[TTagType] of string = (
'BYTE', 'ASCII', 'UINT16', 'UINT32', 'URATIONAL',
'SBYTE', 'BINARY', 'SINT16', 'SINT32', 'SRATIONAL',
'SINGLE', 'DOUBLE',
'IFD'
);
var
lTag: TTag;
s: String;
tagID: TTagIDRec;
begin
if Selected then begin
lTag := TTag(Item.Data);
if lTag <> nil then begin
tagID := TTagIDRec(lTag.TagID);
Statusbar1.Panels[0].Text := Format('ID %d [$%.4x]', [tagID.Tag, tagID.Tag]);
Statusbar1.Panels[1].Text := Format('Parent %d [$%.4x]', [tagID.Parent, tagID.Parent]);
Statusbar1.Panels[2].Text := 'Name: ' + lTag.Name;
Statusbar1.Panels[3].Text := 'Type: ' + TAGTYPE_NAMES[lTag.TagType];
Statusbar1.Panels[4].Text := 'Elements: ' + IntToStr(lTag.Count);
exit;
end;
end;
Statusbar1.Panels[0].Text := '';
Statusbar1.Panels[1].Text := '';
Statusbar1.Panels[2].Text := '';
Statusbar1.Panels[3].Text := '';
Statusbar1.Panels[4].Text := '';
end;
procedure TMainForm.UpdateCaption;
var
fn: String;
begin
if FImgInfo <> nil then
FileNameInfo.Caption := Format(
'File: %s' + LineEnding +
'Size: %d kB' + LineEnding +
'Date: %s', [
FImgInfo.Filename, FImgInfo.FileSize div 1024, DateTimeToStr(FImgInfo.FileDate)])
else
if FFilename <> '' then
FilenameInfo.Caption := Format('File: %s', [FFileName])
else
FilenameInfo.caption := '< no file >';
end;
end.

View File

@ -0,0 +1,15 @@
program ExifSimpleDemo;
uses
Forms,
sdMain in 'sdMain.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
MainForm.BeforeRun;
Application.Run;
end.

View File

@ -0,0 +1,139 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{927DB824-6C5B-4A5F-8555-627D51D654D3}</ProjectGuid>
<MainSource>ExifSimpleDemo.dpr</MainSource>
<Base>True</Base>
<Config Condition="'$(Config)'==''">Debug</Config>
<TargetedPlatforms>1</TargetedPlatforms>
<AppType>Application</AppType>
<FrameworkType>VCL</FrameworkType>
<ProjectVersion>18.6</ProjectVersion>
<Platform Condition="'$(Platform)'==''">Win32</Platform>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
<Base_Win32>true</Base_Win32>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''">
<Base_Win64>true</Base_Win64>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_1)'!=''">
<Cfg_1>true</Cfg_1>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''">
<Cfg_1_Win32>true</Cfg_1_Win32>
<CfgParent>Cfg_1</CfgParent>
<Cfg_1>true</Cfg_1>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''">
<Cfg_2>true</Cfg_2>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''">
<Cfg_2_Win32>true</Cfg_2_Win32>
<CfgParent>Cfg_2</CfgParent>
<Cfg_2>true</Cfg_2>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Base)'!=''">
<DCC_E>false</DCC_E>
<DCC_F>false</DCC_F>
<DCC_K>false</DCC_K>
<DCC_N>false</DCC_N>
<DCC_S>false</DCC_S>
<DCC_ImageBase>00400000</DCC_ImageBase>
<SanitizedProjectName>ExifSimpleDemo</SanitizedProjectName>
<DCC_Namespace>Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace)</DCC_Namespace>
<VerInfo_Locale>1031</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=</VerInfo_Keys>
<DCC_UnitSearchPath>..\..;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win32)'!=''">
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
<BT_BuildType>Debug</BT_BuildType>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys>
<VerInfo_Locale>1033</VerInfo_Locale>
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
<AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
<UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
<UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win64)'!=''">
<UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
<UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1)'!=''">
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
<DCC_DebugInformation>0</DCC_DebugInformation>
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
<AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
<DCC_Optimize>false</DCC_Optimize>
<DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2_Win32)'!=''">
<AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Locale>1033</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys>
</PropertyGroup>
<ItemGroup>
<DelphiCompile Include="$(MainSource)">
<MainSource>MainSource</MainSource>
</DelphiCompile>
<DCCReference Include="sdmain.pas">
<Form>Form1</Form>
</DCCReference>
<BuildConfiguration Include="Debug">
<Key>Cfg_2</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
<BuildConfiguration Include="Release">
<Key>Cfg_1</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
</ItemGroup>
<ProjectExtensions>
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
<Borland.ProjectType/>
<BorlandProject>
<Delphi.Personality>
<Source>
<Source Name="MainSource">ExifSimpleDemo.dpr</Source>
</Source>
<Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k260.bpl">Microsoft Office 2000 Beispiele für gekapselte Komponenten für Automatisierungsserver</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dclofficexp260.bpl">Microsoft Office XP Beispiele für gekapselte Komponenten für Automation Server</Excluded_Packages>
</Excluded_Packages>
</Delphi.Personality>
<Platforms>
<Platform value="Win32">True</Platform>
<Platform value="Win64">False</Platform>
</Platforms>
</BorlandProject>
<ProjectFileVersion>12</ProjectFileVersion>
</ProjectExtensions>
<Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
<Import Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj" Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')"/>
</Project>

View File

@ -0,0 +1,186 @@
object MainForm: TMainForm
Left = 329
Top = 131
Caption = 'MainForm'
ClientHeight = 477
ClientWidth = 788
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = True
OnCreate = FormCreate
OnDestroy = FormDestroy
DesignSize = (
788
477)
PixelsPerInch = 96
TextHeight = 13
object Thumbnail: TImage
Left = 620
Top = 41
Width = 160
Height = 151
Anchors = [akTop, akRight]
Center = True
Proportional = True
Stretch = True
end
object Label1: TLabel
Left = 620
Top = 380
Width = 50
Height = 13
Anchors = [akRight, akBottom]
Caption = 'New value'
Color = clBtnFace
ParentColor = False
end
object Label2: TLabel
Left = 618
Top = 330
Width = 18
Height = 13
Anchors = [akRight, akBottom]
Caption = 'Tag'
Color = clBtnFace
ParentColor = False
end
object BtnLoad: TButton
Left = 728
Top = 8
Width = 52
Height = 25
Anchors = [akTop, akRight]
Caption = 'Load'
TabOrder = 0
OnClick = BtnLoadClick
end
object CbFilename: TComboBox
Left = 8
Top = 9
Width = 677
Height = 21
Anchors = [akLeft, akTop, akRight]
TabOrder = 1
Text = '..\test-image.jpg'
OnSelect = CbFilenameSelect
end
object Memo: TMemo
Left = 8
Top = 41
Width = 604
Height = 384
Anchors = [akLeft, akTop, akRight, akBottom]
ScrollBars = ssBoth
TabOrder = 2
end
object BtnBrowse: TButton
Left = 689
Top = 8
Width = 35
Height = 25
Anchors = [akTop, akRight]
Caption = '...'
TabOrder = 3
OnClick = BtnBrowseClick
end
object Panel1: TPanel
Left = 0
Top = 442
Width = 788
Height = 35
Align = alBottom
BevelOuter = bvNone
TabOrder = 4
DesignSize = (
788
35)
object CbVerbosity: TComboBox
Left = 8
Top = 1
Width = 192
Height = 21
Style = csDropDownList
ItemIndex = 2
TabOrder = 0
Text = 'Hex tag IDs'
Items.Strings = (
'Tag names only'
'Decimal tag IDs'
'Hex tag IDs')
end
object CbDecodeValue: TCheckBox
Left = 208
Top = 3
Width = 96
Height = 19
Caption = 'Decode values'
Checked = True
State = cbChecked
TabOrder = 1
OnClick = CbDecodeValueClick
end
object CbTruncateBinaryTags: TCheckBox
Left = 312
Top = 3
Width = 127
Height = 19
Caption = 'Truncate binary tags'
Checked = True
State = cbChecked
TabOrder = 2
OnClick = CbTruncateBinaryTagsClick
end
object CbBinaryAsASCII: TCheckBox
Left = 447
Top = 3
Width = 123
Height = 19
Caption = 'Binary tags as ASCII'
TabOrder = 3
OnClick = CbBinaryAsASCIIClick
end
object BtnSave: TButton
Left = 650
Top = 0
Width = 130
Height = 25
Anchors = [akTop, akRight]
Caption = 'Save as "_modified"'
Enabled = False
TabOrder = 4
OnClick = BtnSaveClick
end
end
object CbTags: TComboBox
Left = 620
Top = 349
Width = 160
Height = 21
Style = csDropDownList
Anchors = [akRight, akBottom]
DropDownCount = 32
TabOrder = 5
OnSelect = CbTagsSelect
end
object EdNewTagValue: TEdit
Left = 620
Top = 399
Width = 160
Height = 21
Anchors = [akRight, akBottom]
TabOrder = 6
end
object OpenDialog: TOpenDialog
DefaultExt = '.jpg'
Filter =
'All supported images (*.jpg; *.jpeg; *.jfe); *.tiff; *.tif|*.jpg' +
';*.jpeg;*.jfe;*.tiff;*.tif|JPG files (*.jpg; *.jpeg; *.jfe)|*.jp' +
'g;*.jpeg;*.jfe|TIFF files (*.tiff; *.tif)|*.tiff;*.tif'
Left = 248
Top = 168
end
end

View File

@ -0,0 +1,449 @@
unit sdMain;
interface
uses
Classes, SysUtils, Types, Forms, Controls, Graphics, Dialogs, StdCtrls,
ExtCtrls, fpeMetadata, fpeMakerNote;
type
{ TMainForm }
TMainForm = class(TForm)
BtnLoad: TButton;
BtnBrowse: TButton;
BtnSave: TButton;
CbDecodeValue: TCheckBox;
CbFilename: TComboBox;
CbVerbosity: TComboBox;
CbTruncateBinaryTags: TCheckBox;
CbBinaryAsASCII: TCheckBox;
CbTags: TComboBox;
EdNewTagValue: TEdit;
Label1: TLabel;
Label2: TLabel;
Thumbnail: TImage;
Memo: TMemo;
OpenDialog: TOpenDialog;
Panel1: TPanel;
procedure BtnLoadClick(Sender: TObject);
procedure BtnBrowseClick(Sender: TObject);
procedure BtnSaveClick(Sender: TObject);
procedure CbBinaryAsASCIIClick(Sender: TObject);
procedure CbDecodeValueClick(Sender: TObject);
procedure CbFilenameSelect(Sender: TObject);
procedure CbTagsSelect(Sender: TObject);
procedure CbTruncateBinaryTagsClick(Sender: TObject);
procedure CbVerbosityChange(Sender: TObject);
procedure EdNewTagValueEditingDone(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FImgInfo: TImgInfo;
FModified: Boolean;
procedure AddToHistory(AFileName: String);
procedure DisplayMetadata;
procedure LoadFile(const AFileName: String);
procedure LoadThumbnail;
procedure PopulateTagCombo;
procedure ReadFromIni;
procedure UpdateCaption(AInit: Boolean);
procedure WriteToIni;
public
procedure BeforeRun;
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
uses
IniFiles, jpeg, fpeGlobal, fpeTags, fpeExifData;
{ TMainForm }
procedure TMainForm.AddToHistory(AFileName: String);
var
i: Integer;
begin
if (AFileName = '') or (not FileExists(AFileName)) then
exit;
i := CbFileName.Items.Indexof(AFileName);
if i > -1 then
CbFileName.Items.Delete(i);
CbFileName.Items.Insert(0, AFileName);
CbFileName.ItemIndex := 0;
end;
procedure TMainForm.BeforeRun;
begin
ReadFromIni;
end;
procedure TMainForm.BtnBrowseClick(Sender: TObject);
var
olddir: String;
begin
olddir := GetCurrentDir;
OpenDialog.FileName := '';
if OpenDialog.Execute then begin
AddToHistory(OpenDialog.Filename);
SetCurrentDir(oldDir);
LoadFile(OpenDialog.Filename);
end;
end;
procedure TMainForm.BtnLoadClick(Sender: TObject);
begin
LoadFile(CbFilename.Text);
end;
procedure TMainForm.BtnSaveClick(Sender: TObject);
var
fn, ext: String;
begin
ext := ExtractFileExt(CbFilename.Text);
fn := ChangeFileExt(CbFileName.Text, '');
if pos('_modified', fn) <> Length(fn) - Length('modified') then
fn := fn + '_modified' + ext
else
fn := CbFilename.Text;
FImgInfo.SaveToFile(fn);
MessageDlg(Format('File saved as "%s"', [fn]), mtInformation, [mbOK], 0);
end;
procedure TMainForm.CbBinaryAsASCIIClick(Sender: TObject);
begin
DisplayMetadata;
end;
procedure TMainForm.CbDecodeValueClick(Sender: TObject);
begin
DisplayMetaData;
end;
procedure TMainForm.CbFilenameSelect(Sender: TObject);
begin
LoadFile(CbFileName.Text);
end;
procedure TMainForm.CbTagsSelect(Sender: TObject);
var
lTag: TTag;
decoded: Boolean;
begin
if FImgInfo.HasExif then begin
lTag := FImgInfo.ExifData.TagByName[CbTags.Text];
decoded := lTag.DecodeValue;
lTag.DecodeValue := false;
EdNewTagValue.Text := lTag.AsString;
lTag.DecodeValue := decoded;
end;
end;
procedure TMainForm.CbTruncateBinaryTagsClick(Sender: TObject);
begin
DisplayMetadata;
end;
procedure TMainForm.CbVerbosityChange(Sender: TObject);
begin
DisplayMetadata;
end;
procedure TMainForm.DisplayMetadata;
const
SEPARATOR = ': ';
var
exportOptions: TExportOptions;
begin
Memo.Lines.Clear;
if FImgInfo <> nil then begin
exportOptions := [eoShowTagName];
case CbVerbosity.ItemIndex of
1: Include(exportOptions, eoShowDecimalTagID);
2: Include(exportOptions, eoShowHexTagID);
end;
if CbDecodeValue.Checked then
Include(exportOptions, eoDecodeValue) else
Exclude(exportOptions, eoDecodeValue);
if CbTruncateBinaryTags.Checked then
Include(exportOptions, eoTruncateBinary) else
Exclude(exportOptions, eoTruncateBinary);
if CbBinaryAsASCII.Checked then
Include(exportOptions, eoBinaryAsASCII) else
Exclude(exportOptions, eoBinaryAsASCII);
Memo.Lines.BeginUpdate;
try
if FImgInfo.ExifData <> nil then begin
FImgInfo.ExifData.ExportOptions := exportOptions;
FImgInfo.ExifData.ExportToStrings(Memo.Lines, SEPARATOR);
end;
if FImgInfo.IptcData <> nil then
FImgInfo.IptcData.ExportToStrings(Memo.Lines, exportOptions, SEPARATOR);
finally
Memo.Lines.EndUpdate;
Memo.Invalidate;
end;
end;
end;
procedure TMainForm.EdNewTagValueEditingDone(Sender: TObject);
var
lTag: TTag;
i: Integer;
f: Double;
dt: TDateTime;
begin
if FImgInfo.HasExif then begin
lTag := FImgInfo.ExifData.TagByName[CbTags.Text];
if lTag = nil then begin
MessageDlg('Tag not found.', mtError, [mbOK], 0);
exit;
end;
if lTag.ReadOnly then begin
MessageDlg('This tag is readonly.', mtError, [mbOK], 0);
exit;
end;
if (lTag is TDateTimeTag) then begin
if TryStrToDateTime(EdNewTagValue.Text, dt) then begin
FModified := FModified or (TDateTimeTag(lTag).AsDateTime <> dt);
TDateTimeTag(lTag).AsDateTime := dt;
end else begin
MessageDlg('Date/time value expected for this kind of tag.', mtError, [mbOK], 0);
exit;
end;
end else
if (lTag is TShutterSpeedTag) then begin
FModified := true;
TShutterSpeedTag(lTag).AsString := EdNewTagValue.Text;
end else
if (lTag is TStringTag) then begin
FModified := FModified or (EdNewTagValue.Text <> TStringTAg(lTag).AsString);
TStringTag(lTag).AsString := EdNewTagValue.Text;
end else
if (lTag is TIntegerTag) and (lTag.Count = 1) then begin
if TryStrToInt(EdNewTagValue.Text, i) then begin
FModified := FModified or (TIntegerTag(lTag).AsInteger <> i);
TIntegerTag(lTag).AsInteger := i;
end else begin
MessageDlg('Integer value expected for this kind of tag.', mtError, [mbOK], 0);
exit;
end;
end else
if (lTag is TFloatTag) and (lTag.Count = 1) then begin
if TryStrToFloat(EdNewTagValue.Text, f) then begin
FModified := FModified or (TFloatTag(lTag).AsFloat <> f);
TFloatTag(lTag).AsFloat := f;
end else begin
MessageDlg('Floating point value expected for this kind of tag.', mtError, [mbOK], 0);
exit;
end;
end;
DisplayMetadata;
UpdateCaption(false);
end;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
UpdateCaption(true);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
WriteToIni;
FImgInfo.Free;
end;
procedure TMainForm.LoadFile(const AFileName: String);
var
exportOptions: TExportOptions;
begin
if FImgInfo = nil then
FImgInfo := TImgInfo.Create;
try
FImgInfo.LoadFromFile(ExpandFileName(CbFilename.Text));
if FImgInfo.ExifData <> nil then begin
DisplayMetadata;
LoadThumbnail;
PopulateTagCombo;
AddToHistory(AFilename);
end else begin
Thumbnail.Picture.Assign(nil);
CbTags.Items.Clear;
end;
if FImgInfo.HasWarnings then begin
Memo.Lines.Add('');
Memo.Lines.Add('*** WARNINGS ****');
Memo.Lines.Add(FImgInfo.Warnings);
end;
UpdateCaption(false);
FModified := false;
BtnSave.Enabled := FImgInfo.ImgFormat = ifJpeg;
except
on E:EFpExifReader do begin
Memo.Lines.Text := E.Message;
Thumbnail.Picture.Assign(nil);
CbTags.Items.Clear;
ShowMessage(E.Message);
end;
end;
end;
procedure TMainForm.LoadThumbnail;
var
ms: TMemoryStream;
jpg: TJpegImage;
begin
if not FImgInfo.HasThumbnail then
exit;
//if (FImgInfo.ExifData = nil) or (not FImgInfo.Exifdata.HasThumbnail) then
// exit;
ms := TMemoryStream.Create;
try
FImgInfo.SaveThumbnailToStream(ms);
ms.Position := 0;
jpg := TJpegImage.Create;
try
jpg.LoadFromStream(ms);
Thumbnail.Picture.Assign(jpg);
finally
jpg.Free;
end;
finally
ms.Free;
end;
end;
procedure TMainForm.PopulateTagCombo;
var
i: Integer;
L: TStrings;
lTag: TTag;
begin
L := TStringList.Create;
try
if FImgInfo.HasExif then
for i:=0 to FImgInfo.ExifData.TagCount-1 do begin
lTag := FImgInfo.ExifData.TagByIndex[i];
if not lTag.ReadOnly or lTag.IsVolatile then
L.Add(GroupNames[lTag.Group] + '.' + lTag.Name);
end;
CbTags.Items.Assign(L);
CbTags.ItemIndex := -1;
finally
L.Free;
end;
end;
function CreateIni: TCustomIniFile;
begin
Result := TMemIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
end;
procedure TMainForm.ReadFromIni;
var
ini: TCustomIniFile;
list: TStrings;
i: Integer;
W, H, L, T: Integer;
R: TRect;
begin
ini := CreateIni;
try
list := TStringList.Create;
try
if WindowState = wsNormal then begin
W := ini.ReadInteger('MainForm', 'Width', Width);
H := ini.ReadInteger('MainForm', 'Height', Height);
L := ini.ReadInteger('MainForm', 'Left', Left);
T := ini.ReadInteger('MainForm', 'Top', Top);
R := Screen.DesktopRect;
if W > R.Right - R.Left then W := R.Right - R.Left;
if L+W > R.Right then L := R.Right - W;
if L < R.Left then L := R.Left;
if H > R.Bottom - R.Top then H := R.Bottom - R.Top;
if T+H > R.Bottom then T := R.Bottom - H;
if T < R.Top then T := R.Top;
SetBounds(L, T, W, H);
end;
CbVerbosity.ItemIndex := ini.ReadInteger('Settings', 'Verbosity', CbVerbosity.ItemIndex);
CbDecodeValue.Checked := ini.ReadBool('Settings', 'DecodeValue', CbDecodeValue.Checked);
ini.ReadSection('History', list);
for i:=list.Count-1 downto 0 do // count downward because AddToHistory adds to the beginning of the list
AddToHistory(ini.ReadString('History', list[i], ''));
if CbFilename.Items.Count = 0 then
AddToHistory('..\test-image.jpg');
CbFilename.ItemIndex := 0;
finally
list.Free;
end;
finally
ini.Free;
end;
end;
procedure TMainForm.UpdateCaption(AInit: Boolean);
const
DEFAULT_CAPTION = 'Picture metadata viewer';
var
mask: String;
begin
if AInit then
Caption := DEFAULT_CAPTION
else
begin
if FModified then
mask := '%s - [*] %s' else
mask := '%s - %s';
if FImgInfo.Filename <> '' then
Caption := Format(mask, [DEFAULT_CAPTION, '"' + FImgInfo.FileName + '"'])
else
Caption := Format(mask, [DEFAULT_CAPTION, 'ERROR']);
end;
end;
procedure TMainForm.WriteToIni;
var
ini: TCustomIniFile;
i: Integer;
begin
ini := CreateIni;
try
ini.WriteInteger('MainForm', 'Left', Left);
ini.WriteInteger('MainForm', 'Top', Top);
ini.WriteInteger('MainForm', 'Width', Width);
ini.WriteInteger('MainForm', 'Height', Height);
ini.WriteInteger('Settings', 'Verbosity', CbVerbosity.ItemIndex);
ini.WriteBool('Settings', 'DecodeValue', CbDecodeValue.Checked);
for i:=0 to CbFileName.Items.Count-1 do
if (CbFilename.Items[i] <> '') and FileExists(CbFilename.Items[i]) then
ini.WriteString('History', 'Item'+IntToStr(i+1), CbFilename.Items[i]);
ini.UpdateFile;
finally
ini.Free;
end;
end;
end.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB