? XmpTagsImportPatch.diff
? icons/f-spot-imported-xmp-tags.png
? libeog/access.lo
? libeog/accessible-image-view-factory.lo
? libeog/accessible-image-view.lo
? libeog/cursors.lo
? libeog/eog-file-selection.lo
? libeog/eog-image.lo
? libeog/eog-pixbuf-util.lo
? libeog/eog-scroll-view.lo
? libeog/image-view.lo
? libeog/libeog-marshal.lo
? libeog/libfspoteog.la
? libeog/ui-image.lo
? libeog/uta.lo
? libeog/zoom.lo
? libfspot/f-image-view.lo
? libfspot/f-jpeg-utils.lo
? libfspot/f-marshal.lo
? libfspot/f-pixbuf-save.lo
? libfspot/f-pixbuf-unsharp.lo
? libfspot/f-pixbuf-utils.lo
? libfspot/f-screen-utils.lo
? libfspot/f-utils.lo
? libfspot/libfspot.la
? libjpegtran/jpeg-data.lo
? libjpegtran/jpeg-marker.lo
? libjpegtran/jpegtran.lo
? libjpegtran/libfspotjpegtran.la
? libjpegtran/transupp.lo
? po/.intltool-merge-cache
? po/stamp-it
? src/AssemblyInfo.cs
? src/XmpTagsImporter.cs
? src/XmpTagsMetadata.cs
? src/core.16450
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/f-spot/ChangeLog,v
retrieving revision 1.1537
diff -u -p -r1.1537 ChangeLog
--- ChangeLog	29 Aug 2006 01:58:59 -0000	1.1537
+++ ChangeLog	30 Aug 2006 12:48:55 -0000
@@ -1,3 +1,17 @@
+2006-08-07  Bengt Thuree <bengt@thuree.com> 
+  
+	* : The below changes fixes bug #342137
+	* src/XmpTagsMetadata.cs: Reads the XMP tags from a photo during import
+  	
+	* src/XmpTagsImporter.cs: Creates XMP tags during import
+ 	
+	* src/FileImportBackend.cs: Calls XmpTagsImporter during import.
+  	
+	* src/Makefile.am: Added XmpTagsMetadata.cs and XmpTagsImporter.cs
+	
+	* src/ImportCommand.cs: disable DisplayTags since otherwise we get a crash. 
+	Cancel do not seem to remove all...
+  
 2006-08-28  Larry Ewing  <lewing@novell.com>
 
 	* src/Makefile.am: move AssemblyInfo.cs.in to the right section.
Index: configure.in
===================================================================
RCS file: /cvs/gnome/f-spot/configure.in,v
retrieving revision 1.104
diff -u -p -r1.104 configure.in
--- configure.in	29 Aug 2006 17:28:50 -0000	1.104
+++ configure.in	30 Aug 2006 12:48:57 -0000
@@ -187,6 +187,7 @@ libgphoto2-sharp/Makefile
 semweb/Makefile
 tools/Makefile
 po/Makefile.in
+src/AssemblyInfo.cs
 src/Defines.cs
 src/AssemblyInfo.cs
 src/f-spot.exe.config
Index: icons/Makefile.am
===================================================================
RCS file: /cvs/gnome/f-spot/icons/Makefile.am,v
retrieving revision 1.19
diff -u -p -r1.19 Makefile.am
--- icons/Makefile.am	28 Mar 2006 05:00:33 -0000	1.19
+++ icons/Makefile.am	30 Aug 2006 12:49:04 -0000
@@ -9,6 +9,7 @@ noinst_DATA =					\
 	f-spot-favorite.png			\
 	f-spot-fullscreen.png			\
 	f-spot-hidden.png			\
+	f-spot-imported-xmp-tags.png		\
 	f-spot-loading.png			\
 	f-spot-new-tag.png			\
 	f-spot-other.png			\
Index: src/FileImportBackend.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/FileImportBackend.cs,v
retrieving revision 1.36
diff -u -p -r1.36 FileImportBackend.cs
--- src/FileImportBackend.cs	18 Jul 2006 23:17:42 -0000	1.36
+++ src/FileImportBackend.cs	30 Aug 2006 12:50:36 -0000
@@ -3,6 +3,8 @@ using Gtk;
 using Gnome;
 using System.Collections;
 using System;
+using FSpot;
+using FSpot.Xmp;
 using System.IO;
 
 public class ImportException : System.Exception {
@@ -13,12 +15,14 @@ public class ImportException : System.Ex
 
 public class FileImportBackend : ImportBackend {
 	PhotoStore store;
+	TagStore tag_store = FSpot.Core.Database.Tags;
 	bool recurse;
 	bool copy;
 	string [] base_paths;
 	Tag [] tags;
 
 	int count;
+	XmpTagsImporter xmptags;
 
 	ArrayList import_info;
 	Stack directories;
@@ -39,8 +43,7 @@ public class FileImportBackend : ImportB
 		
 		public ImportInfo (string original)
 		{
-			original_path
- = original;
+			original_path = original;
 		        destination_path= null;
 			Photo = null;
 		}
@@ -114,6 +117,8 @@ public class FileImportBackend : ImportB
 		}	
 
 		directories = new Stack ();
+		xmptags = new XmpTagsImporter (store, tag_store);
+
 		return import_info.Count;
 	}
 
