fpexif: Delphi support for XMP metadata.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9012 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2023-11-06 16:42:17 +00:00
parent 3040781187
commit 035442ee36
9 changed files with 297 additions and 63 deletions

View File

@ -10,11 +10,9 @@ object MainForm: TMainForm
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
@ -181,7 +179,7 @@ object MainForm: TMainForm
Top = 21
Width = 647
Height = 550
ActivePage = PgMetadata
ActivePage = PgXMP
Align = alClient
TabOrder = 1
OnChange = PageControl1Change
@ -241,6 +239,87 @@ object MainForm: TMainForm
end
end
end
object PgXMP: TTabSheet
Caption = 'XMP'
ImageIndex = 2
object Splitter4: TSplitter
Left = 0
Top = 150
Width = 639
Height = 3
Cursor = crVSplit
Align = alTop
end
object XMPListView: TListView
Left = 0
Top = 0
Width = 639
Height = 150
Align = alTop
Columns = <
item
Caption = 'Description'
Width = 200
end
item
Caption = 'Value'
Width = 200
end>
TabOrder = 0
ViewStyle = vsReport
end
object XMPSynEdit: TMemo
Left = 0
Top = 153
Width = 639
Height = 335
Align = alClient
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Courier New'
Font.Style = []
ParentFont = False
TabOrder = 1
end
object Panel6: TPanel
Left = 0
Top = 488
Width = 639
Height = 34
Align = alBottom
BevelOuter = bvNone
TabOrder = 2
object cbProcessXMP: TCheckBox
Left = 8
Top = 8
Width = 129
Height = 17
Caption = 'Load && display XMP'
Checked = True
State = cbChecked
TabOrder = 0
end
object btnApplyChangesXMP: TButton
Left = 143
Top = 7
Width = 98
Height = 25
Caption = 'Apply changes'
TabOrder = 1
OnClick = btnApplyChangesXMPClick
end
object btnSaveXMP: TButton
Left = 247
Top = 7
Width = 75
Height = 25
Caption = 'Save'
TabOrder = 2
OnClick = btnSaveXMPClick
end
end
end
object PgImage: TTabSheet
Caption = 'Image'
object Image: TImage

View File

