Commit 66f98bf3 authored by Raphael Quinet's avatar Raphael Quinet Committed by Raphaël Quinet
Browse files

Register plug_in_metadata_import and plug_in_metadata_export in the PDB.

2005-04-11  Raphael Quinet  <raphael@gimp.org>

	* plug-ins/metadata/metadata.c: Register plug_in_metadata_import
	and plug_in_metadata_export in the PDB.

	* plug-ins/metadata/Makefile.am
	* plug-ins/metadata/base64.h
	* plug-ins/metadata/base64.c: Added base64 decoder, needed for
	reading the thumbnail images (base64 encoded inside XML element).

	* plug-ins/metadata/xmp-parse.h
	* plug-ins/metadata/xmp-parse.c: Added XMP_PTYPE_ALT_THUMBS for
	parsing a list of thumbnail images.  Decode and store the base64
	encoded images.

	* plug-ins/metadata/xmpdump.c
	* plug-ins/metadata/xmp-model.h
	* plug-ins/metadata/xmp-model.c: Added support for thumbnails,
	defined global symbols for standard XMP schema URIs.

	* plug-ins/metadata/interface.c: Preliminary support for widget
	cross-references just for testing - it does not save anything yet.
	Added thumbnail tab, although the thumbnail image is not displayed
	yet.
