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.
+