mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-07-03 09:23:24 +00:00

because it confuses gtk-doc and breaks some links. Also change the "Index of new symbols in GIMP 2.x" sections to be what seems to be the modern standard (looked at the GLib and GTK+ docs), and update some other stuff.
896 lines
30 KiB
C
896 lines
30 KiB
C
/* LIBGIMP - The GIMP Library
|
|
* Copyright (C) 1995-2000 Peter Mattis and Spencer Kimball
|
|
*
|
|
* gimpmetadata.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
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <gexiv2/gexiv2.h>
|
|
|
|
#include "gimp.h"
|
|
#include "gimpui.h"
|
|
#include "gimpmetadata.h"
|
|
|
|
#include "libgimp-intl.h"
|
|
|
|
|
|
static void gimp_image_metadata_rotate (gint32 image_ID,
|
|
GExiv2Orientation orientation);
|
|
static GdkPixbuf * gimp_image_metadata_rotate_pixbuf (GdkPixbuf *pixbuf,
|
|
GExiv2Orientation orientation);
|
|
static void gimp_image_metadata_rotate_query (gint32 image_ID,
|
|
const gchar *mime_type,
|
|
GimpMetadata *metadata,
|
|
gboolean interactive);
|
|
static gboolean gimp_image_metadata_rotate_dialog (gint32 image_ID,
|
|
GExiv2Orientation orientation,
|
|
const gchar *parasite_name);
|
|
|
|
|
|
/* public functions */
|
|
|
|
/**
|
|
* gimp_image_metadata_load_prepare:
|
|
* @image_ID: 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
|
|
* gimp_image_metadata_load_finish().
|
|
*
|
|
* Returns: The file's metadata.
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
GimpMetadata *
|
|
gimp_image_metadata_load_prepare (gint32 image_ID,
|
|
const gchar *mime_type,
|
|
GFile *file,
|
|
GError **error)
|
|
{
|
|
GimpMetadata *metadata;
|
|
|
|
g_return_val_if_fail (image_ID > 0, 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 = gimp_metadata_load_from_file (file, error);
|
|
|
|
if (metadata)
|
|
{
|
|
#if 0
|
|
{
|
|
gchar *xml = gimp_metadata_serialize (metadata);
|
|
GimpMetadata *new = gimp_metadata_deserialize (xml);
|
|
gchar *xml2 = gimp_metadata_serialize (new);
|
|
|
|
FILE *f = fopen ("/tmp/gimp-test-xml1", "w");
|
|
fprintf (f, "%s", xml);
|
|
fclose (f);
|
|
|
|
f = fopen ("/tmp/gimp-test-xml2", "w");
|
|
fprintf (f, "%s", xml2);
|
|
fclose (f);
|
|
|
|
system ("diff -u /tmp/gimp-test-xml1 /tmp/gimp-test-xml2");
|
|
|
|
g_free (xml);
|
|
g_free (xml2);
|
|
g_object_unref (new);
|
|
}
|
|
#endif
|
|
|
|
gexiv2_metadata_erase_exif_thumbnail (metadata);
|
|
}
|
|
|
|
return metadata;
|
|
}
|
|
|
|
/**
|
|
* gimp_image_metadata_load_finish:
|
|
* @image_ID: 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
|
|
* @interactive: Whether this function is allowed to query info with dialogs
|
|
*
|
|
* Applies the @metadata previously loaded with
|
|
* gimp_image_metadata_load_prepare() to the image, taking into account
|
|
* the passed @flags.
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
void
|
|
gimp_image_metadata_load_finish (gint32 image_ID,
|
|
const gchar *mime_type,
|
|
GimpMetadata *metadata,
|
|
GimpMetadataLoadFlags flags,
|
|
gboolean interactive)
|
|
{
|
|
g_return_if_fail (image_ID > 0);
|
|
g_return_if_fail (mime_type != NULL);
|
|
g_return_if_fail (GEXIV2_IS_METADATA (metadata));
|
|
|
|
if (flags & GIMP_METADATA_LOAD_COMMENT)
|
|
{
|
|
gchar *comment;
|
|
|
|
comment = gexiv2_metadata_get_tag_interpreted_string (metadata,
|
|
"Exif.Photo.UserComment");
|
|
if (! comment)
|
|
comment = gexiv2_metadata_get_tag_interpreted_string (metadata,
|
|
"Exif.Image.ImageDescription");
|
|
|
|
if (comment)
|
|
{
|
|
GimpParasite *parasite;
|
|
|
|
parasite = gimp_parasite_new ("gimp-comment",
|
|
GIMP_PARASITE_PERSISTENT,
|
|
strlen (comment) + 1,
|
|
comment);
|
|
g_free (comment);
|
|
|
|
gimp_image_attach_parasite (image_ID, parasite);
|
|
gimp_parasite_free (parasite);
|
|
}
|
|
}
|
|
|
|
if (flags & GIMP_METADATA_LOAD_RESOLUTION)
|
|
{
|
|
gdouble xres;
|
|
gdouble yres;
|
|
GimpUnit unit;
|
|
|
|
if (gimp_metadata_get_resolution (metadata, &xres, &yres, &unit))
|
|
{
|
|
gimp_image_set_resolution (image_ID, xres, yres);
|
|
gimp_image_set_unit (image_ID, unit);
|
|
}
|
|
}
|
|
|
|
if (flags & GIMP_METADATA_LOAD_ORIENTATION)
|
|
{
|
|
gimp_image_metadata_rotate_query (image_ID, mime_type,
|
|
metadata, interactive);
|
|
}
|
|
|
|
gimp_image_set_metadata (image_ID, metadata);
|
|
}
|
|
|
|
/**
|
|
* gimp_image_metadata_save_prepare:
|
|
* @image_ID: The image
|
|
* @mime_type: The saved file's mime-type
|
|
* @suggested_flags: Suggested default values for the @flags passed to
|
|
* gimp_image_metadata_save_finish()
|
|
*
|
|
* Gets the image metadata for saving it using
|
|
* gimp_image_metadata_save_finish().
|
|
*
|
|
* The @suggested_flags are determined from what kind of metadata
|
|
* (Exif, XMP, ...) is actually present in the image. The suggested
|
|
* value for GIMP_METADATA_SAVE_THUMBNAIL is determined by whether
|
|
* there was a thumbnail in the previously imported image.
|
|
*
|
|
* Returns: The image's metadata, prepared for saving.
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
GimpMetadata *
|
|
gimp_image_metadata_save_prepare (gint32 image_ID,
|
|
const gchar *mime_type,
|
|
GimpMetadataSaveFlags *suggested_flags)
|
|
{
|
|
GimpMetadata *metadata;
|
|
|
|
g_return_val_if_fail (image_ID > 0, NULL);
|
|
g_return_val_if_fail (mime_type != NULL, NULL);
|
|
g_return_val_if_fail (suggested_flags != NULL, NULL);
|
|
|
|
*suggested_flags = GIMP_METADATA_SAVE_ALL;
|
|
|
|
metadata = gimp_image_get_metadata (image_ID);
|
|
|
|
if (metadata)
|
|
{
|
|
GDateTime *datetime;
|
|
const GimpParasite *comment_parasite;
|
|
const gchar *comment = NULL;
|
|
gint image_width;
|
|
gint image_height;
|
|
gdouble xres;
|
|
gdouble yres;
|
|
gchar buffer[32];
|
|
|
|
image_width = gimp_image_width (image_ID);
|
|
image_height = gimp_image_height (image_ID);
|
|
|
|
datetime = g_date_time_new_now_local ();
|
|
|
|
comment_parasite = gimp_image_get_parasite (image_ID, "gimp-comment");
|
|
if (comment_parasite)
|
|
comment = gimp_parasite_data (comment_parasite);
|
|
|
|
/* Exif */
|
|
|
|
if (! gexiv2_metadata_has_exif (metadata))
|
|
*suggested_flags &= ~GIMP_METADATA_SAVE_EXIF;
|
|
|
|
if (comment)
|
|
{
|
|
gexiv2_metadata_set_tag_string (metadata,
|
|
"Exif.Photo.UserComment",
|
|
comment);
|
|
gexiv2_metadata_set_tag_string (metadata,
|
|
"Exif.Image.ImageDescription",
|
|
comment);
|
|
}
|
|
|
|
g_snprintf (buffer, sizeof (buffer),
|
|
"%d:%02d:%02d %02d:%02d:%02d",
|
|
g_date_time_get_year (datetime),
|
|
g_date_time_get_month (datetime),
|
|
g_date_time_get_day_of_month (datetime),
|
|
g_date_time_get_hour (datetime),
|
|
g_date_time_get_minute (datetime),
|
|
g_date_time_get_second (datetime));
|
|
gexiv2_metadata_set_tag_string (metadata,
|
|
"Exif.Image.DateTime",
|
|
buffer);
|
|
|
|
gexiv2_metadata_set_tag_string (metadata,
|
|
"Exif.Image.Software",
|
|
PACKAGE_STRING);
|
|
|
|
gimp_metadata_set_pixel_size (metadata,
|
|
image_width, image_height);
|
|
|
|
gimp_image_get_resolution (image_ID, &xres, &yres);
|
|
gimp_metadata_set_resolution (metadata, xres, yres,
|
|
gimp_image_get_unit (image_ID));
|
|
|
|
/* XMP */
|
|
|
|
if (! gexiv2_metadata_has_xmp (metadata))
|
|
*suggested_flags &= ~GIMP_METADATA_SAVE_XMP;
|
|
|
|
gexiv2_metadata_set_tag_string (metadata,
|
|
"Xmp.dc.Format",
|
|
mime_type);
|
|
|
|
if (! g_strcmp0 (mime_type, "image/tiff"))
|
|
{
|
|
/* TIFF specific XMP data */
|
|
|
|
g_snprintf (buffer, sizeof (buffer), "%d", image_width);
|
|
gexiv2_metadata_set_tag_string (metadata,
|
|
"Xmp.tiff.ImageWidth",
|
|
buffer);
|
|
|
|
g_snprintf (buffer, sizeof (buffer), "%d", image_height);
|
|
gexiv2_metadata_set_tag_string (metadata,
|
|
"Xmp.tiff.ImageLength",
|
|
buffer);
|
|
|
|
g_snprintf (buffer, sizeof (buffer),
|
|
"%d:%02d:%02d %02d:%02d:%02d",
|
|
g_date_time_get_year (datetime),
|
|
g_date_time_get_month (datetime),
|
|
g_date_time_get_day_of_month (datetime),
|
|
g_date_time_get_hour (datetime),
|
|
g_date_time_get_minute (datetime),
|
|
g_date_time_get_second (datetime));
|
|
gexiv2_metadata_set_tag_string (metadata,
|
|
"Xmp.tiff.DateTime",
|
|
buffer);
|
|
}
|
|
|
|
/* IPTC */
|
|
|
|
if (! gexiv2_metadata_has_iptc (metadata))
|
|
*suggested_flags &= ~GIMP_METADATA_SAVE_IPTC;
|
|
|
|
g_snprintf (buffer, sizeof (buffer),
|
|
"%d-%d-%d",
|
|
g_date_time_get_year (datetime),
|
|
g_date_time_get_month (datetime),
|
|
g_date_time_get_day_of_month (datetime));
|
|
gexiv2_metadata_set_tag_string (metadata,
|
|
"Iptc.Application2.DateCreated",
|
|
buffer);
|
|
|
|
g_snprintf (buffer, sizeof (buffer),
|
|
"%02d:%02d:%02d-%02d:%02d",
|
|
g_date_time_get_hour (datetime),
|
|
g_date_time_get_minute (datetime),
|
|
g_date_time_get_second (datetime),
|
|
g_date_time_get_hour (datetime),
|
|
g_date_time_get_minute (datetime));
|
|
gexiv2_metadata_set_tag_string (metadata,
|
|
"Iptc.Application2.TimeCreated",
|
|
buffer);
|
|
|
|
g_date_time_unref (datetime);
|
|
|
|
/* Thumbnail */
|
|
|
|
if (FALSE /* FIXME if (original image had a thumbnail) */)
|
|
*suggested_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
|
|
}
|
|
|
|
return metadata;
|
|
}
|
|
|
|
/**
|
|
* gimp_image_metadata_save_finish:
|
|
* @image_ID: The image
|
|
* @mime_type: The saved file's mime-type
|
|
* @metadata: The metadata to set on the image
|
|
* @flags: Flags to specify what of the metadata to save
|
|
* @file: The file to load the metadata from
|
|
* @error: Return location for error message
|
|
*
|
|
* Saves the @metadata retrieved from the image with
|
|
* gimp_image_metadata_save_prepare() to @file, taking into account
|
|
* the passed @flags.
|
|
*
|
|
* Return value: Whether the save was successful.
|
|
*
|
|
* Since: 2.10
|
|
*/
|
|
gboolean
|
|
gimp_image_metadata_save_finish (gint32 image_ID,
|
|
const gchar *mime_type,
|
|
GimpMetadata *metadata,
|
|
GimpMetadataSaveFlags flags,
|
|
GFile *file,
|
|
GError **error)
|
|
{
|
|
GExiv2Metadata *new_metadata;
|
|
gboolean support_exif;
|
|
gboolean support_xmp;
|
|
gboolean support_iptc;
|
|
gchar *value;
|
|
gboolean success = FALSE;
|
|
gint i;
|
|
|
|
g_return_val_if_fail (image_ID > 0, FALSE);
|
|
g_return_val_if_fail (mime_type != NULL, FALSE);
|
|
g_return_val_if_fail (GEXIV2_IS_METADATA (metadata), FALSE);
|
|
g_return_val_if_fail (G_IS_FILE (file), FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
if (! (flags & (GIMP_METADATA_SAVE_EXIF |
|
|
GIMP_METADATA_SAVE_XMP |
|
|
GIMP_METADATA_SAVE_IPTC |
|
|
GIMP_METADATA_SAVE_THUMBNAIL)))
|
|
return TRUE;
|
|
|
|
/* read metadata from saved file */
|
|
new_metadata = gimp_metadata_load_from_file (file, error);
|
|
|
|
if (! new_metadata)
|
|
return FALSE;
|
|
|
|
support_exif = gexiv2_metadata_get_supports_exif (new_metadata);
|
|
support_xmp = gexiv2_metadata_get_supports_xmp (new_metadata);
|
|
support_iptc = gexiv2_metadata_get_supports_iptc (new_metadata);
|
|
|
|
if ((flags & GIMP_METADATA_SAVE_EXIF) && support_exif)
|
|
{
|
|
gchar **exif_data = gexiv2_metadata_get_exif_tags (metadata);
|
|
|
|
for (i = 0; exif_data[i] != NULL; i++)
|
|
{
|
|
if (! gexiv2_metadata_has_tag (new_metadata, exif_data[i]) &&
|
|
gimp_metadata_is_tag_supported (exif_data[i], mime_type))
|
|
{
|
|
value = gexiv2_metadata_get_tag_string (metadata, exif_data[i]);
|
|
gexiv2_metadata_set_tag_string (new_metadata, exif_data[i],
|
|
value);
|
|
g_free (value);
|
|
}
|
|
}
|
|
|
|
g_strfreev (exif_data);
|
|
}
|
|
|
|
if ((flags & GIMP_METADATA_SAVE_XMP) && support_xmp)
|
|
{
|
|
gchar **xmp_data = gexiv2_metadata_get_xmp_tags (metadata);
|
|
|
|
for (i = 0; xmp_data[i] != NULL; i++)
|
|
{
|
|
if (! gexiv2_metadata_has_tag (new_metadata, xmp_data[i]) &&
|
|
gimp_metadata_is_tag_supported (xmp_data[i], mime_type))
|
|
{
|
|
value = gexiv2_metadata_get_tag_string (metadata, xmp_data[i]);
|
|
gexiv2_metadata_set_tag_string (new_metadata, xmp_data[i],
|
|
value);
|
|
g_free (value);
|
|
}
|
|
}
|
|
|
|
g_strfreev (xmp_data);
|
|
}
|
|
|
|
if ((flags & GIMP_METADATA_SAVE_IPTC) && support_iptc)
|
|
{
|
|
gchar **iptc_data = gexiv2_metadata_get_iptc_tags (metadata);
|
|
|
|
for (i = 0; iptc_data[i] != NULL; i++)
|
|
{
|
|
if (! gexiv2_metadata_has_tag (new_metadata, iptc_data[i]) &&
|
|
gimp_metadata_is_tag_supported (iptc_data[i], mime_type))
|
|
{
|
|
value = gexiv2_metadata_get_tag_string (metadata, iptc_data[i]);
|
|
gexiv2_metadata_set_tag_string (new_metadata, iptc_data[i],
|
|
value);
|
|
g_free (value);
|
|
}
|
|
}
|
|
|
|
g_strfreev (iptc_data);
|
|
}
|
|
|
|
if (flags & GIMP_METADATA_SAVE_THUMBNAIL)
|
|
{
|
|
GdkPixbuf *thumb_pixbuf;
|
|
gchar *thumb_buffer;
|
|
gint image_width;
|
|
gint image_height;
|
|
gsize count;
|
|
gint thumbw;
|
|
gint thumbh;
|
|
|
|
#define EXIF_THUMBNAIL_SIZE 256
|
|
|
|
image_width = gimp_image_width (image_ID);
|
|
image_height = gimp_image_height (image_ID);
|
|
|
|
if (image_width > image_height)
|
|
{
|
|
thumbw = EXIF_THUMBNAIL_SIZE;
|
|
thumbh = EXIF_THUMBNAIL_SIZE * image_height / image_width;
|
|
}
|
|
else
|
|
{
|
|
thumbh = EXIF_THUMBNAIL_SIZE;
|
|
thumbw = EXIF_THUMBNAIL_SIZE * image_width / image_height;
|
|
}
|
|
|
|
thumb_pixbuf = gimp_image_get_thumbnail (image_ID, thumbw, thumbh,
|
|
GIMP_PIXBUF_KEEP_ALPHA);
|
|
|
|
if (gdk_pixbuf_save_to_buffer (thumb_pixbuf, &thumb_buffer, &count,
|
|
"jpeg", NULL,
|
|
"quality", "75",
|
|
NULL))
|
|
{
|
|
gchar buffer[32];
|
|
|
|
gexiv2_metadata_set_exif_thumbnail_from_buffer (new_metadata,
|
|
(guchar *) thumb_buffer,
|
|
count);
|
|
|
|
g_snprintf (buffer, sizeof (buffer), "%d", thumbw);
|
|
gexiv2_metadata_set_tag_string (new_metadata,
|
|
"Exif.Thumbnail.ImageWidth",
|
|
buffer);
|
|
|
|
g_snprintf (buffer, sizeof (buffer), "%d", thumbh);
|
|
gexiv2_metadata_set_tag_string (new_metadata,
|
|
"Exif.Thumbnail.ImageLength",
|
|
buffer);
|
|
|
|
gexiv2_metadata_set_tag_string (new_metadata,
|
|
"Exif.Thumbnail.BitsPerSample",
|
|
"8 8 8");
|
|
gexiv2_metadata_set_tag_string (new_metadata,
|
|
"Exif.Thumbnail.SamplesPerPixel",
|
|
"3");
|
|
gexiv2_metadata_set_tag_string (new_metadata,
|
|
"Exif.Thumbnail.PhotometricInterpretation",
|
|
"6");
|
|
|
|
g_free (thumb_buffer);
|
|
}
|
|
|
|
g_object_unref (thumb_pixbuf);
|
|
}
|
|
|
|
success = gimp_metadata_save_to_file (new_metadata, file, error);
|
|
|
|
g_object_unref (new_metadata);
|
|
|
|
return success;
|
|
}
|
|
|
|
gint32
|
|
gimp_image_metadata_load_thumbnail (GFile *file,
|
|
GError **error)
|
|
{
|
|
GimpMetadata *metadata;
|
|
GInputStream *input_stream;
|
|
GdkPixbuf *pixbuf;
|
|
guint8 *thumbnail_buffer;
|
|
gint thumbnail_size;
|
|
gint32 image_ID = -1;
|
|
|
|
g_return_val_if_fail (G_IS_FILE (file), -1);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
|
|
|
metadata = gimp_metadata_load_from_file (file, error);
|
|
if (! metadata)
|
|
return -1;
|
|
|
|
if (! gexiv2_metadata_get_exif_thumbnail (metadata,
|
|
&thumbnail_buffer,
|
|
&thumbnail_size))
|
|
{
|
|
g_object_unref (metadata);
|
|
return -1;
|
|
}
|
|
|
|
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)
|
|
{
|
|
gint32 layer_ID;
|
|
|
|
image_ID = gimp_image_new (gdk_pixbuf_get_width (pixbuf),
|
|
gdk_pixbuf_get_height (pixbuf),
|
|
GIMP_RGB);
|
|
gimp_image_undo_disable (image_ID);
|
|
|
|
layer_ID = gimp_layer_new_from_pixbuf (image_ID, _("Background"),
|
|
pixbuf,
|
|
100.0, GIMP_NORMAL_MODE,
|
|
0.0, 0.0);
|
|
g_object_unref (pixbuf);
|
|
|
|
gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
|
|
|
|
gimp_image_metadata_rotate (image_ID,
|
|
gexiv2_metadata_get_orientation (metadata));
|
|
}
|
|
|
|
g_object_unref (metadata);
|
|
|
|
return image_ID;
|
|
}
|
|
|
|
|
|
/* private functions */
|
|
|
|
static void
|
|
gimp_image_metadata_rotate (gint32 image_ID,
|
|
GExiv2Orientation orientation)
|
|
{
|
|
switch (orientation)
|
|
{
|
|
case GEXIV2_ORIENTATION_UNSPECIFIED:
|
|
case GEXIV2_ORIENTATION_NORMAL: /* standard orientation, do nothing */
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_HFLIP:
|
|
gimp_image_flip (image_ID, GIMP_ORIENTATION_HORIZONTAL);
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_ROT_180:
|
|
gimp_image_rotate (image_ID, GIMP_ROTATE_180);
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_VFLIP:
|
|
gimp_image_flip (image_ID, GIMP_ORIENTATION_VERTICAL);
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_ROT_90_HFLIP: /* flipped diagonally around '\' */
|
|
gimp_image_rotate (image_ID, GIMP_ROTATE_90);
|
|
gimp_image_flip (image_ID, GIMP_ORIENTATION_HORIZONTAL);
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_ROT_90: /* 90 CW */
|
|
gimp_image_rotate (image_ID, GIMP_ROTATE_90);
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_ROT_90_VFLIP: /* flipped diagonally around '/' */
|
|
gimp_image_rotate (image_ID, GIMP_ROTATE_90);
|
|
gimp_image_flip (image_ID, GIMP_ORIENTATION_VERTICAL);
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_ROT_270: /* 90 CCW */
|
|
gimp_image_rotate (image_ID, GIMP_ROTATE_270);
|
|
break;
|
|
|
|
default: /* shouldn't happen */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static GdkPixbuf *
|
|
gimp_image_metadata_rotate_pixbuf (GdkPixbuf *pixbuf,
|
|
GExiv2Orientation orientation)
|
|
{
|
|
GdkPixbuf *rotated = NULL;
|
|
GdkPixbuf *temp;
|
|
|
|
switch (orientation)
|
|
{
|
|
case GEXIV2_ORIENTATION_UNSPECIFIED:
|
|
case GEXIV2_ORIENTATION_NORMAL: /* standard orientation, do nothing */
|
|
rotated = g_object_ref (pixbuf);
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_HFLIP:
|
|
rotated = gdk_pixbuf_flip (pixbuf, TRUE);
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_ROT_180:
|
|
rotated = gdk_pixbuf_rotate_simple (pixbuf, GDK_PIXBUF_ROTATE_UPSIDEDOWN);
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_VFLIP:
|
|
rotated = gdk_pixbuf_flip (pixbuf, FALSE);
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_ROT_90_HFLIP: /* flipped diagonally around '\' */
|
|
temp = gdk_pixbuf_rotate_simple (pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE);
|
|
rotated = gdk_pixbuf_flip (temp, TRUE);
|
|
g_object_unref (temp);
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_ROT_90: /* 90 CW */
|
|
rotated = gdk_pixbuf_rotate_simple (pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE);
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_ROT_90_VFLIP: /* flipped diagonally around '/' */
|
|
temp = gdk_pixbuf_rotate_simple (pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE);
|
|
rotated = gdk_pixbuf_flip (temp, FALSE);
|
|
g_object_unref (temp);
|
|
break;
|
|
|
|
case GEXIV2_ORIENTATION_ROT_270: /* 90 CCW */
|
|
rotated = gdk_pixbuf_rotate_simple (pixbuf, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
|
|
break;
|
|
|
|
default: /* shouldn't happen */
|
|
break;
|
|
}
|
|
|
|
return rotated;
|
|
}
|
|
|
|
static void
|
|
gimp_image_metadata_rotate_query (gint32 image_ID,
|
|
const gchar *mime_type,
|
|
GimpMetadata *metadata,
|
|
gboolean interactive)
|
|
{
|
|
GimpParasite *parasite;
|
|
gchar *parasite_name;
|
|
GExiv2Orientation orientation;
|
|
gboolean query = interactive;
|
|
|
|
orientation = gexiv2_metadata_get_orientation (metadata);
|
|
|
|
if (orientation <= GEXIV2_ORIENTATION_NORMAL ||
|
|
orientation > GEXIV2_ORIENTATION_MAX)
|
|
return;
|
|
|
|
parasite_name = g_strdup_printf ("gimp-metadata-exif-rotate(%s)", mime_type);
|
|
|
|
parasite = gimp_get_parasite (parasite_name);
|
|
|
|
if (parasite)
|
|
{
|
|
if (strncmp (gimp_parasite_data (parasite), "yes",
|
|
gimp_parasite_data_size (parasite)) == 0)
|
|
{
|
|
query = FALSE;
|
|
}
|
|
else if (strncmp (gimp_parasite_data (parasite), "no",
|
|
gimp_parasite_data_size (parasite)) == 0)
|
|
{
|
|
gimp_parasite_free (parasite);
|
|
g_free (parasite_name);
|
|
return;
|
|
}
|
|
|
|
gimp_parasite_free (parasite);
|
|
}
|
|
|
|
if (query && ! gimp_image_metadata_rotate_dialog (image_ID,
|
|
orientation,
|
|
parasite_name))
|
|
{
|
|
g_free (parasite_name);
|
|
return;
|
|
}
|
|
|
|
g_free (parasite_name);
|
|
|
|
gimp_image_metadata_rotate (image_ID, orientation);
|
|
|
|
gexiv2_metadata_set_tag_string (metadata,
|
|
"Exif.Image.Orientation", "1");
|
|
gexiv2_metadata_set_tag_string (metadata,
|
|
"Xmp.tiff.Orientation", "1");
|
|
}
|
|
|
|
static gboolean
|
|
gimp_image_metadata_rotate_dialog (gint32 image_ID,
|
|
GExiv2Orientation orientation,
|
|
const gchar *parasite_name)
|
|
{
|
|
GtkWidget *dialog;
|
|
GtkWidget *main_vbox;
|
|
GtkWidget *vbox;
|
|
GtkWidget *label;
|
|
GtkWidget *toggle;
|
|
GdkPixbuf *pixbuf;
|
|
gchar *name;
|
|
gchar *title;
|
|
gint response;
|
|
|
|
name = gimp_image_get_name (image_ID);
|
|
title = g_strdup_printf (_("Rotate %s?"), name);
|
|
g_free (name);
|
|
|
|
dialog = gimp_dialog_new (title, "gimp-metadata-rotate-dialog",
|
|
NULL, 0, NULL, NULL,
|
|
|
|
_("_Keep Original"), GTK_RESPONSE_CANCEL,
|
|
GIMP_STOCK_TOOL_ROTATE, GTK_RESPONSE_OK,
|
|
|
|
NULL);
|
|
|
|
g_free (title);
|
|
|
|
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
|
GTK_RESPONSE_OK,
|
|
GTK_RESPONSE_CANCEL,
|
|
-1);
|
|
|
|
gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
|
|
gimp_window_set_transient (GTK_WINDOW (dialog));
|
|
|
|
main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
|
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
|
|
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
|
main_vbox, FALSE, FALSE, 0);
|
|
gtk_widget_show (main_vbox);
|
|
|
|
#define THUMBNAIL_SIZE 128
|
|
|
|
pixbuf = gimp_image_get_thumbnail (image_ID,
|
|
THUMBNAIL_SIZE, THUMBNAIL_SIZE,
|
|
GIMP_PIXBUF_SMALL_CHECKS);
|
|
|
|
if (pixbuf)
|
|
{
|
|
GdkPixbuf *rotated;
|
|
GtkWidget *hbox;
|
|
GtkWidget *image;
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
|
|
gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_show (hbox);
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
|
|
gtk_widget_show (vbox);
|
|
|
|
label = gtk_label_new (_("Original"));
|
|
gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_MIDDLE);
|
|
gimp_label_set_attributes (GTK_LABEL (label),
|
|
PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
|
|
-1);
|
|
gtk_box_pack_end (GTK_BOX (vbox), label, FALSE, FALSE, 0);
|
|
gtk_widget_show (label);
|
|
|
|
image = gtk_image_new_from_pixbuf (pixbuf);
|
|
gtk_box_pack_end (GTK_BOX (vbox), image, FALSE, FALSE, 0);
|
|
gtk_widget_show (image);
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
|
|
gtk_widget_show (vbox);
|
|
|
|
label = gtk_label_new (_("Rotated"));
|
|
gimp_label_set_attributes (GTK_LABEL (label),
|
|
PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
|
|
-1);
|
|
gtk_box_pack_end (GTK_BOX (vbox), label, FALSE, FALSE, 0);
|
|
gtk_widget_show (label);
|
|
|
|
rotated = gimp_image_metadata_rotate_pixbuf (pixbuf, orientation);
|
|
g_object_unref (pixbuf);
|
|
|
|
image = gtk_image_new_from_pixbuf (rotated);
|
|
g_object_unref (rotated);
|
|
|
|
gtk_box_pack_end (GTK_BOX (vbox), image, FALSE, FALSE, 0);
|
|
gtk_widget_show (image);
|
|
}
|
|
|
|
label = g_object_new (GTK_TYPE_LABEL,
|
|
"label", _("This image contains Exif orientation "
|
|
"metadata."),
|
|
"wrap", TRUE,
|
|
"justify", GTK_JUSTIFY_LEFT,
|
|
"xalign", 0.0,
|
|
"yalign", 0.5,
|
|
NULL);
|
|
gimp_label_set_attributes (GTK_LABEL (label),
|
|
PANGO_ATTR_SCALE, PANGO_SCALE_LARGE,
|
|
PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
|
|
-1);
|
|
/* eek */
|
|
gtk_widget_set_size_request (GTK_WIDGET (label),
|
|
2 * THUMBNAIL_SIZE + 12, -1);
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
|
|
gtk_widget_show (label);
|
|
|
|
label = g_object_new (GTK_TYPE_LABEL,
|
|
"label", _("Would you like to rotate the image?"),
|
|
"wrap", TRUE,
|
|
"justify", GTK_JUSTIFY_LEFT,
|
|
"xalign", 0.0,
|
|
"yalign", 0.5,
|
|
NULL);
|
|
/* eek */
|
|
gtk_widget_set_size_request (GTK_WIDGET (label),
|
|
2 * THUMBNAIL_SIZE + 12, -1);
|
|
gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
|
|
gtk_widget_show (label);
|
|
|
|
toggle = gtk_check_button_new_with_mnemonic (_("_Don't ask me again"));
|
|
gtk_box_pack_end (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0);
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
|
|
gtk_widget_show (toggle);
|
|
|
|
response = gimp_dialog_run (GIMP_DIALOG (dialog));
|
|
|
|
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)))
|
|
{
|
|
GimpParasite *parasite;
|
|
const gchar *str = (response == GTK_RESPONSE_OK) ? "yes" : "no";
|
|
|
|
parasite = gimp_parasite_new (parasite_name,
|
|
GIMP_PARASITE_PERSISTENT,
|
|
strlen (str), str);
|
|
gimp_attach_parasite (parasite);
|
|
gimp_parasite_free (parasite);
|
|
}
|
|
|
|
gtk_widget_destroy (dialog);
|
|
|
|
return (response == GTK_RESPONSE_OK);
|
|
}
|