@ -1,8 +1,8 @@
unit mdvMain;
{$IFDEF FPC}
!!! THIS PROGRAM IS INTENDED FOR DELPHI ONLY
{$ENDIF}
!!! THIS PROGRAM IS INTENDED FOR DELPHI ONLY !!!
{$ENDIF}
interface
@ -44,6 +44,14 @@ type
Splitter2: TSplitter;
DriveComboBox1: TDriveComboBox;
Panel5: TPanel;
PgXMP: TTabSheet;
XMPListView: TListView;
Splitter4: TSplitter;
XMPSynEdit: TMemo;
Panel6: TPanel;
cbProcessXMP: TCheckBox;
btnApplyChangesXMP: TButton;
btnSaveXMP: TButton;
procedure BtnChangeDateClick(Sender: TObject);
procedure CbShowTagIDsChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
@ -63,6 +71,8 @@ type
procedure ShellListViewChange(Sender: TObject);
procedure DriveComboBox1Change(Sender: TObject);
procedure Panel5Resize(Sender: TObject);
procedure btnApplyChangesXMPClick(Sender: TObject);
procedure btnSaveXMPClick(Sender: TObject);
private
FFileName: String;
FImgInfo: TImgInfo;
@ -70,6 +80,7 @@ type
FImageOrientation: TExifOrientation;
procedure LoadFile(const AFileName: String);
procedure LoadFromIni;
procedure LoadXMPTags;
procedure SaveToIni;
procedure UpdateCaption;
@ -194,6 +205,24 @@ begin
//ShellListView.Parent.DoubleBuffered := true;
end;
procedure TMainForm.btnApplyChangesXMPClick(Sender: TObject);
var
ms: TMemoryStream;
begin
if Assigned(FImgInfo) and FImgInfo.HasXMP then
begin
ms := TMemoryStream.Create;
try
XMPSynEdit.Lines.SaveToStream(ms);
ms.Position := 0;
FImgInfo.XMPData.LoadFromStream(ms);
LoadXMPTags;
finally
ms.Free;
end;
end;
end;
procedure TMainForm.BtnChangeDateClick(Sender: TObject);
var
lTag: TTag;
@ -225,6 +254,19 @@ begin
FImgInfo.SaveToFile(fn);
end;
procedure TMainForm.btnSaveXMPClick(Sender: TObject);
var
fn: String;
begin
if Assigned(FImgInfo) then
begin
fn := FImgInfo.FileName;
fn := ChangeFileExt(fn, '') + '_modified' + ExtractFileExt(fn);
FImgInfo.SaveToFile(fn);
ShowMessage('Modified image saved as ' + fn);
end;
end;
procedure TMainForm.CbShowTagIDsChange(Sender: TObject);
var
c: TListColumn;
@ -290,6 +332,10 @@ begin
FImgInfo.MetadataKinds := FImgInfo.MetadataKinds + [mdkExif] - [mdkExifNoMakerNotes]
else
FImgInfo.MetadataKinds := FImgInfo.MetadataKinds - [mdkExif] + [mdkExifNoMakerNotes];
if CbProcessXMP.Checked then
FImgInfo.MetadataKinds := FImgInfo.MetaDataKinds + [mdkXMP]
else
FImgInfo.MetadataKinds := FImgInfo.MetaDataKinds - [mdkXMP];
FImgInfo.LoadFromFile(AFileName);
Messages.Hide;
except
@ -332,6 +378,22 @@ begin
end else
DateTimePanel.Hide;
if FImgInfo.HasXMP then begin
ms := TMemoryStream.Create;
try
FImgInfo.XMPData.SaveToStream(ms);
ms.Position := 0;
XMPSynEdit.Lines.LoadFromStream(ms);
LoadXMPTags;
finally
ms.Free;
end;
end else
begin
XMPListView.Clear;
XMPSynEdit.Clear;
end;
if FImgInfo.HasIptc then begin
for i := 0 to FImgInfo.IptcData.TagCount-1 do begin
lTag := FImgInfo.IptcData.TagByIndex[i];
@ -449,6 +511,24 @@ begin
end;
end;
procedure TMainForm.LoadXMPTags;
var
i: Integer;
item: TListItem;
begin
XMPListView.Items.BeginUpdate;
try
XMPListView.Items.Clear;
for i := 0 to FImgInfo.XMPData.TagCount-1 do begin
item := XMPListView.Items.Add;
item.Caption := FImgInfo.XMPData.TagName[i];
item.SubItems.Add(FImgInfo.XMPData.TagByIndex[i]);
end;
finally
XMPListView.Items.EndUpdate;
end;
end;
procedure TMainForm.PageControl1Change(Sender: TObject);
var
crs: TCursor;

View File

@ -141,9 +141,9 @@ object MainForm: TMainForm
Height = 539
Top = 23
Width = 647
ActivePage = PgMetadata
ActivePage = PgXMP
Align = alClient
TabIndex = 0
TabIndex = 1
TabOrder = 1
OnChange = PageControlChange
object PgMetadata: TTabSheet

View File

@ -126,7 +126,7 @@ type
TAdobeImageResourceBlock = class
public
Identifier: Word;
Name: String;
Name: AnsiString;
Data: TBytes;
end;

View File