@@ -195,6 +200,7 @@ public class FileImportBackend : ImportB
 
 		// FIXME Need to get the EXIF info etc.
 		ImportInfo info = (ImportInfo)import_info [this.count];
+		bool needs_commit = false;
 
 		try {
 			string destination = info.OriginalPath;
@@ -226,8 +232,13 @@ public class FileImportBackend : ImportB
 				foreach (Tag t in tags) {
 					photo.AddTag (t);
 				}
-				store.Commit(photo);
+				needs_commit = true;
 			}
+
+			needs_commit |= xmptags.Import (photo, info.DestinationPath, info.OriginalPath);
+
+			if (needs_commit)
+				store.Commit(photo);
 			
 			info.Photo = photo;
 		} catch (System.Exception e) {
@@ -273,6 +284,8 @@ public class FileImportBackend : ImportB
 					info.Delete ();
 			}
 		}
+		// Clean up just created tags
+		xmptags.Cancel();
 	}
 
 	public override void Finish ()
@@ -286,6 +299,7 @@ public class FileImportBackend : ImportB
 		}
 
 		import_info = null;
+		xmptags.Finish();
 		count = 0;
 	}
 
Index: src/ImportCommand.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/ImportCommand.cs,v
retrieving revision 1.63
diff -u -p -r1.63 ImportCommand.cs
--- src/ImportCommand.cs	9 Jun 2006 22:53:02 -0000	1.63
+++ src/ImportCommand.cs	30 Aug 2006 12:50:45 -0000
@@ -346,7 +346,6 @@ public class ImportCommand : FSpot.Glade
 
 			if (item.Source is BrowseSource) {
 				string path = ChoosePath ();
-				
 				if (path != null) {
 					SourceItem path_item = new SourceItem (new VfsSource (path));
 					menu.Prepend (path_item);
@@ -512,7 +511,6 @@ public class ImportCommand : FSpot.Glade
 	public string ChoosePath ()
 	{
 		string path = null;
-
 		CompatFileChooserDialog file_selector =
 			new CompatFileChooserDialog ("Import", this.Dialog,
 						     CompatFileChooserDialog.Action.SelectFolder);
@@ -529,7 +527,6 @@ public class ImportCommand : FSpot.Glade
 		if ((ResponseType) response == ResponseType.Ok) {
 			path = file_selector.Filename;
 		}
-
 		file_selector.Destroy ();
 		return path;
 	}
@@ -578,6 +575,7 @@ public class ImportCommand : FSpot.Glade
 
 		collection = new FSpot.PhotoList (new Photo [0]);
 		tray = new FSpot.ScalingIconView (collection);
+		tray.DisplayTags = false;		// Otherwise deleting just created XMP tags will crash.
 		tray.Selection.Changed += HandleTraySelectionChanged;
 		icon_scrolled.SetSizeRequest (200, 200);
 		icon_scrolled.Add (tray);
@@ -659,7 +657,6 @@ public class ImportCommand : FSpot.Glade
 					store.Commit (p);
 				}
 			}