parent 1fe869bb
2005-04-11 Raphaël Quinet <raphael@gimp.org>
* plug-ins/metadata/metadata.c: Register plug_in_metadata_import
and plug_in_metadata_export in the PDB.
* plug-ins/metadata/Makefile.am
* plug-ins/metadata/base64.h
* plug-ins/metadata/base64.c: Added base64 decoder, needed for
reading the thumbnail images (base64 encoded inside XML element).
* plug-ins/metadata/xmp-parse.h
* plug-ins/metadata/xmp-parse.c: Added XMP_PTYPE_ALT_THUMBS for
parsing a list of thumbnail images. Decode and store the base64
encoded images.
* plug-ins/metadata/xmpdump.c
* plug-ins/metadata/xmp-model.h
* plug-ins/metadata/xmp-model.c: Added support for thumbnails,
defined global symbols for standard XMP schema URIs.
* plug-ins/metadata/interface.c: Preliminary support for widget
cross-references just for testing - it does not save anything yet.
Added thumbnail tab, although the thumbnail image is not displayed
yet.
2005-04-11 Michael Natterer <mitch@gimp.org>
* app/widgets/gimpclipboard.c: don't include "core/gimpviewable.h"
......
......@@ -25,7 +25,9 @@ metadata_SOURCES = \
xmp-parse.h \
xmp-parse.c \
xmp-encode.h \
xmp-encode.c
xmp-encode.c \
base64.h \
base64.c
# exif-decode.h \
# exif-decode.c \
# exif-encode.h \
......@@ -37,7 +39,9 @@ noinst_PROGRAMS = xmpdump
xmpdump_SOURCES = \
xmpdump.c \
xmp-parse.h \
xmp-parse.c
xmp-parse.c \
base64.h \
base64.c
INCLUDES = \
-I$(top_srcdir) \
......
/* base64.h - encode and decode base64 encoding according to RFC 1521
*
* Copyright (C) 2005, Raphaël Quinet <raphael@gimp.org>
*
* 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 2 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* Yet another implementation of base64 decoding. I ended up writing this
* because most of the implementations that I found required a null-terminated
* buffer, some of the others did not ignore whitespace (especially those
* written for HTTP usage) and the rest were not compatible with the LGPL. Or
* at least I haven't been able to find LGPL implementations. Writing this
* according to RFC 1521 did not take long anyway.
*/
#ifndef WITHOUT_GIMP
# include "config.h"
# include <string.h>
# include "base64.h"
# include "libgimp/stdplugins-intl.h"
#else
# include <string.h>
# include "base64.h"
# define _(String) (String)
# define N_(String) (String)
#endif
#if HAVE_STDIO_H
#include <stdio.h>
#endif
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
static const gchar base64_code[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const gint base64_6bits[256] =
{
-2, -3, -3, -3, -3, -3, -3, -3, -3, -1, -1, -1, -1, -1, -3, -3,
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
-1, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, 62, -3, -3, -3, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -3, -3, -3, -2, -3, -3,
-3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -3, -3, -3, -3, -3,
-3, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -3, -3, -3, -3, -3,
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3
}; /* -1: skip whitespace, -2: end of input, -3: error */
/*
* FIXME: update this comment...
* - dest_size should be at least 3/4 of strlen (src) minus all CRLF
* - returns the number of bytes stored in dest
*/
gint
base64_decode (const gchar *src_b64,
guint src_size,
gchar *dest,
guint dest_size)
{
gint32 decoded;
gint i;
gint n;
gint bits;
g_return_val_if_fail (src_b64 != NULL, -1);
decoded = 0;
n = 0;
bits = -3;
for (i = 0; src_size && (i + 3 <= dest_size); src_b64++, src_size--)
{
bits = base64_6bits[(int) *src_b64 & 0xff];
if (bits < 0)
{
if (bits < -1)
break;
else
continue;
}
decoded <<= 6;
decoded += bits;
if (++n >= 4)
{
/* we have decoded 4 source chars => 24 bits of output (3 chars) */
dest[i++] = decoded >> 16;
dest[i++] = (decoded >> 8) & 0xff;
dest[i++] = decoded & 0xff;
decoded = 0;
n = 0;
}
}
if (bits < -2)
return -1;
if ((n == 3) && (i + 2 <= dest_size))
{
/* 3 source chars (+ 1 padding "=") => 16 bits of output (2 chars) */
dest[i++] = decoded >> 10;
dest[i++] = (decoded >> 2) & 0xff;
}
else if ((n == 2) && (i + 1 <= dest_size))
{
/* 2 source chars (+ 2 padding "=") => 8 bits of output (1 char) */
dest[i++] = decoded >> 4;
}
if (i < dest_size)
dest[i] = 0;
return i;
}
gint
base64_encode (const gchar *src,
guint src_size,
gchar *dest_b64,
guint dest_size)
{
/* FIXME: just index the base64_code string, 6 bits at a time */
g_warning ("not written yet\n");
return 1;
}
/* base64.h - encode and decode base64 encoding according to RFC 1521
*
* Copyright (C) 2005, Raphaël Quinet <raphael@gimp.org>
*
* 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 2 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef BASE64_H
#define BASE64_H
#include <glib.h>
G_BEGIN_DECLS
gint base64_decode (const gchar *src_b64,
guint src_size,
gchar *dest,
guint dest_size);
gint base64_encode (const gchar *src,
guint src_size,
gchar *dest_b64,
guint dest_size);
G_END_DECLS
#endif /* BASE64_H */
......@@ -188,8 +188,67 @@ update_icons (MetadataGui *mgui)
gtk_tree_model_foreach (model, icon_foreach_func, mgui);
}
/* FIXME: temporary data structure for testing */
typedef struct
{
const gchar *schema;
const gchar *property_name;
GSList *widget_list;
} WidgetXRef;
static void
add_description_tab (GtkWidget *notebook)
entry_changed (GtkEntry *entry,
gpointer user_data)
{
WidgetXRef *xref = user_data;
g_print ("XMP: %s 0x%x 0x%x %s\n", xref->property_name, (int) entry, (int) user_data, gtk_entry_get_text (entry)); /* FIXME */
}
static void
register_entry_xref (GtkWidget *entry,
const gchar *schema,
const gchar *property_name)
{
WidgetXRef *xref;
xref = g_new (WidgetXRef, 1);
xref->schema = schema;
xref->property_name = property_name;
xref->widget_list = g_slist_prepend (NULL, entry);
g_signal_connect (GTK_ENTRY (entry), "changed",
G_CALLBACK (entry_changed), xref);
}
static void
text_changed (GtkTextBuffer *text_buffer,
gpointer user_data)
{
WidgetXRef *xref = user_data;
GtkTextIter start;
GtkTextIter end;
gtk_text_buffer_get_bounds (text_buffer, &start, &end);
g_print ("XMP: %s 0x%x 0x%x %s\n", xref->property_name, (int) text_buffer, (int) user_data, gtk_text_buffer_get_text (text_buffer, &start, &end, FALSE)); /* FIXME */
}
static void
register_text_xref (GtkTextBuffer *text_buffer,
const gchar *schema,
const gchar *property_name)
{
WidgetXRef *xref;
xref = g_new (WidgetXRef, 1);
xref->schema = schema;
xref->property_name = property_name;
xref->widget_list = g_slist_prepend (NULL, text_buffer);
g_signal_connect (GTK_TEXT_BUFFER (text_buffer), "changed",
G_CALLBACK (text_changed), xref);
}
static void
add_description_tab (GtkWidget *notebook)
{
GtkWidget *frame;
GtkWidget *table;
......@@ -211,11 +270,13 @@ add_description_tab (GtkWidget *notebook)
/* gtk_widget_show (table); */
entry = gtk_entry_new ();
register_entry_xref (entry, XMP_SCHEMA_DUBLIN_CORE, "title");
gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
_("Image _Title:"), 0.0, 0.5,
entry, 1, FALSE);
entry = gtk_entry_new ();
register_entry_xref (entry, XMP_SCHEMA_DUBLIN_CORE, "creator");
gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
_("_Author:"), 0.0, 0.5,
entry, 1, FALSE);
......@@ -232,13 +293,15 @@ add_description_tab (GtkWidget *notebook)
"FIXME:\n"
"These widgets are currently disconnected from the XMP model.\n"
"Please use the Advanced tab.",
-1); /*FIXME*/
-1);
register_text_xref (text_buffer, XMP_SCHEMA_DUBLIN_CORE, "description");
gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
_("_Description:"), 0.0, 0.5,
scrolled_window, 1, FALSE);
entry = gtk_entry_new ();
register_entry_xref (entry, XMP_SCHEMA_PHOTOSHOP, "CaptionWriter");
gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
_("Description _Writer:"), 0.0, 0.5,
entry, 1, FALSE);
......@@ -251,6 +314,7 @@ add_description_tab (GtkWidget *notebook)
GTK_POLICY_AUTOMATIC);
text_view = gtk_text_view_new ();
text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
register_text_xref (text_buffer, XMP_SCHEMA_PDF, "Keywords");
gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
_("_Keywords:"), 0.0, 0.5,
......@@ -264,6 +328,7 @@ add_copyright_tab (GtkWidget *notebook)
{
GtkWidget *label;
/* FIXME: add entries, cross-link with XMP model */
label = gtk_label_new (_("Empty"));
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), label,
gtk_label_new (_("Copyright")));
......@@ -275,6 +340,7 @@ add_origin_tab (GtkWidget *notebook)
{
GtkWidget *label;
/* FIXME: add entries, cross-link with XMP model */
label = gtk_label_new (_("Empty"));
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), label,
gtk_label_new (_("Origin")));
......@@ -286,6 +352,7 @@ add_camera1_tab (GtkWidget *notebook)
{
GtkWidget *label;
/* FIXME: add entries, cross-link with XMP model */
label = gtk_label_new (_("Empty"));
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), label,
gtk_label_new (_("Camera 1")));
......@@ -297,12 +364,28 @@ add_camera2_tab (GtkWidget *notebook)
{
GtkWidget *label;
/* FIXME: add entries, cross-link with XMP model */
label = gtk_label_new (_("Empty"));
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), label,
gtk_label_new (_("Camera 2")));
gtk_widget_show (label);
}
static void
add_thumbnail_tab (GtkWidget *notebook)
{
GdkPixbuf *default_thumb;
GtkWidget *image;
/* FIXME: link thumbnail with XMP model */
default_thumb = gtk_widget_render_icon (notebook, GIMP_STOCK_QUESTION,
(GtkIconSize) -1, "thumbnail");
image = gtk_image_new_from_pixbuf (default_thumb);
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), image,
gtk_label_new (_("Thumbnail")));
gtk_widget_show (image);
}
static void
add_advanced_tab (GtkWidget *notebook,
GtkTreeModel *model)
......@@ -625,6 +708,7 @@ metadata_dialog (gint32 image_ID,
add_origin_tab (notebook);
add_camera1_tab (notebook);
add_camera2_tab (notebook);
add_thumbnail_tab (notebook);
add_advanced_tab (notebook, xmp_model_get_tree_model (mgui.xmp_model));
gtk_window_set_default_size (GTK_WINDOW (mgui.dlg), 400, 500);
......
......@@ -34,7 +34,7 @@
/* FIXME: uncomment when these are working
#include "exif-decode.h"
#include "exif-encode.h"
#include "iptc-parse.h"
#include "iptc-decode.h"
*/
#define METADATA_PARASITE "gimp-metadata"
......@@ -77,16 +77,16 @@ query (void)
static GimpParamDef decode_xmp_args[] =
{
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_STRING, "xmp", "XMP packet" },
{ GIMP_PDB_STRING, "xmp", "XMP packet" }
};
static GimpParamDef encode_xmp_args[] =
{
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_IMAGE, "image", "Input image" }
};
static GimpParamDef encode_xmp_return_vals[] =
{
{ GIMP_PDB_STRING, "xmp", "XMP packet" },
{ GIMP_PDB_STRING, "xmp", "XMP packet" }
};
/* FIXME: uncomment when these are working
......@@ -94,17 +94,17 @@ query (void)
{
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_INT32, "exif_size", "size of the EXIF block" },
{ GIMP_PDB_INT8ARRAY, "exif", "EXIF block" },
{ GIMP_PDB_INT8ARRAY, "exif", "EXIF block" }
};
static GimpParamDef encode_exif_args[] =
{
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_IMAGE, "image", "Input image" }
};
static GimpParamDef encode_exif_return_vals[] =
{
{ GIMP_PDB_INT32, "exif_size", "size of the EXIF block" },
{ GIMP_PDB_INT8ARRAY, "exif", "EXIF block" },
{ GIMP_PDB_INT8ARRAY, "exif", "EXIF block" }
};
*/
......@@ -112,13 +112,13 @@ query (void)
{
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_STRING, "schema", "XMP schema prefix or URI" },
{ GIMP_PDB_STRING, "property", "XMP property name" },
{ GIMP_PDB_STRING, "property", "XMP property name" }
};
static GimpParamDef get_return_vals[] =
{
{ GIMP_PDB_INT32, "type", "XMP property type" },
{ GIMP_PDB_INT32, "num_vals", "number of values" },
{ GIMP_PDB_STRINGARRAY, "vals", "XMP property values" },
{ GIMP_PDB_STRINGARRAY, "vals", "XMP property values" }
};
static GimpParamDef set_args[] =
......@@ -128,18 +128,18 @@ query (void)
{ GIMP_PDB_STRING, "property", "XMP property name" },
{ GIMP_PDB_INT32, "type", "XMP property type" },
{ GIMP_PDB_INT32, "num_vals", "number of values" },
{ GIMP_PDB_STRINGARRAY, "vals", "XMP property values" },
{ GIMP_PDB_STRINGARRAY, "vals", "XMP property values" }
};
static GimpParamDef get_simple_args[] =
{
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_STRING, "schema", "XMP schema prefix or URI" },
{ GIMP_PDB_STRING, "property", "XMP property name" },
{ GIMP_PDB_STRING, "property", "XMP property name" }
};
static GimpParamDef get_simple_return_vals[] =
{
{ GIMP_PDB_STRING, "value", "XMP property value" },
{ GIMP_PDB_STRING, "value", "XMP property value" }
};
static GimpParamDef set_simple_args[] =
......@@ -147,7 +147,7 @@ query (void)
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_STRING, "schema", "XMP schema prefix or URI" },
{ GIMP_PDB_STRING, "property", "XMP property name" },
{ GIMP_PDB_STRING, "value", "XMP property value" },
{ GIMP_PDB_STRING, "value", "XMP property value" }
};
/* FIXME: uncomment when these are working
......@@ -155,17 +155,30 @@ query (void)
{
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_STRING, "schema", "XMP schema prefix or URI" },
{ GIMP_PDB_STRING, "property", "XMP property name" },
{ GIMP_PDB_STRING, "property", "XMP property name" }
};
static GimpParamDef add_schema_args[] =
{
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_STRING, "prefix", "XMP schema prefix" },
{ GIMP_PDB_STRING, "uri", "XMP schema URI" },
{ GIMP_PDB_STRING, "uri", "XMP schema URI" }
};
*/
static GimpParamDef import_args[] =
{
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_STRING, "filename", "The name of the XMP file to import" }
};
static GimpParamDef export_args[] =
{
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_STRING, "filename", "The name of the file to save the XMP packet in" },
{ GIMP_PDB_INT32, "overwrite", "Overwrite existing file: { FALSE (0), TRUE (1) }" }
};
gimp_install_procedure ("plug_in_metadata_editor",
"View and edit metadata (EXIF, IPTC, XMP)",
"View and edit metadata information attached to the "
......@@ -309,7 +322,37 @@ query (void)
G_N_ELEMENTS (set_simple_args), 0,
set_simple_args, NULL);
gimp_install_procedure ("plug_in_metadata_import",
"Import XMP from a file into the current image",
"Load an XMP packet from a file and import it into "
"the current image. This can be used to add a "
"license statement or some other predefined "
"metadata to an image",
"Raphaël Quinet <raphael@gimp.org>",
"Raphaël Quinet <raphael@gimp.org>",
"2005",
NULL,
NULL,
GIMP_PLUGIN,
G_N_ELEMENTS (import_args), 0,
import_args, NULL);
gimp_install_procedure ("plug_in_metadata_export",
"Export XMP from the current image to a file",
"Export the metadata associated with the current "
"image into a file. The metadata will be saved as "
"an XMP packet. If overwrite is TRUE, then any "
"existing file will be overwritten without warning. "
"If overwrite is FALSE, then an error will occur if "
"the file already exists.",
"Raphaël Quinet <raphael@gimp.org>",
"Raphaël Quinet <raphael@gimp.org>",
"2005",
NULL,
NULL,
GIMP_PLUGIN,
G_N_ELEMENTS (export_args), 0,
export_args, NULL);
}
static void
......@@ -361,12 +404,14 @@ run (const gchar *name,
gimp_parasite_free (parasite);
}
/* If we have no metadata yet, try to find some XMP in the file (but
* ignore errors if nothing is found). FIXME: This is a workaround
* until all file plug-ins do the right thing when loading their
* files.
/* If we have no metadata yet, try to find an XMP packet in the file
* (but ignore errors if nothing is found).
*
* FIXME: This is a workaround until all file plug-ins do the right
* thing when loading their files.
*/
if (xmp_model_is_empty (xmp_model))
if (xmp_model_is_empty (xmp_model)
&& !! strcmp (name, "plug_in_metadata_decode_xmp"))
{
const gchar *filename;
GError *error = NULL;
......@@ -407,10 +452,12 @@ run (const gchar *name,
else if (! strcmp (name, "plug_in_metadata_get"))
{
g_warning ("Not implemented yet\n"); /* FIXME */
status = GIMP_PDB_EXECUTION_ERROR;
}
else if (! strcmp (name, "plug_in_metadata_set"))
{
g_warning ("Not implemented yet\n"); /* FIXME */
status = GIMP_PDB_EXECUTION_ERROR;
}
else if (! strcmp (name, "plug_in_metadata_get_simple"))
{
......@@ -444,6 +491,34 @@ run (const gchar *name,
property_name, property_value))
status = GIMP_PDB_EXECUTION_ERROR;
}
else if (! strcmp (name, "plug_in_metadata_import"))
{
const gchar *filename;
gchar *buffer;
gssize buffer_length;
GError *error = NULL;
filename = param[1].data.d_string;
if (! g_file_get_contents (filename, &buffer, &buffer_length, &error))
{
g_error_free (error);
status = GIMP_PDB_EXECUTION_ERROR;
}
else if (! xmp_model_parse_buffer (xmp_model, buffer, buffer_length,
TRUE, &error))
{
g_error_free (error);
status = GIMP_PDB_EXECUTION_ERROR;
}