@ -13,11 +13,7 @@ uses
{$IFDEF FPC}
LazUTF8,
{$ENDIF}
fpeGlobal,
{$IFDEF FPC}
fpeXmpData,
{$ENDIF}
fpeExifData, fpeIptcData;
fpeGlobal, fpeXmpData, fpeExifData, fpeIptcData;
type
TImgInfo = class;
@ -67,9 +63,7 @@ type
private
FExifData: TExifData;
FIptcData: TIptcData;
{$IFDEF FPC}
FXmpData: TXmpData;
{$ENDIF}
function GetComment: String;
function GetWarnings: String;
procedure SetComment(const AValue: String);
@ -92,18 +86,14 @@ type
function CreateExifData(ABigEndian: Boolean = false): TExifData;
function CreateIptcData: TIptcData;
{$IFDEF FPC}
function CreateXmpData: TXmpData;
{$ENDIF}
function HasComment: Boolean;
function HasExif: Boolean;
function HasIptc: Boolean;
function HasThumbnail: Boolean;
function HasWarnings: boolean;
{$IFDEF FPC}
function HasXMP: Boolean;
{$ENDIF}
{ Comment stored in the Jpeg COM segment }
property Comment: String read GetComment write SetComment;
@ -128,9 +118,7 @@ type
property ExifData: TExifData read FExifData;
property IptcData: TIptcData read FIptcData;
{$IFDEF FPC}
property XmpData: TXmpData read FXmpData;
{$ENDIF}
end;
@ -138,8 +126,7 @@ implementation
uses
Variants,
fpeStrConsts, fpeUtils, fpeExifReadWrite, {$IFDEF FPC}fpeXMPReadWrite,{$ENDIF}
fpeIptcReadWrite;
fpeStrConsts, fpeUtils, fpeExifReadWrite, fpeXMPReadWrite, fpeIptcReadWrite;
type
TJpegJFIFSegment = packed record
@ -276,9 +263,7 @@ begin
FWarnings.Free;
FExifData.Free;
FIptcData.Free;
{$IFDEF FPC}
FXmpData.Free;
{$ENDIF}
inherited;
end;
@ -298,7 +283,6 @@ begin
Result := FIptcData;
end;
{$IFDEF FPC}
function TImgInfo.CreateXmpData: TXmpData;
begin
FWarnings.Clear;
@ -306,7 +290,6 @@ begin
FXmpData := TXmpData.Create;
Result := FXmpData;
end;
{$ENDIF}
procedure TImgInfo.Error(const AMsg: String);
begin
@ -381,12 +364,10 @@ begin
Result := FWarnings.Count > 0;
end;
{$IFDEF FPC}
function TImgInfo.HasXMP: Boolean;
begin
Result := FXmpData <> nil;
end;
{$ENDIF}
procedure TImgInfo.LoadFromFile(const AFileName: String);
var
@ -441,7 +422,7 @@ var
{$IFDEF FPC}
s: RawByteString = '';
{$ELSE}
s: String;
s: AnsiString;
{$ENDIF}
begin
// Write the header segment and all metadata segments stored in TImgInfo
@ -588,7 +569,6 @@ begin
finally
reader.Free;
end;
{$IFDEF FPC}
end else
if HasXMPHeader(AStream) and (mdkXMP in FMetadataKinds) then
begin
@ -602,7 +582,6 @@ begin
raise EFpExifReader.Create('Error reading XMP data: ' + E.Message);
end;
end;
{$ENDIF}
end;
end;
M_IPTC:
@ -842,7 +821,6 @@ begin
end;
end;
{$IFDEF FPC}
// XMP --> Write another APP1 segment
if (mdkXMP in FMetadataKinds) and HasXMP then
begin
@ -853,8 +831,7 @@ begin
writer.Free;
end;
end;
{$ENDIF}
// Write IPTCSegment (APP13)
if (mdkIPTC in FMetadataKinds) and HasIPTC then begin
writer := TIptcWriter.Create(Self);

View File

