fpexif: Add support of XMP meta data (reading).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8201 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2022-03-07 12:10:52 +00:00
parent a788601c66
commit f39aad84fe
9 changed files with 831 additions and 34 deletions

View File

@ -25,13 +25,19 @@
<Mode0 Name="default"/>
</Modes>
</RunParams>
<RequiredPackages Count="2">
<RequiredPackages Count="4">
<Item1>
<PackageName Value="fpexif_pkg"/>
<PackageName Value="SynEditDsgn"/>
</Item1>
<Item2>
<PackageName Value="LCL"/>
<PackageName Value="SynEdit"/>
</Item2>
<Item3>
<PackageName Value="fpexif_pkg"/>
</Item3>
<Item4>
<PackageName Value="LCL"/>
</Item4>
</RequiredPackages>
<Units Count="3">
<Unit0>
@ -70,6 +76,7 @@
</CodeGeneration>
<Linking>
<Debugging>
<DebugInfoType Value="dsDwarf2Set"/>
<UseExternalDbgSyms Value="True"/>
</Debugging>
<Options>

View File

@ -147,7 +147,7 @@ object MainForm: TMainForm
TabOrder = 1
OnChange = PageControl1Change
object PgMetadata: TTabSheet
Caption = 'Meta data'
Caption = 'EXIF, IPTC'
ClientHeight = 511
ClientWidth = 639
object TagListView: TListView
@ -243,6 +243,539 @@ object MainForm: TMainForm
end
end
end
object PgXMP: TTabSheet
Caption = 'XMP'
ClientHeight = 511
ClientWidth = 639
inline SynEdit: TSynEdit
Left = 0
Height = 231
Top = 280
Width = 639
Align = alClient
Font.Height = -13
Font.Name = 'Courier New'
Font.Pitch = fpFixed
Font.Quality = fqNonAntialiased
ParentColor = False
ParentFont = False
TabOrder = 0
Gutter.Width = 57
Gutter.MouseActions = <>
RightGutter.Width = 0
RightGutter.MouseActions = <>
Highlighter = SynXMLSyn
Keystrokes = <
item
Command = ecUp
ShortCut = 38
end
item
Command = ecSelUp
ShortCut = 8230
end
item
Command = ecScrollUp
ShortCut = 16422
end
item
Command = ecDown
ShortCut = 40
end
item
Command = ecSelDown
ShortCut = 8232
end
item
Command = ecScrollDown
ShortCut = 16424
end
item
Command = ecLeft
ShortCut = 37
end
item
Command = ecSelLeft
ShortCut = 8229
end
item
Command = ecWordLeft
ShortCut = 16421
end
item
Command = ecSelWordLeft
ShortCut = 24613
end
item
Command = ecRight
ShortCut = 39
end
item
Command = ecSelRight
ShortCut = 8231
end
item
Command = ecWordRight
ShortCut = 16423
end
item
Command = ecSelWordRight
ShortCut = 24615
end
item
Command = ecPageDown
ShortCut = 34
end
item
Command = ecSelPageDown
ShortCut = 8226
end
item
Command = ecPageBottom
ShortCut = 16418
end
item
Command = ecSelPageBottom
ShortCut = 24610
end
item
Command = ecPageUp
ShortCut = 33
end
item
Command = ecSelPageUp
ShortCut = 8225
end
item
Command = ecPageTop
ShortCut = 16417
end
item
Command = ecSelPageTop
ShortCut = 24609
end
item
Command = ecLineStart
ShortCut = 36
end
item
Command = ecSelLineStart
ShortCut = 8228
end
item
Command = ecEditorTop
ShortCut = 16420
end
item
Command = ecSelEditorTop
ShortCut = 24612
end
item
Command = ecLineEnd
ShortCut = 35
end
item
Command = ecSelLineEnd
ShortCut = 8227
end
item
Command = ecEditorBottom
ShortCut = 16419
end
item
Command = ecSelEditorBottom
ShortCut = 24611
end
item
Command = ecToggleMode
ShortCut = 45
end
item
Command = ecCopy
ShortCut = 16429
end
item
Command = ecPaste
ShortCut = 8237
end
item
Command = ecDeleteChar
ShortCut = 46
end
item
Command = ecCut
ShortCut = 8238
end
item
Command = ecDeleteLastChar
ShortCut = 8
end
item
Command = ecDeleteLastChar
ShortCut = 8200
end
item
Command = ecDeleteLastWord
ShortCut = 16392
end
item
Command = ecUndo
ShortCut = 32776
end
item
Command = ecRedo
ShortCut = 40968
end
item
Command = ecLineBreak
ShortCut = 13
end
item
Command = ecSelectAll
ShortCut = 16449
end
item
Command = ecCopy
ShortCut = 16451
end
item
Command = ecBlockIndent
ShortCut = 24649
end
item
Command = ecLineBreak
ShortCut = 16461
end
item
Command = ecInsertLine
ShortCut = 16462
end
item
Command = ecDeleteWord
ShortCut = 16468
end
item
Command = ecBlockUnindent
ShortCut = 24661
end
item
Command = ecPaste
ShortCut = 16470
end
item
Command = ecCut
ShortCut = 16472
end
item
Command = ecDeleteLine
ShortCut = 16473
end
item
Command = ecDeleteEOL
ShortCut = 24665
end
item
Command = ecUndo
ShortCut = 16474
end
item
Command = ecRedo
ShortCut = 24666
end
item
Command = ecGotoMarker0
ShortCut = 16432
end
item
Command = ecGotoMarker1
ShortCut = 16433
end
item
Command = ecGotoMarker2
ShortCut = 16434
end
item
Command = ecGotoMarker3
ShortCut = 16435
end
item
Command = ecGotoMarker4
ShortCut = 16436
end
item
Command = ecGotoMarker5
ShortCut = 16437
end
item
Command = ecGotoMarker6
ShortCut = 16438
end
item
Command = ecGotoMarker7
ShortCut = 16439
end
item
Command = ecGotoMarker8
ShortCut = 16440
end
item
Command = ecGotoMarker9
ShortCut = 16441
end
item
Command = ecSetMarker0
ShortCut = 24624
end
item
Command = ecSetMarker1
ShortCut = 24625
end
item
Command = ecSetMarker2
ShortCut = 24626
end
item
Command = ecSetMarker3
ShortCut = 24627
end
item
Command = ecSetMarker4
ShortCut = 24628
end
item
Command = ecSetMarker5
ShortCut = 24629
end
item
Command = ecSetMarker6
ShortCut = 24630
end
item
Command = ecSetMarker7
ShortCut = 24631
end
item
Command = ecSetMarker8
ShortCut = 24632
end
item
Command = ecSetMarker9
ShortCut = 24633
end
item
Command = EcFoldLevel1
ShortCut = 41009
end
item
Command = EcFoldLevel2
ShortCut = 41010
end
item
Command = EcFoldLevel3
ShortCut = 41011
end
item
Command = EcFoldLevel4
ShortCut = 41012
end
item
Command = EcFoldLevel5
ShortCut = 41013
end
item
Command = EcFoldLevel6
ShortCut = 41014
end
item
Command = EcFoldLevel7
ShortCut = 41015
end
item
Command = EcFoldLevel8
ShortCut = 41016
end
item
Command = EcFoldLevel9
ShortCut = 41017
end
item
Command = EcFoldLevel0
ShortCut = 41008
end
item
Command = EcFoldCurrent
ShortCut = 41005
end
item
Command = EcUnFoldCurrent
ShortCut = 41003
end
item
Command = EcToggleMarkupWord
ShortCut = 32845
end
item
Command = ecNormalSelect
ShortCut = 24654
end
item
Command = ecColumnSelect
ShortCut = 24643
end
item
Command = ecLineSelect
ShortCut = 24652
end
item
Command = ecTab
ShortCut = 9
end
item
Command = ecShiftTab
ShortCut = 8201
end
item
Command = ecMatchBracket
ShortCut = 24642
end
item
Command = ecColSelUp
ShortCut = 40998
end
item
Command = ecColSelDown
ShortCut = 41000
end
item
Command = ecColSelLeft
ShortCut = 40997
end
item
Command = ecColSelRight
ShortCut = 40999
end
item
Command = ecColSelPageDown
ShortCut = 40994
end
item
Command = ecColSelPageBottom
ShortCut = 57378
end
item
Command = ecColSelPageUp
ShortCut = 40993
end
item
Command = ecColSelPageTop
ShortCut = 57377
end
item
Command = ecColSelLineStart
ShortCut = 40996
end
item
Command = ecColSelLineEnd
ShortCut = 40995
end
item
Command = ecColSelEditorTop
ShortCut = 57380
end
item
Command = ecColSelEditorBottom
ShortCut = 57379
end>
MouseActions = <>
MouseTextActions = <>
MouseSelActions = <>
Lines.Strings = (
'SynEdit'
)
VisibleSpecialChars = [vscSpace, vscTabAtLast]
SelectedColor.BackPriority = 50
SelectedColor.ForePriority = 50
SelectedColor.FramePriority = 50
SelectedColor.BoldPriority = 50
SelectedColor.ItalicPriority = 50
SelectedColor.UnderlinePriority = 50
SelectedColor.StrikeOutPriority = 50
BracketHighlightStyle = sbhsBoth
BracketMatchColor.Background = clNone
BracketMatchColor.Foreground = clNone
BracketMatchColor.Style = [fsBold]
FoldedCodeColor.Background = clNone
FoldedCodeColor.Foreground = clGray
FoldedCodeColor.FrameColor = clGray
MouseLinkColor.Background = clNone
MouseLinkColor.Foreground = clBlue
LineHighlightColor.Background = clNone
LineHighlightColor.Foreground = clNone
inline SynLeftGutterPartList1: TSynGutterPartList
object SynGutterMarks1: TSynGutterMarks
Width = 24
MouseActions = <>
end
object SynGutterLineNumber1: TSynGutterLineNumber
Width = 17
MouseActions = <>
MarkupInfo.Background = clBtnFace
MarkupInfo.Foreground = clNone
DigitCount = 2
ShowOnlyLineNumbersMultiplesOf = 1
ZeroStart = False
LeadingZeros = False
end
object SynGutterChanges1: TSynGutterChanges
Width = 4
MouseActions = <>
ModifiedColor = 59900
SavedColor = clGreen
end
object SynGutterSeparator1: TSynGutterSeparator
Width = 2
MouseActions = <>
MarkupInfo.Background = clWhite
MarkupInfo.Foreground = clGray
end
object SynGutterCodeFolding1: TSynGutterCodeFolding
MouseActions = <>
MarkupInfo.Background = clNone
MarkupInfo.Foreground = clGray
MouseActionsExpanded = <>
MouseActionsCollapsed = <>
end
end
end
object XMPListView: TListView
Left = 0
Height = 275
Top = 0
Width = 639
Align = alTop
AutoWidthLastColumn = True
Columns = <
item
Caption = 'Description'
Width = 200
end
item
Caption = 'Value'
Width = 435
end>
TabOrder = 1
ViewStyle = vsReport
end
object Splitter4: TSplitter
Cursor = crVSplit
Left = 0
Height = 5
Top = 275
Width = 639
Align = alTop
ResizeAnchor = akTop
end
end
object PgImage: TTabSheet
Caption = 'Image'
ClientHeight = 511
@ -385,4 +918,11 @@ object MainForm: TMainForm
1FDF9F1F363F97D9C744ECB29398DFDF7A8BC172
}
end
object SynXMLSyn: TSynXMLSyn
DefaultFilter = 'XML Document (*.xml,*.xsd,*.xsl,*.xslt,*.dtd)|*.xml;*.xsd;*.xsl;*.xslt;*.dtd'
Enabled = False
WantBracesParsed = False
Left = 480
Top = 145
end
end

