/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Pix * * Copyright (C) 2009 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "exiv2-utils.h" #include "gth-metadata-provider-exiv2.h" struct _GthMetadataProviderExiv2Private { GSettings *general_settings; }; G_DEFINE_TYPE_WITH_CODE (GthMetadataProviderExiv2, gth_metadata_provider_exiv2, GTH_TYPE_METADATA_PROVIDER, G_ADD_PRIVATE (GthMetadataProviderExiv2)) static void gth_metadata_provider_exiv2_finalize (GObject *object) { GthMetadataProviderExiv2 *self; self = GTH_METADATA_PROVIDER_EXIV2 (object); _g_object_unref (self->priv->general_settings); G_OBJECT_CLASS (gth_metadata_provider_exiv2_parent_class)->finalize (object); } static gboolean gth_metadata_provider_exiv2_can_read (GthMetadataProvider *self, GthFileData *file_data, const char *mime_type, char **attribute_v) { if (! g_str_equal (mime_type, "*") && ! _g_content_type_is_a (mime_type, "image/*")) return FALSE; return _g_file_attributes_matches_any_v ("Exif::*," "Xmp::*," "Iptc::*," "Embedded::Image::*," "Embedded::Photo::*," "general::datetime," "general::title," "general::description," "general::location," "general::tags", attribute_v); } static gboolean gth_metadata_provider_exiv2_can_write (GthMetadataProvider *self, const char *mime_type, char **attribute_v) { if (! exiv2_supports_writes (mime_type)) return FALSE; return _g_file_attributes_matches_any_v ("Exif::*," "Xmp::*," "Iptc::*," "Embedded::Image::*," "Embedded::Photo::*," "general::datetime," "general::title," "general::description," "general::location," "general::tags", attribute_v); } static void gth_metadata_provider_exiv2_read (GthMetadataProvider *base, GthFileData *file_data, const char *attributes, GCancellable *cancellable) { GthMetadataProviderExiv2 *self = GTH_METADATA_PROVIDER_EXIV2 (base); GFile *sidecar; GthFileData *sidecar_file_data; gboolean update_general_attributes; if (! g_content_type_is_a (gth_file_data_get_mime_type (file_data), "image/*")) return; /* The embedded metadata is likely to be outdated if the user chooses to * not store metadata in files. */ if (self->priv->general_settings == NULL) self->priv->general_settings = g_settings_new (PIX_GENERAL_SCHEMA); update_general_attributes = g_settings_get_boolean (self->priv->general_settings, PREF_GENERAL_STORE_METADATA_IN_FILES); /* this function is executed in a secondary thread, so calling * slow sync functions is not a problem. */ exiv2_read_metadata_from_file (file_data->file, file_data->info, update_general_attributes, cancellable, NULL); /* sidecar data */ sidecar = exiv2_get_sidecar (file_data->file); sidecar_file_data = gth_file_data_new (sidecar, NULL); if (g_file_query_exists (sidecar_file_data->file, cancellable)) { gth_file_data_update_info (sidecar_file_data, "time::*"); if (g_file_query_exists (sidecar_file_data->file, cancellable)) exiv2_read_sidecar (sidecar_file_data->file, file_data->info, update_general_attributes); } g_object_unref (sidecar_file_data); g_object_unref (sidecar); } static void gth_metadata_provider_exiv2_write (GthMetadataProvider *base, GthMetadataWriteFlags flags, GthFileData *file_data, const char *attributes, GCancellable *cancellable) { GthMetadataProviderExiv2 *self = GTH_METADATA_PROVIDER_EXIV2 (base); void *buffer = NULL; gsize size; GError *error = NULL; GObject *metadata; int i; if (self->priv->general_settings == NULL) self->priv->general_settings = g_settings_new (PIX_GENERAL_SCHEMA); if (! (flags & GTH_METADATA_WRITE_FORCE_EMBEDDED) && ! g_settings_get_boolean (self->priv->general_settings, PREF_GENERAL_STORE_METADATA_IN_FILES)) return; if (! exiv2_supports_writes (gth_file_data_get_mime_type (file_data))) return; if (! _g_file_load_in_buffer (file_data->file, &buffer, &size, cancellable, &error)) return; metadata = g_file_info_get_attribute_object (file_data->info, "general::description"); if (metadata != NULL) { const char *tags_to_remove[] = { "Exif::Image::ImageDescription", "Xmp::tiff::ImageDescription", "Iptc::Application2::Headline", NULL }; const char *tags_to_update[] = { "Exif::Photo::UserComment", "Xmp::dc::description", "Iptc::Application2::Caption", NULL }; for (i = 0; tags_to_remove[i] != NULL; i++) g_file_info_remove_attribute (file_data->info, tags_to_remove[i]); /* Remove the value type to use the default type for each field * as described in exiv2_tools/main.c */ g_object_set (metadata, "value-type", NULL, NULL); for (i = 0; tags_to_update[i] != NULL; i++) { GObject *orig_metadata; orig_metadata = g_file_info_get_attribute_object (file_data->info, tags_to_update[i]); if (orig_metadata != NULL) { /* keep the original value type */ g_object_set (orig_metadata, "raw", gth_metadata_get_raw (GTH_METADATA (metadata)), "formatted", gth_metadata_get_formatted (GTH_METADATA (metadata)), NULL); } else g_file_info_set_attribute_object (file_data->info, tags_to_update[i], metadata); } } else { for (i = 0; _DESCRIPTION_TAG_NAMES[i] != NULL; i++) g_file_info_remove_attribute (file_data->info, _DESCRIPTION_TAG_NAMES[i]); } metadata = g_file_info_get_attribute_object (file_data->info, "general::title"); if (metadata != NULL) { g_object_set (metadata, "value-type", NULL, NULL); for (i = 0; _TITLE_TAG_NAMES[i] != NULL; i++) g_file_info_set_attribute_object (file_data->info, _TITLE_TAG_NAMES[i], metadata); } else { for (i = 0; _TITLE_TAG_NAMES[i] != NULL; i++) g_file_info_remove_attribute (file_data->info, _TITLE_TAG_NAMES[i]); } metadata = g_file_info_get_attribute_object (file_data->info, "general::location"); if (metadata != NULL) { g_object_set (metadata, "value-type", NULL, NULL); for (i = 0; _LOCATION_TAG_NAMES[i] != NULL; i++) g_file_info_set_attribute_object (file_data->info, _LOCATION_TAG_NAMES[i], metadata); } else { for (i = 0; _LOCATION_TAG_NAMES[i] != NULL; i++) g_file_info_remove_attribute (file_data->info, _LOCATION_TAG_NAMES[i]); } metadata = g_file_info_get_attribute_object (file_data->info, "general::tags"); if (metadata != NULL) { if (GTH_IS_METADATA (metadata)) g_object_set (metadata, "value-type", NULL, NULL); for (i = 0; _KEYWORDS_TAG_NAMES[i] != NULL; i++) g_file_info_set_attribute_object (file_data->info, _KEYWORDS_TAG_NAMES[i], metadata); } else { for (i = 0; _KEYWORDS_TAG_NAMES[i] != NULL; i++) g_file_info_remove_attribute (file_data->info, _KEYWORDS_TAG_NAMES[i]); } metadata = g_file_info_get_attribute_object (file_data->info, "general::rating"); if (metadata != NULL) { if (GTH_IS_METADATA (metadata)) g_object_set (metadata, "value-type", NULL, NULL); for (i = 0; _RATING_TAG_NAMES[i] != NULL; i++) g_file_info_set_attribute_object (file_data->info, _RATING_TAG_NAMES[i], metadata); } else { for (i = 0; _RATING_TAG_NAMES[i] != NULL; i++) g_file_info_remove_attribute (file_data->info, _RATING_TAG_NAMES[i]); } metadata = g_file_info_get_attribute_object (file_data->info, "general::datetime"); if (metadata != NULL) { GthMetadata *xmp_metadata = NULL; GTimeVal timeval; if (_g_time_val_from_exif_date (gth_metadata_get_raw (GTH_METADATA (metadata)), &timeval)) { char *xmp_format; xmp_metadata = gth_metadata_new (); xmp_format = _g_time_val_to_xmp_date (&timeval); g_object_set (xmp_metadata, "raw", xmp_format, "formatted", gth_metadata_get_formatted (GTH_METADATA (metadata)), "value-type", NULL, /* use the default type as described in extensions/exiv2_tools/main.c */ NULL); g_free (xmp_format); } for (i = 0; _ORIGINAL_DATE_TAG_NAMES[i] != NULL; i++) { if (g_str_has_prefix (_ORIGINAL_DATE_TAG_NAMES[i], "Xmp::")) { if (xmp_metadata != NULL) g_file_info_set_attribute_object (file_data->info, _ORIGINAL_DATE_TAG_NAMES[i], G_OBJECT (xmp_metadata)); } else g_file_info_set_attribute_object (file_data->info, _ORIGINAL_DATE_TAG_NAMES[i], metadata); } _g_object_unref (xmp_metadata); } else { for (i = 0; _ORIGINAL_DATE_TAG_NAMES[i] != NULL; i++) g_file_info_remove_attribute (file_data->info, _ORIGINAL_DATE_TAG_NAMES[i]); } if (exiv2_write_metadata_to_buffer (&buffer, &size, file_data->info, NULL, &error)) { GFileInfo *tmp_info; _g_file_write (file_data->file, FALSE, G_FILE_CREATE_NONE, buffer, size, cancellable, &error); tmp_info = g_file_info_new (); g_file_info_set_attribute_uint64 (tmp_info, G_FILE_ATTRIBUTE_TIME_MODIFIED, g_file_info_get_attribute_uint64 (file_data->info, G_FILE_ATTRIBUTE_TIME_MODIFIED)); g_file_info_set_attribute_uint32 (tmp_info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, g_file_info_get_attribute_uint32 (file_data->info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC)); g_file_set_attributes_from_info (file_data->file, tmp_info, G_FILE_QUERY_INFO_NONE, NULL, NULL); g_object_unref (tmp_info); } if (buffer != NULL) g_free (buffer); g_clear_error (&error); } static void gth_metadata_provider_exiv2_class_init (GthMetadataProviderExiv2Class *klass) { GObjectClass *object_class; GthMetadataProviderClass *mp_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = gth_metadata_provider_exiv2_finalize; mp_class = GTH_METADATA_PROVIDER_CLASS (klass); mp_class->can_read = gth_metadata_provider_exiv2_can_read; mp_class->can_write = gth_metadata_provider_exiv2_can_write; mp_class->read = gth_metadata_provider_exiv2_read; mp_class->write = gth_metadata_provider_exiv2_write; } static void gth_metadata_provider_exiv2_init (GthMetadataProviderExiv2 *self) { self->priv = gth_metadata_provider_exiv2_get_instance_private (self); self->priv->general_settings = NULL; }