@ -20,24 +20,28 @@ uses
Classes, SysUtils, contnrs,
{$IFDEF FPC}
laz2_dom, laz2_xmlread,
{$ELSE}
XMLDoc, XMLIntf,
{$ENDIF}
fpeGlobal, fpeTags;
type
TXMPData = class
private
FData: String;
FData: AnsiString;
{$IFDEF FPC}
FDoc: TXMLDocument;
{$ELSE}
FDoc: IXMLDocument;
{$ENDIF}
FTags: TStringList;
function GetTagByIndex(AIndex: Integer): String;
function GetTagByName(ATagName: String): String;
function GetTagName(AIndex: Integer): String;
function GetTagCount: Integer;
protected
{$IFDEF FPC}
procedure Create_RDFDescription_Tags(ANode: TDOMNode);
procedure Create_RDFDescription_Tags(ANode: {$IFDEF FPC}TDOMNode{$ELSE}IXMLNode{$ENDIF});
procedure CreateTags;
{$ENDIF}
public
constructor Create;
destructor Destroy; override;
@ -69,61 +73,120 @@ begin
inherited;
end;
{$IFDEF FPC}
procedure TXMPData.Create_RDFDescription_Tags(ANode: TDOMNode);
procedure TXMPData.Create_RDFDescription_Tags(ANode: {$IFDEF FPC}TDOMNode{$ELSE}IXMLNode{$ENDIF});
var
{$IFDEF FPC}
node: TDOMNode;
nodeName: String;
i: Integer;
attr: TDOMNode;
{$ELSE}
node: IXMLNode;
attr: IXMLNode;
{$ENDIF}
nodeName: String;
i, n: Integer;
lTagName, lTagValue: String;
lTag: TTag;
begin
while ANode <> nil do begin
nodeName := ANode.NodeName;
{$IFDEF FPC}
for i := 0 to ANode.Attributes.Length-1 do
begin
attr := ANode.Attributes.Item[i];
lTagName := attr.NodeName;
lTagValue := attr.NodeValue;
lTagValue := attr.nodeValue;
FTags.Add(lTagName + '=' + lTagValue);
end;
if ANode.HasChildNodes then
begin
node := ANode.FirstChild;
while node <> nil do
while node <> nil do
begin
nodeName := node.NodeName;
{$IFDEF FPC}
lTagValue := node.TextContent;
{$ELSE}
lTagValue := node.NodeValue;
{$ENDIF}
if lTagName <> '' then
FTags.Add(nodeName + '=' + lTagValue);
node := node.NextSibling;
end;
end;
ANode := ANode.NextSibling;
{$ELSE}
for i := 0 to ANode.AttributeNodes.Count-1 do
begin
attr := ANode.AttributeNodes[i];
lTagName := attr.NodeName;
lTagValue := attr.NodeValue;
FTags.Add(lTagName + '=' + lTagValue);
end;
if ANode.HasChildNodes then
begin
node := ANode.ChildNodes.First;
while node <> nil do
begin
nodeName := node.NodeName;
{$IFDEF FPC}
lTagValue := node.TextContent;
{$ELSE}
lTagValue := node.NodeValue;
{$ENDIF}
if lTagName <> '' then
FTags.Add(nodeName + '=' + lTagValue);
node := node.NextSibling;
end;
end;
ANode := ANode.NextSibling;
{$ENDIF}
end;
end;
procedure TXMPData.CreateTags;
var
stream: TStringStream;
{$IFDEF FPC}
node: TDOMNode;
{$ELSE}
node: IXMLNode;
{$ENDIF}
nodeName: String;
stream: TStringStream;
begin
{$IFDEF FPC}
FDoc.Free;
{$ENDIF}
stream := TStringStream.Create(FData);
try
{$IFDEF FPC}
ReadXMLFile(FDoc, stream);
{$ELSE}
FDoc := TXMLDocument.Create(nil);
FDoc.Options := FDoc.Options - [doNodeAutoCreate];
FDoc.LoadFromStream(stream, xetUTF_8);
{$ENDIF}
finally
stream.Free;
end;
FTags.Clear;
try
{$IFDEF FPC}
node := FDoc.DocumentElement.FindNode('rdf:RDF');
if node = nil then exit;
node := node.FirstChild;
while node <> nil do
{$ELSE}
node := FDoc.DocumentElement;
if node.ChildNodes.Count = 0 then
exit;
node := node.ChildNodes.First;
nodename :=node.NodeName;
if nodeName <> 'rdf:RDF' then
exit;
node := node.ChildNodes.First;
{$ENDIF}
while node <> nil do
begin
nodeName := node.NodeName;
if nodeName = 'rdf:Description' then
@ -132,11 +195,12 @@ begin
end;
except
FTags.Clear;
{$IFDEF FPC}
FreeAndNil(FDoc);
{$ENDIF}
raise;
end;
end;
{$ENDIF}
function TXMPData.GetTagByIndex(AIndex: Integer): String;
begin
@ -161,7 +225,7 @@ end;
procedure TXMPData.LoadFromStream(AStream: TStream; ASize: Integer = -1);
var
p: Int64;
i: SizeInt;
i: Cardinal;
begin
if ASize = -1 then
ASize := AStream.Size;
@ -181,9 +245,7 @@ begin
end;
AStream.Position := p;
{$IFDEF FPC}
CreateTags;
{$ENDIF}
end;
procedure TXMPData.SaveToStream(AStream: TStream);

View File

