diff --git a/components/fpexif/examples/metadata_viewer/MetadataViewer.lpi b/components/fpexif/examples/metadata_viewer/MetadataViewer.lpi index 5d9ef4394..5d4412d66 100644 --- a/components/fpexif/examples/metadata_viewer/MetadataViewer.lpi +++ b/components/fpexif/examples/metadata_viewer/MetadataViewer.lpi @@ -25,13 +25,19 @@ - + - + - + + + + + + + @@ -70,6 +76,7 @@ + diff --git a/components/fpexif/examples/metadata_viewer/mdvmain.lfm b/components/fpexif/examples/metadata_viewer/mdvmain.lfm index 02f02fae2..c1adb728f 100644 --- a/components/fpexif/examples/metadata_viewer/mdvmain.lfm +++ b/components/fpexif/examples/metadata_viewer/mdvmain.lfm @@ -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 diff --git a/components/fpexif/examples/metadata_viewer/mdvmain.pas b/components/fpexif/examples/metadata_viewer/mdvmain.pas index c342c00f9..3b05c83e2 100644 --- a/components/fpexif/examples/metadata_viewer/mdvmain.pas +++ b/components/fpexif/examples/metadata_viewer/mdvmain.pas @@ -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; @@ -314,6 +321,21 @@ begin DateTimePanel.Show; 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 diff --git a/components/fpexif/fpeexifreadwrite.pas b/components/fpexif/fpeexifreadwrite.pas index fa355e18c..346ad0ee3 100644 --- a/components/fpexif/fpeexifreadwrite.pas +++ b/components/fpexif/fpeexifreadwrite.pas @@ -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; @@ -484,15 +484,20 @@ end; // For JPEG files only: // 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 +// The function returns FALSE if the header is not valid. +// 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; //------------------------------------------------------------------------------ diff --git a/components/fpexif/fpeglobal.pas b/components/fpexif/fpeglobal.pas index 6fd2c8436..00bc45939 100644 --- a/components/fpexif/fpeglobal.pas +++ b/components/fpexif/fpeglobal.pas @@ -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} diff --git a/components/fpexif/fpemetadata.pas b/components/fpexif/fpemetadata.pas index 06ea46280..b7181f94a 100644 --- a/components/fpexif/fpemetadata.pas +++ b/components/fpexif/fpemetadata.pas @@ -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: diff --git a/components/fpexif/fpexif_pkg.lpk b/components/fpexif/fpexif_pkg.lpk index 2f2f03330..2c16f7fed 100644 --- a/components/fpexif/fpexif_pkg.lpk +++ b/components/fpexif/fpexif_pkg.lpk @@ -1,6 +1,6 @@ - + @@ -12,7 +12,7 @@ - + @@ -83,17 +83,22 @@ - + - + + + + + + diff --git a/components/fpexif/fpexif_pkg.pas b/components/fpexif/fpexif_pkg.pas index 15b9b8fa8..21661443d 100644 --- a/components/fpexif/fpexif_pkg.pas +++ b/components/fpexif/fpexif_pkg.pas @@ -12,7 +12,7 @@ uses fpeIptcReadWrite, fpeExifData, fpeIptcData, fpeStrConsts, fpeMakerNote, fpeMakerNoteNikon, fpeMakerNoteMinolta, fpeMakerNoteOlympus, fpeMakerNoteEpson, fpeMakerNoteFuji, fpeMakerNoteSanyo, fpeMakerNoteCasio, - fpeMakerNoteCanon; + fpeMakerNoteCanon, fpeXMPData; implementation diff --git a/components/fpexif/fpexmpdata.pas b/components/fpexif/fpexmpdata.pas new file mode 100644 index 000000000..22a6f99aa --- /dev/null +++ b/components/fpexif/fpexmpdata.pas @@ -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. +