View File

@ -5,9 +5,9 @@ unit mdvMain;
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ShellCtrls,
ExtCtrls, ComCtrls, StdCtrls,
fpeGlobal, fpeMetadata;
Classes, SysUtils, FileUtil, SynEdit, SynHighlighterXML, Forms, Controls,
Graphics, Dialogs, ShellCtrls, ExtCtrls, ComCtrls, StdCtrls, fpeGlobal,
fpeMetadata;
type
@ -23,6 +23,7 @@ type
Image: TImage;
Label1: TLabel;
LblChangeDate: TLabel;
XMPListView: TListView;
Messages: TMemo;
PageControl1: TPageControl;
Panel1: TPanel;
@ -33,9 +34,13 @@ type
PreviewImage: TImage;
ImageList: TImageList;
Splitter3: TSplitter;
Splitter4: TSplitter;
StatusBar1: TStatusBar;
PgMetadata: TTabSheet;
PgImage: TTabSheet;
PgXMP: TTabSheet;
SynEdit: TSynEdit;
SynXMLSyn: TSynXMLSyn;
TagListView: TListView;
ShellPanel: TPanel;
ShellListView: TShellListView;
@ -182,7 +187,8 @@ begin
ShellListView.SmallImages := ImageList;
ShellTreeView.Images := ImageList;
{$ENDIF}
//ShellListView.Parent.DoubleBuffered := true;
// XMPListView.ControlStyle:= XMPListView.Controlstyle + [csOpaque];
end;
procedure TMainForm.BtnChangeDateClick(Sender: TObject);
@ -248,6 +254,7 @@ end;
procedure TMainForm.LoadFile(const AFileName: String);
var
lTag: TTag;
xmpTag: String;
item: TListItem;
i: Integer;
ms: TMemoryStream;
@ -315,6 +322,21 @@ begin
end else
DateTimePanel.Hide;
if FImgInfo.HasXMP then begin
ms := TMemoryStream.Create;
FImgInfo.XMPData.SaveToStream(ms);
ms.Position := 0;
SynEdit.Lines.LoadFromStream(ms);
XMPListView.Clear;
for i := 0 to FImgInfo.XMPData.TagCount-1 do begin
xmpTag := FImgInfo.XMPData.TagByIndex[i];
item := XMPListView.Items.Add;
item.Caption := Copy(xmptag, 1, pos('=', xmptag)-1);
item.SubItems.Add(copy(xmptag, pos('=', xmptag)+1));
end;
end;
if FImgInfo.HasIptc then begin
for i := 0 to FImgInfo.IptcData.TagCount-1 do begin
lTag := FImgInfo.IptcData.TagByIndex[i];