@ -1,12 +1,14 @@
unit fpeXMPReadWrite;
{$mode objfpc}{$H+}
{$IFDEF FPC}
{$mode objfpc}{$H+}
{$ENDIF}
interface
uses
Classes, SysUtils,
fpeGlobal, fpeMetaData;
fpeGlobal, fpeUtils, fpeMetaData;
const
XMP_BASE_KEY = 'http://ns.adobe.com/xap/1.0/';
@ -27,12 +29,12 @@ implementation
function HasXMPHeader(AStream: TStream): Boolean;
var
p: Int64;
hdr: array of ansichar;
hdr: array of ansichar;
begin
p := AStream.Position;
SetLength(hdr, Length(XMP_KEY));
AStream.Read(hdr[0], Length(XMP_KEY));
Result := CompareMem(@hdr[0], @XMP_KEY[1], Length(XMP_KEY));
AStream.Read(hdr[0], Length(XMP_KEY));
Result := CompareMem(PAnsiChar(hdr), PAnsiChar(AnsiString(XMP_KEY)), Length(XMP_KEY));
if not Result then
AStream.Position := p;
end;
@ -66,7 +68,7 @@ begin
// Size of the segment
ADataSize := NToBE(Word(ADataSize));
AStream.WriteBuffer(ADataSize, 2);
AStream.WriteBuffer(XMP_KEY, Length(XMP_KEY));
AStream.WriteBuffer(AnsiString(XMP_KEY), Length(XMP_KEY));
end;
end.

View File

@ -7,7 +7,7 @@
<TargetedPlatforms>1</TargetedPlatforms>
<AppType>Application</AppType>
<FrameworkType>VCL</FrameworkType>
<ProjectVersion>18.8</ProjectVersion>
<ProjectVersion>19.5</ProjectVersion>
<Platform Condition="'$(Platform)'==''">Win32</Platform>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
@ -18,6 +18,11 @@
<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>
@ -29,6 +34,12 @@
<Cfg_1>true</Cfg_1>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win64)'!=''">
<Cfg_1_Win64>true</Cfg_1_Win64>
<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>
@ -40,6 +51,12 @@
<Cfg_2>true</Cfg_2>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win64)'!=''">
<Cfg_2_Win64>true</Cfg_2_Win64>
<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>
@ -58,6 +75,9 @@
<VerInfo_Locale>1033</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=0.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=</VerInfo_Keys>
<DCC_DcuOutput>output\dcu\Delphi</DCC_DcuOutput>
<Icon_MainIcon>$(BDS)\bin\delphi_PROJECTICON.ico</Icon_MainIcon>
<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_Win32)'!=''">
<DCC_Namespace>System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
@ -70,6 +90,13 @@
<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_Namespace>System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(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=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1)'!=''">
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
<DCC_DebugInformation>0</DCC_DebugInformation>
@ -80,6 +107,9 @@
<AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
<AppDPIAwarenessMode>PerMonitor</AppDPIAwarenessMode>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win64)'!=''">
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
<DCC_Optimize>false</DCC_Optimize>
@ -94,6 +124,9 @@
<AppDPIAwarenessMode>PerMonitor</AppDPIAwarenessMode>
<BT_BuildType>Debug</BT_BuildType>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2_Win64)'!=''">
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
</PropertyGroup>
<ItemGroup>
<DelphiCompile Include="$(MainSource)">
<MainSource>MainSource</MainSource>
@ -107,15 +140,11 @@
<DCCReference Include="..\..\fpemetadata.pas"/>
<DCCReference Include="..\..\fpestrconsts.pas"/>
<DCCReference Include="..\..\fpetags.pas"/>
<DCCReference Include="..\..\fpeutils.pas"/>
<DCCReference Include="..\..\fpeUtils.pas"/>
<DCCReference Include="common\fetexifbe.pas"/>
<DCCReference Include="common\fetexifle.pas"/>
<DCCReference Include="common\fetiptc.pas"/>
<DCCReference Include="common\fetutils.pas"/>
<BuildConfiguration Include="Debug">
<Key>Cfg_2</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
@ -123,6 +152,10 @@
<Key>Cfg_1</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
<BuildConfiguration Include="Debug">
<Key>Cfg_2</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
</ItemGroup>
<ProjectExtensions>
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
@ -135,6 +168,7 @@
</Delphi.Personality>
<Platforms>
<Platform value="Win32">True</Platform>
<Platform value="Win64">False</Platform>
</Platforms>
</BorlandProject>
<ProjectFileVersion>12</ProjectFileVersion>