-
 			this.Dialog.Destroy ();
 			return collection.Count;
 		} else {
@@ -699,6 +696,7 @@ public class ImportCommand : FSpot.Glade
 	{
 		idle_start.Stop ();
 		step.Stop ();
+		
 		if (importer != null) {
 			importer.Cancel ();
 			importer = null;
Index: src/Makefile.am
===================================================================
RCS file: /cvs/gnome/f-spot/src/Makefile.am,v
retrieving revision 1.65
diff -u -p -r1.65 Makefile.am
--- src/Makefile.am	29 Aug 2006 01:58:59 -0000	1.65
+++ src/Makefile.am	30 Aug 2006 12:50:52 -0000
@@ -119,6 +119,8 @@ F_SPOT_CSDISTFILES =				\
 	$(srcdir)/TagSelectionDialog.cs		\
 	$(srcdir)/X3fFile.cs			\
 	$(srcdir)/XmpFile.cs			\
+	$(srcdir)/XmpTagsImporter.cs		\
+	$(srcdir)/XmpTagsMetadata.cs		\
 	$(srcdir)/main.cs
 
 ASSEMBLIES =					\
@@ -147,6 +149,7 @@ RESOURCES =										\
 	-resource:$(top_srcdir)/icons/f-spot-events.png,f-spot-events.png		\
 	-resource:$(top_srcdir)/icons/f-spot-favorite.png,f-spot-favorite.png 		\
 	-resource:$(top_srcdir)/icons/f-spot-hidden.png,f-spot-hidden.png 		\
+	-resource:$(top_srcdir)/icons/f-spot-imported-xmp-tags.png,f-spot-imported-xmp-tags.png	\
 	-resource:$(top_srcdir)/icons/f-spot-loading.png,f-spot-loading.png		\
 	-resource:$(top_srcdir)/icons/f-spot-logo.png,f-spot-logo.png			\
 	-resource:$(top_srcdir)/icons/f-spot-new-tag.png,f-spot-new-tag.png		\
--- /dev/null	2006-08-28 22:36:53.000000000 +1000
+++ src/XmpTagsImporter.cs	2006-08-30 22:26:22.000000000 +1000
@@ -0,0 +1,250 @@
+// XmpTagsImporter.cs: Creates tags based on embedded XMP tags in the photo/sidecar.
+//
+// Author:
+//   Bengt Thuree (bengt@thuree.com)
+//
+// (C) 2006 Bengt Thuree
+// 
+
+using Gtk;
+using System;
+using System.Collections;
+using System.IO;
+using System.Xml;
+using FSpot.Xmp;
+
+namespace FSpot.Xmp {
+	public class XmpTagsImporter {
+		private PhotoStore photo_store;
+		private TagStore tag_store;
+		private Stack tags_created;
+		private Gtk.Window parent;
+		static private string LastImportStr = "Imported Tags";
+		static private string LastImportIcon = "f-spot-imported-xmp-tags.png";
+		static private string CityStr = "City";
+		static private string CountryStr = "Country";
+		static private string LocationStr = "Location";
+		static private string StateStr = "State";
+		static private string PlacesIcon = "f-spot-places.png";
+		static private string XMPSidecarStr = "Tags in a XMP Sidecar";
+
+		private class TagInfo {
+			// This class contains the Root tag name, and its Icon name (if any)
+			string tag_name;
+			string icon_name; 
+		       
+			public string TagName {
+				get { return tag_name; }
+			}
+			
+			public string IconName {
+				get { return icon_name; }
+			}
+
+			public bool HasIcon {
+				get { return icon_name != null; }
+			}
+
+			public TagInfo (string t_name, string i_name)
+			{
+				tag_name = t_name;
+				icon_name = i_name;
+			}
+			
+			public TagInfo (string t_name)
+			{
+				tag_name = t_name;
+				icon_name = null;
+			}
+		} // TagInfo
+		
+		TagInfo li_root_tag; // This is the Last Import root tag
+		// The following are the various sub tags under Last Import.
+		TagInfo li_subroot_country, li_subroot_city, li_subroot_state, li_subroot_loc;
+					
+		public XmpTagsImporter (PhotoStore photo_store, TagStore tag_store)
+		{
+			this.photo_store = photo_store;
+			this.tag_store = tag_store;
+			tags_created = new Stack ();
+			// Prepare the Last Import root tag
+			li_root_tag = new TagInfo (LastImportStr, LastImportIcon);
+			// Prepare possible sub root's under the Last Import root tag
+			li_subroot_loc = new TagInfo (LocationStr, PlacesIcon);
+			li_subroot_country = new TagInfo (CountryStr, PlacesIcon);
+			li_subroot_city = new TagInfo (CityStr, PlacesIcon);
+			li_subroot_state = new TagInfo (StateStr, PlacesIcon);
+		}
+
+		private Tag AddImportRootTagIfNotExist (TagInfo new_tag, Category parent)
+		{
+			Tag root_tag = tag_store.GetTagByName (Mono.Posix.Catalog.GetString (new_tag.TagName));
+			if (root_tag == null) {
+				root_tag = tag_store.GetTagByName (new_tag.TagName);
+
+				if (root_tag == null) {
+					root_tag = tag_store.CreateCategory (
+						parent, 
+						Mono.Posix.Catalog.GetString (new_tag.TagName));
+						if (new_tag.HasIcon)
+							root_tag.StockIconName = new_tag.IconName;
+					tags_created.Push (root_tag);
+				}
+			}
+			return root_tag;
+		}
+
+		private void AddTagToPhoto (Photo photo, string new_tag_name, TagInfo sub_tag_root)
+		{
+			if (new_tag_name != null) {
+				// Check if a tag exists for this new_tag_name
+				Tag tag = tag_store.GetTagByName (new_tag_name);
+
+				// If not, make sure the rootstr tag exists and add this new_tag_name under it
+				if (tag == null) {
+					Tag root_tag = AddImportRootTagIfNotExist (li_root_tag, tag_store.RootCategory);
+
+					// If we should have a sub root, add it now.
+					if (sub_tag_root != null)	{
+						Tag subroot_tag = AddImportRootTagIfNotExist (sub_tag_root, root_tag as Category);
+						root_tag = subroot_tag; // Ensure we attach to subroot_tag 
+					} // end adding subroot
+					
+					// add the new tag, and put it under the root/subroot
+					// but only if it does not exist (same name as root tag)
+					tag = tag_store.GetTagByName (new_tag_name);
+					if (tag == null) {
+						tag = tag_store.CreateCategory (root_tag as Category, new_tag_name);
+						tags_created.Push (tag);
+					}
+				}
+				
+				// Now we have the tag for this place, add the photo to it
+				photo.AddTag (tag);
+			} // if new_tag_name != null
+		}
+
+		private void AddTagToPhoto (Photo photo, string [] new_tag_names, TagInfo sub_tag_root)
+		{
+			if (new_tag_names != null)
+				foreach (string tag in new_tag_names)
+					AddTagToPhoto (photo, tag, sub_tag_root);
+		}
+		
+
+		
+		public bool Import (Photo photo, string path, string orig_path)
+		{
+			bool needs_commit = false; // Default to no XMP data embedded in picture
+
+			XmpTagsMetadata xmd = new XmpTagsMetadata (path, orig_path);
+			
+			// Read the XMP tags (embedded tags takes priority)
+			xmd.Read_Sidecar_tags();	// first read xmp tags from a possible sidecar file
+			xmd.Read_Embedded_tags();	// second read the tags embedded in the photo (exif/xmp etc)
+			
+			// If no tags (EXIF/XMP etc) were found, return false
+			if (xmd.EmptyTags())
+				return needs_commit;
+
+			// Ok, we have XMP data embeeded in the photo. 
+			needs_commit = true;
+
+			// F-Spot do not keep Headline/Caption but only a description field, 
+			// so we need to combine the two headline/caption to one field (with a " : " between)
+			// Also, there are at least two different options here. So we assume that if
+			// something is set, we should use it. Start to check for the title, then description.
+
+			// Since F-Spot do not handle the EXIF description to good yet, we leave this one out.
+			// photo.Description = xmd.GetXmpTag ("exif_description");
+
+			// F-Spot only stores the description into XMP User Comment, so if this one is set, we use it.
+			
+			if (xmd.GetXmpBag_0 ("UserComment") != null)
+				photo.Description = xmd.GetXmpBag_0 ("UserComment");
+
+			// We want to construct the following : Description = <Headline> :: <Caption>         
+
+			// only check for more title/comment if you still do not have one.
+			if ((photo.Description == null) || (photo.Description.Length == 0)) {
+				if (xmd.GetXmpTag ("Headline") != null) 
+					photo.Description = xmd.GetXmpTag ("Headline");
+				// Lets add the Caption to the existing Description (Headline).
+				if (xmd.GetXmpTag ("Caption") != null)
+					photo.Description += (( (photo.Description == null) ? "" : " :: ") + xmd.GetXmpTag ("Caption"));
+			}	
+			
+			// only check for more title/comment if you still do not have one. 
+			if ((photo.Description == null) || (photo.Description.Length == 0)) {
+				if (xmd.GetXmpTag ("title") != null) 
+					photo.Description = xmd.GetXmpTag ("title");
+				// Lets add the Description  to the existing Description (Title).
+				if (xmd.GetXmpTag ("description") != null)
+					photo.Description += (( (photo.Description == null) ? "" : " :: ") + xmd.GetXmpTag ("description"));
+			}
+			
+			// F-Spot uses exif time for the time beeing.
+//			if (xmd.GetXmpTag ("DateTimeOriginal) != null)
+//				photo.Time = xmd.GetXmpTag ("DateTimeOriginal);
+
+			if (xmd.GetXmpTag ("State") != null)
+				AddTagToPhoto (photo, xmd.GetXmpTag ("State"), li_subroot_state);
+			if (xmd.GetXmpTag ("City") != null)
+				AddTagToPhoto (photo, xmd.GetXmpTag ("City"), li_subroot_city);
+			if (xmd.GetXmpTag ("Country") != null)
+				AddTagToPhoto (photo, xmd.GetXmpTag ("Country"), li_subroot_country);
+			if (xmd.GetXmpTag ("Location") != null)
+				AddTagToPhoto (photo, xmd.GetXmpTag ("Location"), li_subroot_loc);
+
+			if (xmd.GetXmpTag ("Source") != null)
+				AddTagToPhoto (photo, xmd.GetXmpTag ("Source"), null);
+
+			if (xmd.GetXmpBag ("subject") != null)
+				if (xmd.GetXmpBag ("subject") is string [])
+					AddTagToPhoto (photo, xmd.GetXmpBag ("subject") as string [], null);
+				else
+					AddTagToPhoto (photo, xmd.GetXmpBag ("subject") as string, null);
+
+			if (xmd.GetXmpBag ("SupplementalCategories") != null)
+				if (xmd.GetXmpBag ("SupplementalCategories") is string [])
+					AddTagToPhoto (photo, xmd.GetXmpBag ("SupplementalCategories") as string [], null);
+				else
+					AddTagToPhoto (photo, xmd.GetXmpBag ("SupplementalCategories") as string, null);	
+
+			if (xmd.GetXmpBag ("People") != null)
+				if (xmd.GetXmpBag ("People") is string [])
+					AddTagToPhoto (photo, xmd.GetXmpBag ("People") as string [], null);
+				else
+					AddTagToPhoto (photo, xmd.GetXmpBag ("People") as string, null);	
+
+
+			
+			// Ok, not good. But at least one indication which photo has tags in a sidecar.
+			// FIXME : Should be changed to possible an internal representation later.
+			//if (xmd.AnySidecarTags())
+			//	AddTagToPhoto (photo, Mono.Posix.Catalog.GetString (XMPSidecarStr), null);
+			
+			return needs_commit;
+		}
+
+		public void Cancel()
+		{
+			// User have cancelled the import.
+			// Remove all created tags
+			while (tags_created.Count > 0) 
+				tag_store.Remove ((DbItem) tags_created.Pop());
+			
+			// Clear the tags_created array
+			tags_created.Clear();
+		}
+
+		public void Finish()
+		{
+			// Clear the tags_created array, since we do not need it anymore.
+			tags_created.Clear();		
+		}
+
+
+	}
+
+} // namespace
--- /dev/null	2006-08-28 22:36:53.000000000 +1000
+++ src/XmpTagsMetadata.cs	2006-08-30 22:48:40.000000000 +1000
@@ -0,0 +1,601 @@
+// #define enable_debug
+//
+// XmpTagsMetaData.cs: Collects a number of embedded XMP tags from a photo
+//	Will try to find an external sidecar file (<image file name>.xmp) both in
+//	same place as photo, as well as from the imported location of the photo.
+//
+// 	Read the sidecar tags first, and then the embedded tags. (which is controlled by the calling class)
+//	Doing this, we will not loose any tags and the embedded tags takes priority.
+//
+// Author:
+//   Bengt Thuree (bengt@thuree.com)
+//	With great help from Stephane, Cosme and Warren
+//
+// (C) 2006 Bengt Thuree
+// 
+
+using System;
+using System.IO;
+using System.Xml;
+using FSpot;
+using FSpot.Xmp;
+using SemWeb;
+
+namespace FSpot.Xmp {
+	public class XmpTagsMetadata {
+	
+		public class Bag_Couple {
+		
+			private string title;
+			public string Title {
+				get { 
+					return title;
+				}
+			}
+
+			private string value;
+			public string Value {
+				get { 
+					return value;
+				}
+			}
+			
+			public Bag_Couple (string t1, string v1)
+			{
+				this.title = t1;
+				this.value = v1;
+			}
+		}
+		
+		private bool xmptags_sidecar_exist;	// Sets to true if a xmp sidecar (<image file name>.xmp) 
+							// is found (with photo, or import location)
+		private bool xmptags_found_sidecar_tag; // Indicates if the sidecar file contained tags or not.
+		private bool xmptags_found_xmp_tags;	// Sets to true if EXIF or XMP tags exists.
+		private string xmptags_photofile;	// full path to image file
+		private string xmptags_sidecarfile;	// full path to sidecar file
+		
+		private System.Collections.ArrayList items_list; // Temporary storage of items before we store them in the right place.
+
+		static System.Collections.Hashtable xmp_table;
+		static System.Collections.Hashtable xmp_bag_table;
+
+		// We probably do not need the below tables with strings for the hash tables.
+		// We should be able to just store the predicates from the embedded XMP tags directly.
+		// But by using the below tables we know much better which tags we do support, and it makes
+		// it much easier to fetch the tag. As well as knowing which tag is a row tag, and which is a bag tag.I 
+
+		static string [] xmp_bag_tags = {
+			// http://ns.adobe.com/photoshop/1.0
+			"SupplementalCategories" ,  // same as subject (keywords) 
+			
+			// http://purl.org/dc/elements/1.1
+			"subject", 
+			"creator", 
+			"rights",
+			"description",
+			
+			// http://ns.adobe.com/xap/1.0/
+			"title",
+			"UsageTerms",
+
+			// http://ns.adobe.com/exif/1.0
+			"UserComment",
+			"ISOSpeedRatings",
+			"SubjectArea",
+			"ComponentsConfiguration",
+			"ExifCFAPattern",
+			"Flash",
+			
+			// http://ns.adobe.com/tiff/1.0	
+			"ReferenceBlackWhite",
+			
+			// http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/
+			"Scene",
+			"CreatorContactInfo",
+			"SubjectCode",
+			
+			// http://ns.iview-multimedia.com/mediapro/1.0
+			"People",
+			
+			"BitsPerSample",
+			"ToneCurve"
+		};
+						
+		static string [] xmp_tags = {
+			// http://ns.adobe.com/photoshop/1.0
+			"City" ,
+			"State" ,
+			"Country" ,
+			"Caption" ,
+			"CaptionWriter" ,
+			"Credit" ,
+			"Category" ,
+			"Source" ,
+			"History" ,
+			"Headline",
+			"Urgency",
+			"ICCProfile",
+			"ColorMode",
+			"Instructions",
+			"TransmissionReference",
+			"AuthorsPosition",
+			"DateCreated",
+			
+			// http://ns.adobe.com/tiff/1.0
+			"Make" ,
+			"Model" ,
+			"Orientation" ,
+			"ResolutionUnit" ,
+			"YCbCrPositioning" ,				
+			"XResolution" ,				
+			"YResolution" ,				
+			"ImageWidth" ,				
+			"ImageLength" ,	
+			"NativeDigest",
+			
+			// http://ns.adobe.com/exif/1.0	
+			"exif_description",
+			"ExposureTime" ,
+			"FNumber" ,
+			"ShutterSpeedValue" ,
+			"ApertureValue" ,
+			"ExposureBiasValue" ,
+			"MaxApertureValue" ,
+			"MeteringMode" ,
+			"FocalLength" ,
+			"ColorSpace" ,
+			"PixelXDimension" ,
+			"PixelYDimension" ,
+			"ExposureMode" ,
+			"WhiteBalance" ,
+			"DigitalZoomRatio" ,
+			"LightSource" ,
+			"ExifVersion" ,
+			"DateTimeOriginal" ,
+			"DateTimeDigitized" ,
+			"CompressedBitsPerPixel" ,
+			"FlashPixVersion" ,
+			"FocalPlaneXResolution" ,
+			"FocalPlaneYResolution" ,
+			"FocalPlaneResolutionUnit",
+			"SensingMethod" ,
+			"FileSource" ,
+			"CustomRendered" ,
+			"SceneCaptureType" ,
+			"MetadataDate" ,
+			"SubjectDistance" ,
+			"BrightnessValue",
+			"SceneType" ,
+			"ExposureProgram",
+			"FocalLengthIn35mmFilm" ,
+			"GainControl" ,
+			"Contrast" ,
+			"Saturation" ,
+			"RelatedSoundFile" ,
+			"Sharpness" ,
+			"NativeDigest",
+			"FlashpixVersion",
+			
+			// http://ns.iview-multimedia.com/mediapro/1.0/
+			// Iptc4xmpCore:Location
+			"Location" ,
+			"Status",
+			"Event",
+			
+			// http://iptc.org/std/Iptc4xmpCore/1.0/xmlns
+			"CountryCode",
+			"IntellectualGenre",
+
+			// http://ns.adobe.com/xap/1.0									
+			"Rating" ,
+			"ModifyDate" ,
+			"CreateDate" ,
+			"CreatorTool" ,
+			"DocumentID" ,
+			"InstanceID", 
+			"WebStatement",
+
+			// http://ns.adobe.com/lightroom/1.0
+			"hierarchicalKeywords" ,
+
+			// http://purl.org/dc/elements/1.1
+			"format"
+		};
+
+
+		public XmpTagsMetadata (string photo_path, string import_path)
+		{
+			this.xmptags_photofile = photo_path;
+			this.xmptags_sidecar_exist = false;
+			this.xmptags_found_xmp_tags = false;
+
+			// Check if we can find a sidecar file.
+			string tmpfile = String.Format ("{0}{1}{2}.xmp",
+						Path.GetDirectoryName (photo_path),
+						Path.DirectorySeparatorChar,
+						Path.GetFileName (photo_path));
+
+			if (File.Exists (tmpfile))
+				this.xmptags_sidecar_exist = true;
+			else {
+				// FIXME, When F-Spot copies the sidecar file with the photo, we do not need the extra path.
+				// FIXME bug #342892 http://bugzilla.gnome.org/show_bug.cgi?id=342892
+				tmpfile = String.Format ("{0}{1}{2}.xmp",
+							Path.GetDirectoryName (import_path),
+							Path.DirectorySeparatorChar,
+							Path.GetFileName (import_path));
+				if (File.Exists (tmpfile))
+					this.xmptags_sidecar_exist = true;
+			}
+			if (this.xmptags_sidecar_exist) {
+				this.xmptags_sidecarfile = tmpfile;
+				System.Console.WriteLine ("XMP Sidecar file found at {0}", this.xmptags_sidecarfile);
+			}
+			
+			// Initialize the xmp tags hash tables.
+			xmp_table = new System.Collections.Hashtable ();
+			foreach (string t1 in xmp_tags) 
+				xmp_table [t1] = null;
+			xmp_bag_table = new System.Collections.Hashtable ();
+			foreach (string b1 in xmp_bag_tags) 
+				xmp_bag_table [b1] = null;
+		}
+		
+		private string TrimEmptyChars (string txt)
+		{
+			// This small function will ensure there are no null characters in a string.
+
+			string tmp;
+
+			// Return if already empty
+			if ( (txt == null) || (txt.Length == 0) )
+				return null;
+
+			// Clean normal blanks
+			tmp = txt.Trim();
+			if ( (tmp == null) || (tmp.Length == 0) )
+				return null;
+
+			// Clean char 0
+			char[] emptychar = new char[1];
+			emptychar[0] = (char) 0;
+			tmp = tmp.Trim (emptychar);
+			if ( (tmp == null) || (tmp.Length == 0) )
+				return null;
+
+			// clean normal blanks again
+			tmp = tmp.Trim();
+
+			if ( (tmp == null) || (tmp.Length == 0) )
+				return null;
+			return tmp;
+		}
+
+		private void UpdateXMPBagTable (string predicate, string full_path)
+		{
+			// Return directly if nothing to add.
+			if (items_list.Count == 0)
+				return;
+				
+			try {
+				// Check if we already have some values, if so, add them to the items_list so we do not loose them
+				if (xmp_bag_table [ predicate ] != null) {
+					if (xmp_bag_table [ predicate ] is string [])
+						foreach (string keyword in (xmp_bag_table [ predicate ] as string [])) {
+							// Add keyword if it does not exist already.
+							if ( !items_list.Contains (keyword)) {
+								items_list.Add(keyword); // add keyword to the list of keywords
+							} 
+					} else if (xmp_bag_table [ predicate ] is string) {
+						// Add keyword if it does not already exist
+						if (!items_list.Contains (xmp_bag_table [ predicate ] as string)) {
+							items_list.Add(xmp_bag_table [ predicate ] as string); // add keyword to the list of keywords
+						}
+					}
+				}
+
+				// Ok, lets add this bag to its proper place in the hash table
+				bool found = false;
+				foreach (string str in xmp_bag_tags)
+					if (str == predicate) { // found the proper entry
+						if (items_list[0] is string) {
+							if (items_list.Count == 1) {
+								xmp_bag_table [ predicate ] = items_list[0].ToString();
+							
+								// Remove weird characters.
+								xmp_bag_table [ predicate ] = TrimEmptyChars( xmp_bag_table [ predicate ] as string);
+								
+							} else if (items_list.Count > 1) {
+								xmp_bag_table [ predicate ] = new string [items_list.Count];
+								items_list.CopyTo ( xmp_bag_table [ predicate ] as string [] );
+							}
+						} else if (items_list[0] is Bag_Couple) {
+							if (items_list.Count == 1) {
+								xmp_bag_table [ predicate ] = items_list[0];								
+							} else if (items_list.Count > 1) {
+								xmp_bag_table [ predicate ] = new Bag_Couple [items_list.Count];
+								items_list.CopyTo ( xmp_bag_table [ predicate ] as Bag_Couple [] );
+							}
+
+						}
+						found = true;
+						break;
+					}
+				if (!found)
+					Console.WriteLine ("====> Not handled importing XMP bag predicate >>{0}<<::>{1}<, >>{2}<<", predicate, full_path);							
+			
+			} catch {
+				Console.WriteLine ("====> Not handled importing XMP bag predicate >>{0}<<, >>{1}<<", predicate, full_path);			
+			}
+		}
+	
+		
+		private void UpdateXMPTable (string predicate, string tag_value, string full_path)
+		{
+			try {
+				bool found = false;
+				foreach (string str in xmp_tags)
+					if (str == predicate) { // found the proper entry
+						xmp_table [ predicate ] = tag_value;
+
+						// Remove some weird empty charachters
+						xmp_table [ predicate ] = TrimEmptyChars( xmp_table [ predicate ] as string);
+						found = true;
+						break;
+					}
+				if (!found)
+					Console.WriteLine ("====> Not handled importing XMP row predicate >>{0}<<::>{1}<, >>{2}<<", predicate, tag_value, full_path);							
+			} catch {
+				Console.WriteLine ("====> Not handled importing XMP row predicate >>{0}<<::>{1}<, >>{2}<<", predicate, tag_value, full_path);			
+			}
+		}
+		
+		public string GetXmpTag (string predicate)
+		{
+			try {
+				return xmp_table [ predicate ] as string;
+			
+			} catch {
+				Console.WriteLine ("====> Not handled importing XMP row predicate >>{0}<<", predicate);			
+			}	
+			return null;
+		}
+
+		public object GetXmpBag (string predicate)
+		{
+			try {
+				return xmp_bag_table [ predicate ];
+			} catch {
+				Console.WriteLine ("====> Not handled importing XMP bag predicate >>{0}<<", predicate);			
+			}	
+			return null;
+		}
+		
+		public string GetXmpBag_0 (string predicate)
+		{
+			// This function will return a bag as a string. 
+			// Either the bag is a string, or first entry in a string array
+			if (GetXmpBag (predicate) is string)
+				return GetXmpBag (predicate) as string;
+			else if (GetXmpBag (predicate) is string []) {
+				string [] tmp = GetXmpBag (predicate) as string [];
+				if ( (tmp != null) && (tmp.Length > 0) )
+					return tmp[0].ToString();
+			}
+			return null;
+		}
+
+		private void GetBagData (MemoryStore substore, string CollectionPredicate)
+		{
+			// This function will collect all variables that are in a bag/collection.
+			// This is typically keywords, but also some other fields.
+			// This function will store the bag's variable into items_list, and if there are more items
+			// in the bag/collection, call itself recursively with the next item.
+			string type = null;
+
+			foreach (Statement stmt in substore) {
+				if (stmt.Predicate == MetadataStore.Namespaces.Resolve ("rdf:type")) {
+					string prefix; // not used
+					MetadataStore.Namespaces.Normalize (stmt.Object.ToString (), out prefix, out type);
+				}
+			}
+
+			foreach (Statement sub in substore) {
+				if (sub.Object is Literal) {
+					string predicate = sub.Predicate.ToString ();
+					string title = System.IO.Path.GetFileName (predicate);
+					string value = ((Literal)(sub.Object)).Value;
+					
+					// Remember that the title will be localised in GetDescription call.
+					Description.GetDescription (substore, sub, out title, out value);
+					
+					// Lets get the non-localized title again.
+					title = System.IO.Path.GetFileName (predicate);
+#if enable_debug
+					Console.WriteLine ("8 type = >>{0}<<", type);
+					Console.WriteLine ("9 title = >>{0}<<", title);
+					Console.WriteLine ("10 value = >>{0}<<", value);
+#endif
+
+					if (type == null) {
+						Bag_Couple bag_couple = new Bag_Couple (title, value);
+						items_list.Add (bag_couple);
+						// Nothing to collect
+					} else {
+						// Add this value to the collection (to be processed after we are 
+						// finished with this main collection.
+						items_list.Add(value);
+					}
+				} else {
+					if (type == null) {
+						MemoryStore substore2 = substore.Select (new Statement ((Entity)sub.Object, null, null, null));
+						if (substore.StatementCount > 0) {
+							// Since there are more bags/statements to collect.
+							// Lets call ourself recursively to collect next one.
+							GetBagData (substore2, CollectionPredicate);
+						}
+					}
+				} // else
+			} // foreach
+		}
+		
+
+		public bool EmptyTags()
+		{
+			return ( (this.xmptags_found_xmp_tags || this.xmptags_found_sidecar_tag) == false);
+		}
+
+		public bool AnySidecarTags()
+		{
+			return ( this.xmptags_found_sidecar_tag);
+		}
+		
+		public void Read_Sidecar_tags()
+		{
+			bool tmp_bool = false;
+			if (xmptags_sidecar_exist)
+				tmp_bool = Read_tags ( true );
+			this.xmptags_found_sidecar_tag = tmp_bool;
+		}
+		
+		public void Read_Embedded_tags()
+		{
+			this.xmptags_found_xmp_tags = Read_tags ( false );
+		}
+
+		private bool Read_tags (bool read_sidecar_data)
+		{
+			XmpFile xmpfile = null;
+			MetadataStore store = new MetadataStore ();		
+
+			// Initialize variable
+			items_list = new System.Collections.ArrayList();
+			
+			try {
+				// First prepare the meta store, either from a sidecar of embedded 
+				if (read_sidecar_data) {
+#if enable_debug				
+					Console.WriteLine ("Importing sidecar metadata from sidecar {0}", xmptags_sidecarfile);
+#endif
+					xmpfile = new XmpFile(System.IO.File.OpenRead(xmptags_sidecarfile));
+					store = xmpfile.Store;
+				} else {
+#if enable_debug				
+					Console.WriteLine ("Importing embedded metadata from {0}", xmptags_photofile);	
+#endif
+					ImageFile img = ImageFile.Create (xmptags_photofile);
+
+					if (img is SemWeb.StatementSource) {
+						SemWeb.StatementSource source = (SemWeb.StatementSource)img;
+						source.Select (store);
+					}
+
+					// Get the EXIF description
+					if (img is FSpot.JpegFile) {
+						FSpot.JpegFile jimg = img as FSpot.JpegFile;
+						UpdateXMPTable ("exif_description", jimg.Description, "from EXIF");
+//						Console.WriteLine ("Set the description to JPEG = >>{0}<<" ,jimg.Description);	
+					} else if (img is FSpot.Png.PngFile) {
+						FSpot.Png.PngFile png = img as FSpot.Png.PngFile;
+						UpdateXMPTable ("exif_description", png.Description, "from PNG");						
+//						Console.WriteLine ("Set the description to PNG = >>{0}<<" ,png.Description);	
+					}
+
+				} // if xmptags_sidecar
+
+				// If there are any XMP tags, go through them one by one...
+				if (store.StatementCount > 0) {
+					foreach (SemWeb.Statement stmt in store) {				
+
+						// Skip anonymous subjects because they are
+						// probably part of a collection
+						if (stmt.Subject.Uri == null) 
+							continue;
+
+						string title;
+						string predicate;
+						string value;
+						title = null;
+						value = null;
+						
+						Description.GetDescription (store, stmt, out title, out value);
+
+						// The title is translated to localised language in GetDescription, 
+						// so we go back to original predicate
+						predicate = System.IO.Path.GetFileName (stmt.Predicate.ToString ()); 
+
+
+						bool thisIsCollection = (value == null);
+
+#if enable_debug
+						Console.WriteLine ("1 Subject = >>{0}<<" ,stmt.Subject);	
+						Console.WriteLine ("2 Predicate = >>{0}<<", stmt.Predicate);
+						Console.WriteLine ("3 Object = >>{0}<<", stmt.Object);						
+						Console.WriteLine ("4 Meta = >>{0}<<", stmt.Meta);
+						Console.WriteLine ("5 Uri = >>{0}<<", stmt.Subject.Uri);
+						Console.WriteLine ("6 Title = >>{0}<<", title);
+						Console.WriteLine ("7 Value = >>{0}<< --- Collection {1}", value == null ? "null" : value, value == null ? "YES" : "no" );
+#endif
+
+						string stmt_obj_str;
+						if (stmt.Object is SemWeb.Literal)
+							stmt_obj_str = ((SemWeb.Literal)(stmt.Object)).Value;
+						else
+							stmt_obj_str = stmt.Object.ToString();
+
+						if (thisIsCollection) {
+							MemoryStore substore = store.Select (new SemWeb.Statement ((Entity)stmt.Object, null, null, null));
+							
+							// Since start tag was found for this list, we clear it so we have a know starting state.
+							items_list.Clear();
+							
+							// Collect all the values stored in this collection/bag.
+							// It will put the result in the items_list array
+							GetBagData (substore, predicate);
+							
+							// Convert the items_list (from the collection/bag's values) to specific variables/keywords
+							UpdateXMPBagTable (predicate, stmt.Predicate.ToString());
+						} else {
+							// This XMP title just contains one value, and not a collection/bag. 
+							UpdateXMPTable (predicate, stmt_obj_str, stmt.Predicate.ToString());
+						}
+					} // foreach
+				} else 
+					return false; // No XMP tags found
+				
+
+			} catch (Exception e) {
+				Console.WriteLine ("Exception: {0}", e.ToString ());
+				return false;
+			}
+		
+#if enable_debug		
+			foreach (string tag in xmp_tags)
+				if (xmp_table [ tag ] != null)
+					Console.WriteLine ("{0} --> >>{1}<<", tag, xmp_table [ tag ]);
+			foreach (string tag in xmp_bag_tags)
+				if (xmp_bag_table [ tag ] != null) {
+					Console.Write ("{0} --> ", tag );
+					if (xmp_bag_table [ tag ] is string)
+						Console.Write (">>{0}<<", xmp_bag_table [ tag ] as string);
+					else if (xmp_bag_table [ tag ] is string [])
+						foreach (string keyword in (xmp_bag_table [ tag ] as string []))
+							Console.Write (">>{0}<<, ", keyword );
+					else if (xmp_bag_table [ tag ] is Bag_Couple ) {
+						Bag_Couple couple = xmp_bag_table [ tag ] as Bag_Couple;
+						Console.Write (">>{0}::{1}<< ", couple.Title, couple.Value );
+					} else if (xmp_bag_table [ tag ] is Bag_Couple [])
+						foreach (Bag_Couple couple in (xmp_bag_table [ tag ] as Bag_Couple []))
+							Console.Write (">>{0}::{1}<< ", couple.Title, couple.Value );
+
+
+					else Console.Write ("XXXXX=====XXXXX=====XXXXX Not string, nor string[]");
+					Console.WriteLine("");
+				}
+#endif
+//			Console.WriteLine ("Done importing xmp metadata");
+			return true;
+		}
+	}
+
+} // namespace