View File

@ -63,7 +63,7 @@ type
procedure ReadIFD(AStream: TStream; AParent: TTagID); override;
public
constructor Create(AImgInfo: TImgInfo); override;
function ReadExifHeader(AStream: TStream): Boolean;
class function ReadExifHeader(AStream: TStream): Boolean;
procedure ReadFromStream(AStream: TStream; AImgFormat: TImgFormat); override;
function ReadTiffHeader(AStream: TStream; out ABigEndian: Boolean): Boolean;
property BigEndian: Boolean read FBigEndian;
@ -485,14 +485,19 @@ end;
// Reads the header of the APP1 jpeg segment ("EXIF segment")
// Note that the segment marker and the segment size already have been read.
// The function returns FALSE if the header is not valid.
// Call ReadFromStream immediately afterwards
// In this case, the stream position is reset to where it started.
// Otherwise ReadFromStream can be called immediately afterwards.
//------------------------------------------------------------------------------
function TExifReader.ReadExifHeader(AStream: TStream): Boolean;
class function TExifReader.ReadExifHeader(AStream: TStream): Boolean;
var
hdr: array[0..5] of ansichar;
p: Int64;
begin
p := AStream.Position;
AStream.Read({%H-}hdr[0], SizeOf(hdr));
Result := CompareMem(@hdr[0], @EXIF_SIGNATURE[0], SizeOf(hdr));
if not Result then
AStream.Position := p;
end;
//------------------------------------------------------------------------------

