/* LIBPIKA - The PIKA Library * Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball * * pikaimagemetadata.c * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include "config.h" #include #include #include #include "pika.h" #include "pikaimagemetadata.h" #include "libpika-intl.h" static gchar * pika_image_metadata_interpret_comment (gchar *comment); static void pika_image_metadata_rotate (PikaImage *image, GExiv2Orientation orientation); /* public functions */ /** * pika_image_metadata_load_prepare: * @image: The image * @mime_type: The loaded file's mime-type * @file: The file to load the metadata from * @error: Return location for error * * Loads and returns metadata from @file to be passed into * pika_image_metadata_load_finish(). * * Returns: (transfer full): The file's metadata. * * Since: 2.10 */ PikaMetadata * pika_image_metadata_load_prepare (PikaImage *image, const gchar *mime_type, GFile *file, GError **error) { PikaMetadata *metadata; g_return_val_if_fail (PIKA_IS_IMAGE (image), NULL); g_return_val_if_fail (mime_type != NULL, NULL); g_return_val_if_fail (G_IS_FILE (file), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); metadata = pika_metadata_load_from_file (file, error); return metadata; } static gchar * pika_image_metadata_interpret_comment (gchar *comment) { /* Exiv2 can return unwanted text at the start of a comment * taken from Exif.Photo.UserComment since 0.27.3. * Let's remove that part and return NULL if there * is nothing else left as comment. */ if (comment) { if (g_str_has_prefix (comment, "charset=Ascii ")) { gchar *real_comment; /* Skip "charset=Ascii " (length 14) to find the real comment */ real_comment = g_strdup (comment + 14); g_free (comment); comment = real_comment; } if (comment[0] == '\0') { /* Removing an empty comment.*/ g_free (comment); return NULL; } } return comment; } /** * pika_image_metadata_load_finish: * @image: The image * @mime_type: The loaded file's mime-type * @metadata: The metadata to set on the image * @flags: Flags to specify what of the metadata to apply to the image * * Applies the @metadata previously loaded with * pika_image_metadata_load_prepare() to the image, taking into account * the passed @flags. * * Since: 3.0 */ void pika_image_metadata_load_finish (PikaImage *image, const gchar *mime_type, PikaMetadata *metadata, PikaMetadataLoadFlags flags) { g_return_if_fail (PIKA_IS_IMAGE (image)); g_return_if_fail (GEXIV2_IS_METADATA (metadata)); if (flags & PIKA_METADATA_LOAD_COMMENT) { gchar *comment; GError *error = NULL; comment = gexiv2_metadata_try_get_tag_interpreted_string (GEXIV2_METADATA (metadata), "Exif.Photo.UserComment", &error); if (error) { /* XXX. Should this be rather a user-facing error? */ g_printerr ("%s: unreadable '%s' metadata tag: %s\n", G_STRFUNC, "Exif.Photo.UserComment", error->message); g_clear_error (&error); } else if (comment) { comment = pika_image_metadata_interpret_comment (comment); } if (! comment) { comment = gexiv2_metadata_try_get_tag_interpreted_string (GEXIV2_METADATA (metadata), "Exif.Image.ImageDescription", &error); if (error) { g_printerr ("%s: unreadable '%s' metadata tag: %s\n", G_STRFUNC, "Exif.Image.ImageDescription", error->message); g_clear_error (&error); } } if (comment) { PikaParasite *parasite; parasite = pika_parasite_new ("pika-comment", PIKA_PARASITE_PERSISTENT, strlen (comment) + 1, comment); g_free (comment); pika_image_attach_parasite (image, parasite); pika_parasite_free (parasite); } } if (flags & PIKA_METADATA_LOAD_RESOLUTION) { gdouble xres; gdouble yres; PikaUnit unit; if (pika_metadata_get_resolution (metadata, &xres, &yres, &unit)) { pika_image_set_resolution (image, xres, yres); pika_image_set_unit (image, unit); } } if (! (flags & PIKA_METADATA_LOAD_ORIENTATION)) { gexiv2_metadata_try_set_orientation (GEXIV2_METADATA (metadata), GEXIV2_ORIENTATION_NORMAL, NULL); } if (flags & PIKA_METADATA_LOAD_COLORSPACE) { PikaColorProfile *profile = pika_image_get_color_profile (image); /* only look for colorspace information from metadata if the * image didn't contain an embedded color profile */ if (! profile) { PikaMetadataColorspace colorspace; colorspace = pika_metadata_get_colorspace (metadata); switch (colorspace) { case PIKA_METADATA_COLORSPACE_UNSPECIFIED: case PIKA_METADATA_COLORSPACE_UNCALIBRATED: case PIKA_METADATA_COLORSPACE_SRGB: /* use sRGB, a NULL profile will do the right thing */ break; case PIKA_METADATA_COLORSPACE_ADOBERGB: profile = pika_color_profile_new_rgb_adobe (); break; } if (profile) pika_image_set_color_profile (image, profile); } if (profile) g_object_unref (profile); } pika_image_set_metadata (image, metadata); } /** * pika_image_metadata_load_thumbnail: * @file: A #GFile image * @error: Return location for error message * * Retrieves a thumbnail from metadata if present. * * Returns: (transfer none) (nullable): a #PikaImage of the @file thumbnail. * * Since: 2.10 */ PikaImage * pika_image_metadata_load_thumbnail (GFile *file, GError **error) { PikaMetadata *metadata; GInputStream *input_stream; GdkPixbuf *pixbuf; guint8 *thumbnail_buffer; gint thumbnail_size; PikaImage *image = NULL; g_return_val_if_fail (G_IS_FILE (file), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); metadata = pika_metadata_load_from_file (file, error); if (! metadata) return NULL; if (! gexiv2_metadata_get_exif_thumbnail (GEXIV2_METADATA (metadata), &thumbnail_buffer, &thumbnail_size)) { g_object_unref (metadata); return NULL; } input_stream = g_memory_input_stream_new_from_data (thumbnail_buffer, thumbnail_size, (GDestroyNotify) g_free); pixbuf = gdk_pixbuf_new_from_stream (input_stream, NULL, error); g_object_unref (input_stream); if (pixbuf) { PikaLayer *layer; image = pika_image_new (gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), PIKA_RGB); pika_image_undo_disable (image); layer = pika_layer_new_from_pixbuf (image, _("Background"), pixbuf, 100.0, pika_image_get_default_new_layer_mode (image), 0.0, 0.0); g_object_unref (pixbuf); pika_image_insert_layer (image, layer, NULL, 0); pika_image_metadata_rotate (image, gexiv2_metadata_try_get_orientation (GEXIV2_METADATA (metadata), NULL)); } g_object_unref (metadata); return image; } /* private functions */ static void pika_image_metadata_rotate (PikaImage *image, GExiv2Orientation orientation) { switch (orientation) { case GEXIV2_ORIENTATION_UNSPECIFIED: case GEXIV2_ORIENTATION_NORMAL: /* standard orientation, do nothing */ break; case GEXIV2_ORIENTATION_HFLIP: pika_image_flip (image, PIKA_ORIENTATION_HORIZONTAL); break; case GEXIV2_ORIENTATION_ROT_180: pika_image_rotate (image, PIKA_ROTATE_180); break; case GEXIV2_ORIENTATION_VFLIP: pika_image_flip (image, PIKA_ORIENTATION_VERTICAL); break; case GEXIV2_ORIENTATION_ROT_90_HFLIP: /* flipped diagonally around '\' */ pika_image_rotate (image, PIKA_ROTATE_90); pika_image_flip (image, PIKA_ORIENTATION_HORIZONTAL); break; case GEXIV2_ORIENTATION_ROT_90: /* 90 CW */ pika_image_rotate (image, PIKA_ROTATE_90); break; case GEXIV2_ORIENTATION_ROT_90_VFLIP: /* flipped diagonally around '/' */ pika_image_rotate (image, PIKA_ROTATE_90); pika_image_flip (image, PIKA_ORIENTATION_VERTICAL); break; case GEXIV2_ORIENTATION_ROT_270: /* 90 CCW */ pika_image_rotate (image, PIKA_ROTATE_270); break; default: /* shouldn't happen */ break; } }