View File

@ -16,13 +16,14 @@ type
TMetaDataKind = (
mdkExif, // Complete Exif (incl MakerNotes)
mdkExifNoMakerNotes, // Exif without MakerNotes (instead of mdkExif)
mdkXMP, // XMP
mdkIPTC, // IPTC
mdkComment // Comment segment
);
TMetaDataKinds = set of TMetaDataKind;
const
mdkAll = [mdkExif, mdkIPTC, mdkComment];
mdkAll = [mdkExif, mdkXMP, mdkIPTC, mdkComment];
type
{$IFNDEF FPC}

View File

@ -14,7 +14,7 @@ uses
LazUTF8,
{$ENDIF}
fpeGlobal,
fpeExifData, fpeIptcData;
fpeExifData, fpeIptcData, fpeXmpData;
type
TImgInfo = class;
@ -64,6 +64,7 @@ type
private
FExifData: TExifData;
FIptcData: TIptcData;
FXmpData: TXmpData;
function GetComment: String;
function GetWarnings: String;
procedure SetComment(const AValue: String);
@ -86,12 +87,14 @@ type
function CreateExifData(ABigEndian: Boolean = false): TExifData;
function CreateIptcData: TIptcData;
function CreateXmpData: TXmpData;
function HasComment: Boolean;
function HasExif: Boolean;
function HasIptc: Boolean;
function HasThumbnail: Boolean;
function HasWarnings: boolean;
function HasXMP: Boolean;
{ Comment stored in the Jpeg COM segment }
property Comment: String read GetComment write SetComment;
@ -115,7 +118,8 @@ type
property WriteJFIFandEXIF: Boolean read FWriteJFIFandEXIF write FWriteJFIFandEXIF;
property ExifData: TExifData read FExifData;
property IptcData: TIptcData read FIptcData; // to do: rename to IptcData
property IptcData: TIptcData read FIptcData;
property XmpData: TXmpData read FXmpData;
end;
@ -260,6 +264,7 @@ begin
FWarnings.Free;
FExifData.Free;
FIptcData.Free;
FXmpData.Free;
inherited;
end;
@ -279,6 +284,14 @@ begin
Result := FIptcData;
end;
function TImgInfo.CreateXmpData: TXmpData;
begin
FWarnings.Clear;
FXmpData.Free;
FXmpData := TXmpData.Create;
Result := FXmpData;
end;
procedure TImgInfo.Error(const AMsg: String);
begin
raise EFpExif.Create(AMsg);
@ -352,6 +365,11 @@ begin
Result := FWarnings.Count > 0;
end;
function TImgInfo.HasXMP: Boolean;
begin
Result := FXmpData <> nil;
end;
procedure TImgInfo.LoadFromFile(const AFileName: String);
var
stream: TStream;
@ -527,22 +545,34 @@ begin
p := AStream.Position;
case marker of
M_EXIF:
if FMetaDataKinds * [mdkExif, mdkExifNoMakerNotes] <> [] then begin
reader := TExifReader.Create(self);
try
if TExifReader(reader).ReadExifHeader(AStream) and
TExifReader(reader).ReadTiffHeader(AStream, bigEndian) then
begin
FExifData := CreateExifData(bigEndian);
try
reader.ReadFromStream(AStream, ifJpeg);
except
FreeAndNil(FExifData);
raise;
if FMetaDataKinds * [mdkExif, mdkExifNoMakerNotes, mdkXMP] <> [] then begin
if TExifReader.ReadExifHeader(AStream) then
begin
reader := TExifReader.Create(self);
try
if TExifReader(reader).ReadTiffHeader(AStream, bigEndian) then
begin
FExifData := CreateExifData(bigEndian);
try
reader.ReadFromStream(AStream, ifJpeg);
except
FreeAndNil(FExifData);
raise;
end;
end;
finally
reader.Free;
end;
end else
if HasXMPHeader(AStream) then
begin
FXmpData := CreateXMPData;
try
FXmpData.ReadFromStream(AStream, size - Length(XMP_KEY));
except
FreeAndNil(FXmpData);
raise;
end;
finally
reader.Free;
end;
end;
M_IPTC:

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<Package Version="4">
<Package Version="5">
<Name Value="fpexif_pkg"/>
<Author Value="Werner Pamler"/>
<CompilerOptions>
@ -12,7 +12,7 @@
<Description Value="Library for displaying and editing of meta data (EXIF, IPTC) in images."/>
<License Value="LGPL with linking exception (like Lazarus)"/>
<Version Minor="1"/>
<Files Count="20">
<Files Count="21">
<Item1>
<Filename Value="fpeglobal.pas"/>
<UnitName Value="fpeGlobal"/>
@ -83,17 +83,22 @@
</Item17>
<Item18>
<Filename Value="fpemakernotesanyo.pas"/>
<UnitName Value="fpemakernotesanyo"/>
<UnitName Value="fpeMakerNoteSanyo"/>
</Item18>
<Item19>
<Filename Value="fpemakernotecasio.pas"/>
<UnitName Value="fpemakernotecasio"/>
<UnitName Value="fpeMakerNoteCasio"/>
</Item19>
<Item20>
<Filename Value="fpemakernotecanon.pas"/>
<UnitName Value="fpeMakerNoteCanon"/>
</Item20>
<Item21>
<Filename Value="fpexmpdata.pas"/>
<UnitName Value="fpeXMPData"/>
</Item21>
</Files>
<CompatibilityMode Value="True"/>
<i18n>
<EnableI18N Value="True"/>
<OutDir Value="languages"/>

View File

@ -12,7 +12,7 @@ uses
fpeIptcReadWrite, fpeExifData, fpeIptcData, fpeStrConsts, fpeMakerNote,
fpeMakerNoteNikon, fpeMakerNoteMinolta, fpeMakerNoteOlympus,
fpeMakerNoteEpson, fpeMakerNoteFuji, fpeMakerNoteSanyo, fpeMakerNoteCasio,
fpeMakerNoteCanon;
fpeMakerNoteCanon, fpeXMPData;
implementation

View File

@ -0,0 +1,187 @@
unit fpeXMPData;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, contnrs,
{$IFDEF FPC}
laz2_dom, laz2_xmlread,
{$ENDIF}
fpeGlobal, fpeTags;
const
XMP_BASE_KEY = 'http://ns.adobe.com/xap/1.0/';
XMP_KEY = XMP_BASE_KEY + #0;
type
TXMPData = class
private
FData: String;
FDoc: TXMLDocument;
FTags: TStringList;
// FTags: TTagList;
function GetTagByIndex(AIndex: Integer): String;
function GetTagByName(ATagName: String): String;
function GetTagCount: Integer;
protected
{$IFDEF FPC}
procedure Create_RDFDescription_Tags(ANode: TDOMNode);
procedure CreateTags;
{$ENDIF}
public
constructor Create;
destructor Destroy; override;
procedure ReadFromStream(AStream: TStream; ASize: Integer = -1);
procedure SaveToStream(AStream: TStream);
property TagByIndex[AIndex: Integer]: String read GetTagByIndex;
property TagByName[ATagName: String]: String read GetTagByName;
property TagCount: Integer read GetTagCount;
end;
function HasXMPHeader(AStream: TStream): Boolean;
implementation
function HasXMPHeader(AStream: TStream): Boolean;
var
p: Int64;
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));
if not Result then
AStream.Position := p;
end;
{ TXMPData }
constructor TXMPData.Create;
begin
inherited;
FTags := TStringList.Create; //TTagList.Create;
end;
destructor TXMPData.Destroy;
begin
{$IFDEF FPC}
FDoc.Free;
{$ENDIF}
FTags.Free;
inherited;
end;
{$IFDEF FPC}
procedure TXMPData.Create_RDFDescription_Tags(ANode: TDOMNode);
var
node: TDOMNode;
nodeName: String;
i: Integer;
attr: TDOMNode;
tagName, tagValue: String;
lTag: TTag;
begin
while ANode <> nil do begin
nodeName := ANode.NodeName;
for i := 0 to ANode.Attributes.Length-1 do
begin
attr := ANode.Attributes.Item[i];
tagName := attr.NodeName;
tagValue := attr.NodeValue;
FTags.Add(tagName + '=' + tagValue);
end;
if ANode.HasChildNodes then
begin
node := ANode.FirstChild;
while node <> nil do
begin
nodeName := node.NodeName;
tagValue := node.TextContent;
if tagName <> '' then
FTags.Add(nodeName + '=' + tagValue);
node := node.NextSibling;
end;
end;
ANode := ANode.NextSibling;
end;
end;
procedure TXMPData.CreateTags;
var
stream: TStringStream;
node: TDOMNode;
nodeName: String;
begin
FDoc.Free;
stream := TStringStream.Create(FData);
try
ReadXMLFile(FDoc, stream);
finally
stream.Free;
end;
FTags.Clear;
try
node := FDoc.DocumentElement.FindNode('rdf:RDF');
if node = nil then exit;
node := node.FirstChild;
while node <> nil do
begin
nodeName := node.NodeName;
if nodeName = 'rdf:Description' then
Create_RDFDescription_Tags(node);
node := node.NextSibling;
end;
except
FTags.Clear;
FreeAndNil(FDoc);
raise;
end;
end;
{$ENDIF}
function TXMPData.GetTagByIndex(AIndex: Integer): String;
begin
Result := FTags[AIndex];
end;
function TXMPData.GetTagByName(ATagName: String): String;
begin
Result := FTags.Values[ATagName];;
end;
function TXMPData.GetTagCount: Integer;
begin
Result := FTags.Count;
end;
procedure TXMPData.ReadFromStream(AStream: TStream; ASize: Integer = -1);
var
p: Int64;
begin
if ASize = -1 then
ASize := AStream.Size;
SetLength(FData, ASize);
p := AStream.Position;
AStream.Read(FData[1], ASize);
AStream.Position := p;
{$IFDEF FPC}
CreateTags;
{$ENDIF}
end;
procedure TXMPData.SaveToStream(AStream: TStream);
begin
if FData <> '' then
AStream.Write(FData[1], Length(FData));
end;
end.