mirror of https://github.com/GNOME/gimp.git
Added placeholder for file properties.
2005-03-04 Raphael Quinet <raphael@gimp.org> * menus/image-menu.xml.in: Added placeholder for file properties. * plug-ins/metadata/.cvsignore * plug-ins/metadata/Makefile.am * plug-ins/metadata/README * plug-ins/metadata/interface.c * plug-ins/metadata/interface.h * plug-ins/metadata/metadata.c * plug-ins/metadata/xmp-model.c * plug-ins/metadata/xmp-model.h * plug-ins/metadata/xmp-parse.c * plug-ins/metadata/xmp-parse.h * plug-ins/metadata/xmp-gen.c * plug-ins/metadata/xmp-gen.h: First import of metadata editor. Currently, it cannot read metadata (except for XMP), it cannot edit metadata and it cannot save metadata (just export). But this might improve later... The README file contains some info.
This commit is contained in:
parent
cc4e204b83
commit
0d58035627
20
ChangeLog
20
ChangeLog
|
@ -1,3 +1,23 @@
|
|||
2005-03-04 Raphaël Quinet <raphael@gimp.org>
|
||||
|
||||
* menus/image-menu.xml.in: Added placeholder for file properties.
|
||||
|
||||
* plug-ins/metadata/.cvsignore
|
||||
* plug-ins/metadata/Makefile.am
|
||||
* plug-ins/metadata/README
|
||||
* plug-ins/metadata/interface.c
|
||||
* plug-ins/metadata/interface.h
|
||||
* plug-ins/metadata/metadata.c
|
||||
* plug-ins/metadata/xmp-model.c
|
||||
* plug-ins/metadata/xmp-model.h
|
||||
* plug-ins/metadata/xmp-parse.c
|
||||
* plug-ins/metadata/xmp-parse.h
|
||||
* plug-ins/metadata/xmp-gen.c
|
||||
* plug-ins/metadata/xmp-gen.h: First import of metadata editor.
|
||||
Currently, it cannot read metadata (except for XMP), it cannot
|
||||
edit metadata and it cannot save metadata (just export). But this
|
||||
might improve later... The README file contains some info.
|
||||
|
||||
2005-03-04 Sven Neumann <sven@gimp.org>
|
||||
|
||||
* app/dialogs/user-install-dialog.c
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
<separator />
|
||||
<placeholder name="Send" />
|
||||
<separator />
|
||||
<placeholder name="Info" />
|
||||
<separator />
|
||||
<menuitem action="view-close" />
|
||||
<menuitem action="file-quit" />
|
||||
</menu>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
Makefile
|
||||
Makefile.in
|
||||
.deps
|
||||
.libs
|
||||
metadata
|
||||
xmpdump
|
|
@ -0,0 +1,56 @@
|
|||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
|
||||
libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
|
||||
libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
|
||||
libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
|
||||
libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
|
||||
|
||||
if OS_WIN32
|
||||
mwindows = -mwindows
|
||||
endif
|
||||
|
||||
AM_LDFLAGS = $(mwindows)
|
||||
|
||||
libexecdir = $(gimpplugindir)/plug-ins
|
||||
|
||||
libexec_PROGRAMS = metadata
|
||||
|
||||
metadata_SOURCES = \
|
||||
metadata.c \
|
||||
interface.h \
|
||||
interface.c \
|
||||
xmp-model.h \
|
||||
xmp-model.c \
|
||||
xmp-parse.h \
|
||||
xmp-parse.c \
|
||||
xmp-gen.h \
|
||||
xmp-gen.c
|
||||
# exif-parse.h \
|
||||
# exif-parse.c \
|
||||
# exif-gen.h \
|
||||
# exif-gen.c \
|
||||
# iptc-parse.h \
|
||||
# iptc-parse.c
|
||||
|
||||
noinst_PROGRAMS = xmpdump
|
||||
xmpdump_SOURCES = \
|
||||
xmpdump.c \
|
||||
xmp-parse.h \
|
||||
xmp-parse.c
|
||||
|
||||
INCLUDES = \
|
||||
-I$(top_srcdir) \
|
||||
$(GTK_CFLAGS) \
|
||||
$(EXIF_CFLAGS) \
|
||||
-I$(includedir)
|
||||
|
||||
LDADD = \
|
||||
$(libgimpui) \
|
||||
$(libgimpwidgets) \
|
||||
$(libgimp) \
|
||||
$(libgimpcolor) \
|
||||
$(libgimpbase) \
|
||||
$(GTK_LIBS) \
|
||||
$(RT_LIBS) \
|
||||
$(INTLLIBS)
|
|
@ -0,0 +1,76 @@
|
|||
What is this?
|
||||
-------------
|
||||
|
||||
This is the beginning of an editor for metadata. It is far from
|
||||
complete yet.
|
||||
|
||||
Current status
|
||||
--------------
|
||||
- It does not read metadata, except if there is an XMP block in the
|
||||
image file.
|
||||
- It does not write metadata, it can only export XMP into a separate
|
||||
file.
|
||||
- It does not edit metadata, except for some very limited editing of
|
||||
basic scalar types (strings, integers, ...). It is currently not
|
||||
possible to add new properties using the editor (but this can be
|
||||
done via the PDB call plug_in_metadata_set_simple()).
|
||||
- Basically, the current code is only useful for viewing some XMP
|
||||
metadata if the image file contains an XMP block. The import/export
|
||||
functions are also supposed to work. Import merges, so it is
|
||||
possible for example to import a license file such as the XMP files
|
||||
from Creative Commons in order to apply a license to an image.
|
||||
- The metadata is saved in a persistent parasite that is overwritten
|
||||
after each call. Caution: the editor should not be open while you
|
||||
are trying to test some of the PDB calls, otherwise you would
|
||||
discard all changes as soon as you press OK. This is a problem that
|
||||
cannot be solved easily as long as the metadata editor is a plug-in.
|
||||
|
||||
How to find test images?
|
||||
------------------------
|
||||
|
||||
The easiest way is to try a Google search for "<?xpacket". This is
|
||||
the marker that is used at the beginning of XMP packets. Many images
|
||||
or PDF files are incorrectly served as text/plain and therefore
|
||||
indexed by Google as text files. By looking for XMP tags such as
|
||||
"<?xpacket", it is possible to find a large number of files containing
|
||||
XMP metadata.
|
||||
|
||||
Work in progress:
|
||||
-----------------
|
||||
- improve XMP code generation (xmp-gen.c) - currently, some structured
|
||||
property types are not written back to the buffer, so the round-trip
|
||||
decode/encode can silently discard some properties
|
||||
- add tabs for easy editing of metadata, connect them to the tree
|
||||
- fix the conversion to/from EXIF (exif-parse.c, exif-gen.c)
|
||||
- register PDB functions correctly (for setting/getting metadata)
|
||||
- update the JPEG plug-in
|
||||
|
||||
Near future:
|
||||
- add custom callbacks for editing in the tree, validate entries, ...
|
||||
- smarter display of rational values, closed sets, ...
|
||||
- improve the layout of the various tabs
|
||||
|
||||
Not-so-near future:
|
||||
- move some of this stuff into the core
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Files modified (2004-08-24):
|
||||
configure.in (added /plug-ins/metadata/Makefile)
|
||||
menus/image-menu.xml.in (added placeholder "<Image>/File/Info")
|
||||
plug-ins/Makefile.am (added "metadata" in SUBDIRS)
|
||||
plug-ins/common/jpeg.c (changed to get/set metadata through PDB)
|
||||
New files:
|
||||
plug-ins/metadata/Makefile.am (generates Makefile.in to get a Makefile)
|
||||
plug-ins/metadata/metadata.c (main part: registers the plug-in, etc.)
|
||||
plug-ins/metadata/interface.h
|
||||
plug-ins/metadata/interface.c (user interface: widgets and callbacks)
|
||||
plug-ins/metadata/xmp-model.h
|
||||
plug-ins/metadata/xmp-model.c (model for the treeview, list of XMP schemas)
|
||||
plug-ins/metadata/xmp-parse.h
|
||||
plug-ins/metadata/xmp-parse.c (simple parser for XMP metadata)
|
||||
plug-ins/metadata/xmp-gen.h
|
||||
plug-ins/metadata/xmp-gen.c (generates XMP metadata from the tree model)
|
||||
plug-ins/metadata/exif-parse.h
|
||||
plug-ins/metadata/exif-parse.c (converts EXIF into the XMP tree model)
|
||||
plug-ins/metadata/exif-gen.h
|
||||
plug-ins/metadata/exif-gen.c (generates EXIF metadata from the tree model)
|
|
@ -0,0 +1,605 @@
|
|||
/* interface.c - user interface for the metadata editor
|
||||
*
|
||||
* Copyright (C) 2004-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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#ifndef _O_BINARY
|
||||
#define _O_BINARY 0
|
||||
#endif
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <libgimp/gimp.h>
|
||||
#include <libgimp/gimpui.h>
|
||||
|
||||
#include "libgimp/stdplugins-intl.h"
|
||||
|
||||
#include "interface.h"
|
||||
#include "xmp-model.h"
|
||||
#include "xmp-gen.h"
|
||||
|
||||
#define RESPONSE_IMPORT 1
|
||||
#define RESPONSE_EXPORT 2
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkWidget *dlg;
|
||||
XMPModel *xmp_model;
|
||||
GdkPixbuf *edit_icon;
|
||||
GdkPixbuf *auto_icon;
|
||||
gboolean run_ok;
|
||||
} MetadataGui;
|
||||
|
||||
static void
|
||||
value_edited (GtkCellRendererText *cell,
|
||||
const gchar *path_string,
|
||||
const gchar *new_text,
|
||||
gpointer data)
|
||||
{
|
||||
GtkTreeModel *model = (GtkTreeModel *)data;
|
||||
GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
|
||||
GtkTreeIter iter;
|
||||
gchar *old_text;
|
||||
|
||||
gtk_tree_model_get_iter (model, &iter, path);
|
||||
gtk_tree_model_get (model, &iter, COL_XMP_VALUE, &old_text, -1);
|
||||
g_free (old_text);
|
||||
|
||||
/* FIXME: update value[] array */
|
||||
/* FIXME: check widget xref and update other widget if not NULL */
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
|
||||
COL_XMP_VALUE, new_text,
|
||||
-1);
|
||||
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
|
||||
static void
|
||||
add_view_columns (GtkTreeView *treeview)
|
||||
{
|
||||
gint col_offset;
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeViewColumn *column;
|
||||
GtkTreeModel *model = gtk_tree_view_get_model (treeview);
|
||||
|
||||
/* Property Name */
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
g_object_set (renderer, "xalign", 0.0, NULL);
|
||||
col_offset =
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
|
||||
-1, _("Property"),
|
||||
renderer,
|
||||
"text",
|
||||
COL_XMP_NAME,
|
||||
"weight",
|
||||
COL_XMP_WEIGHT,
|
||||
"weight-set",
|
||||
COL_XMP_WEIGHT_SET,
|
||||
NULL);
|
||||
column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), col_offset - 1);
|
||||
gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
|
||||
|
||||
/* Icon */
|
||||
renderer = gtk_cell_renderer_pixbuf_new ();
|
||||
col_offset =
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
|
||||
-1, "",
|
||||
renderer,
|
||||
"pixbuf",
|
||||
COL_XMP_EDIT_ICON,
|
||||
"visible",
|
||||
COL_XMP_VISIBLE,
|
||||
NULL);
|
||||
column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), col_offset - 1);
|
||||
gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
|
||||
|
||||
/* Value */
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
g_object_set (renderer, "xalign", 0.0, NULL);
|
||||
|
||||
g_signal_connect (renderer, "edited",
|
||||
G_CALLBACK (value_edited), model);
|
||||
col_offset =
|
||||
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
|
||||
-1, _("Value"),
|
||||
renderer,
|
||||
"text",
|
||||
COL_XMP_VALUE,
|
||||
"editable",
|
||||
COL_XMP_EDITABLE,
|
||||
"visible",
|
||||
COL_XMP_VISIBLE,
|
||||
NULL);
|
||||
column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), col_offset - 1);
|
||||
gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
icon_foreach_func (GtkTreeModel *model,
|
||||
GtkTreePath *path,
|
||||
GtkTreeIter *iter,
|
||||
gpointer user_data)
|
||||
{
|
||||
gboolean editable;
|
||||
MetadataGui *mgui = user_data;
|
||||
|
||||
gtk_tree_model_get (model, iter,
|
||||
COL_XMP_EDITABLE, &editable,
|
||||
-1);
|
||||
if (editable == XMP_AUTO_UPDATE)
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model), iter,
|
||||
COL_XMP_EDIT_ICON, mgui->auto_icon,
|
||||
-1);
|
||||
else if (editable == TRUE)
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model), iter,
|
||||
COL_XMP_EDIT_ICON, mgui->edit_icon,
|
||||
-1);
|
||||
else
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model), iter,
|
||||
COL_XMP_EDIT_ICON, NULL,
|
||||
-1);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
update_icons (MetadataGui *mgui)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
|
||||
/* add the edit icon to the rows that are editable */
|
||||
model = xmp_model_get_tree_model (mgui->xmp_model);
|
||||
gtk_tree_model_foreach (model, icon_foreach_func, mgui);
|
||||
}
|
||||
|
||||
static void
|
||||
add_description_tab (GtkWidget *notebook)
|
||||
{
|
||||
GtkWidget *frame;
|
||||
GtkWidget *table;
|
||||
GtkWidget *entry;
|
||||
GtkWidget *scrolled_window;
|
||||
GtkWidget *text_view;
|
||||
GtkTextBuffer *text_buffer;
|
||||
|
||||
frame = gimp_frame_new (_("Description"));
|
||||
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame,
|
||||
gtk_label_new (_("Description")));
|
||||
gtk_container_set_border_width (GTK_CONTAINER (frame), 10);
|
||||
/* gtk_widget_show (frame); */
|
||||
|
||||
table = gtk_table_new (5, 2, FALSE);
|
||||
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
|
||||
gtk_table_set_row_spacings (GTK_TABLE (table), 6);
|
||||
gtk_container_add (GTK_CONTAINER (frame), table);
|
||||
/* gtk_widget_show (table); */
|
||||
|
||||
entry = gtk_entry_new ();
|
||||
gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
|
||||
_("Image _Title:"), 0.0, 0.5,
|
||||
entry, 1, FALSE);
|
||||
|
||||
entry = gtk_entry_new ();
|
||||
gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
|
||||
_("_Author:"), 0.0, 0.5,
|
||||
entry, 1, FALSE);
|
||||
|
||||
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
|
||||
GTK_SHADOW_IN);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
text_view = gtk_text_view_new ();
|
||||
text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
|
||||
gtk_text_buffer_set_text (text_buffer, "FIXME:\nThese widgets are currently disconnected from the XMP model.\nPlease use the Advanced tab.", -1); /*FIXME*/
|
||||
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 ();
|
||||
gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
|
||||
_("Description _Writer:"), 0.0, 0.5,
|
||||
entry, 1, FALSE);
|
||||
|
||||
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
|
||||
GTK_SHADOW_IN);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
text_view = gtk_text_view_new ();
|
||||
text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
|
||||
gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
|
||||
gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
|
||||
_("_Keywords:"), 0.0, 0.5,
|
||||
scrolled_window, 1, FALSE);
|
||||
|
||||
gtk_widget_show_all (frame);
|
||||
}
|
||||
|
||||
static void
|
||||
add_copyright_tab (GtkWidget *notebook)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (_("Empty"));
|
||||
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), label,
|
||||
gtk_label_new (_("Copyright")));
|
||||
gtk_widget_show (label);
|
||||
}
|
||||
|
||||
static void
|
||||
add_origin_tab (GtkWidget *notebook)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (_("Empty"));
|
||||
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), label,
|
||||
gtk_label_new (_("Origin")));
|
||||
gtk_widget_show (label);
|
||||
}
|
||||
|
||||
static void
|
||||
add_camera1_tab (GtkWidget *notebook)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (_("Empty"));
|
||||
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), label,
|
||||
gtk_label_new (_("Camera 1")));
|
||||
gtk_widget_show (label);
|
||||
}
|
||||
|
||||
static void
|
||||
add_camera2_tab (GtkWidget *notebook)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
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_advanced_tab (GtkWidget *notebook,
|
||||
GtkTreeModel *model)
|
||||
{
|
||||
GtkWidget *sw;
|
||||
GtkWidget *treeview;
|
||||
|
||||
/* Advanced tab */
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
|
||||
GTK_SHADOW_ETCHED_IN);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
gtk_notebook_append_page (GTK_NOTEBOOK (notebook), sw,
|
||||
gtk_label_new (_("Advanced")));
|
||||
|
||||
/* create tree view - model will be unref'ed in xmp_model_free() */
|
||||
treeview = gtk_tree_view_new_with_model (model);
|
||||
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
|
||||
|
||||
add_view_columns (GTK_TREE_VIEW (treeview));
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (sw), treeview);
|
||||
|
||||
/* expand all rows after the treeview widget has been realized */
|
||||
g_signal_connect (treeview, "realize",
|
||||
G_CALLBACK (gtk_tree_view_expand_all), NULL);
|
||||
gtk_widget_show (treeview);
|
||||
gtk_widget_show (sw);
|
||||
}
|
||||
|
||||
/* show a transient message dialog */
|
||||
static void
|
||||
metadata_message_dialog (GtkMessageType type,
|
||||
GtkWindow *parent,
|
||||
const gchar *title,
|
||||
const gchar *message)
|
||||
{
|
||||
GtkWidget *dlg;
|
||||
|
||||
dlg = gtk_message_dialog_new (parent, 0, type, GTK_BUTTONS_OK, message);
|
||||
|
||||
if (title)
|
||||
gtk_window_set_title (GTK_WINDOW (dlg), title);
|
||||
|
||||
gtk_window_set_role (GTK_WINDOW (dlg), "metadata-message");
|
||||
gtk_dialog_run (GTK_DIALOG (dlg));
|
||||
gtk_widget_destroy (dlg);
|
||||
}
|
||||
|
||||
/* load XMP metadata from a file (the file may contain other data) */
|
||||
static void
|
||||
import_dialog_response (GtkWidget *dlg,
|
||||
gint response_id,
|
||||
gpointer data)
|
||||
{
|
||||
MetadataGui *mgui = data;
|
||||
|
||||
g_return_if_fail (mgui != NULL);
|
||||
if (response_id == GTK_RESPONSE_OK)
|
||||
{
|
||||
gchar *filename;
|
||||
gchar *buffer;
|
||||
guint buffer_length;
|
||||
GError *error = NULL;
|
||||
|
||||
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
|
||||
|
||||
if (! g_file_get_contents (filename, &buffer, &buffer_length, &error))
|
||||
{
|
||||
metadata_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dlg),
|
||||
_("Open failed"), error->message);
|
||||
g_error_free (error);
|
||||
g_free (filename);
|
||||
return;
|
||||
}
|
||||
|
||||
if (! xmp_model_parse_buffer (mgui->xmp_model, buffer, buffer_length,
|
||||
TRUE, &error))
|
||||
{
|
||||
metadata_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dlg),
|
||||
_("Open failed"), error->message);
|
||||
g_error_free (error);
|
||||
g_free (filename);
|
||||
return;
|
||||
}
|
||||
|
||||
update_icons (mgui);
|
||||
|
||||
g_free (buffer);
|
||||
g_free (filename);
|
||||
}
|
||||
|
||||
gtk_widget_destroy (dlg); /* FIXME: destroy or unmap? */
|
||||
}
|
||||
|
||||
/* select file to import */
|
||||
static void
|
||||
file_import_dialog (GtkWidget *parent,
|
||||
MetadataGui *mgui)
|
||||
{
|
||||
static GtkWidget *dlg = NULL;
|
||||
|
||||
if (! dlg)
|
||||
{
|
||||
dlg =
|
||||
gtk_file_chooser_dialog_new (_("Import XMP from file"),
|
||||
GTK_WINDOW (parent),
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_OPEN, GTK_RESPONSE_OK,
|
||||
|
||||
NULL);
|
||||
|
||||
/* FIXME: gimp_help_connect? */
|
||||
gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK);
|
||||
|
||||
g_signal_connect (dlg, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed),
|
||||
&dlg);
|
||||
g_signal_connect (dlg, "response",
|
||||
G_CALLBACK (import_dialog_response),
|
||||
mgui);
|
||||
}
|
||||
gtk_window_present (GTK_WINDOW (dlg));
|
||||
}
|
||||
|
||||
/* save XMP metadata to a file (only XMP, nothing else) */
|
||||
static void
|
||||
export_dialog_response (GtkWidget *dlg,
|
||||
gint response_id,
|
||||
gpointer data)
|
||||
{
|
||||
MetadataGui *mgui = data;
|
||||
|
||||
g_return_if_fail (mgui != NULL);
|
||||
if (response_id == GTK_RESPONSE_OK)
|
||||
{
|
||||
gchar *filename;
|
||||
gchar *buffer;
|
||||
guint buffer_length;
|
||||
int fd;
|
||||
|
||||
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
|
||||
|
||||
/* FIXME: improve this code and rewrite the error handling */
|
||||
buffer_length = xmp_estimate_size (mgui->xmp_model);
|
||||
buffer = g_new (gchar, buffer_length);
|
||||
xmp_generate_block (mgui->xmp_model, buffer, buffer_length);
|
||||
fd = g_open (filename, O_CREAT | O_TRUNC | O_WRONLY | _O_BINARY, 0666);
|
||||
if (fd < 0)
|
||||
{
|
||||
metadata_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dlg),
|
||||
_("Open failed"),
|
||||
_("Cannot create file"));
|
||||
g_free (buffer);
|
||||
g_free (filename);
|
||||
return;
|
||||
}
|
||||
fprintf (stderr, "\nwriting %d bytes to %s...\n", strlen(buffer), filename);
|
||||
if (write (fd, buffer, strlen (buffer)) < 0)
|
||||
{
|
||||
metadata_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dlg),
|
||||
_("Save failed"),
|
||||
_("Some error occured while saving"));
|
||||
g_free (buffer);
|
||||
g_free (filename);
|
||||
return;
|
||||
}
|
||||
if (close (fd) < 0)
|
||||
{
|
||||
metadata_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dlg),
|
||||
_("Save failed"),
|
||||
_("Could not close the file"));
|
||||
g_free (buffer);
|
||||
g_free (filename);
|
||||
return;
|
||||
}
|
||||
g_free (buffer);
|
||||
g_free (filename);
|
||||
}
|
||||
|
||||
gtk_widget_destroy (dlg); /* FIXME: destroy or unmap? */
|
||||
}
|
||||
|
||||
/* select file to export */
|
||||
static void
|
||||
file_export_dialog (GtkWidget *parent,
|
||||
MetadataGui *mgui)
|
||||
{
|
||||
static GtkWidget *dlg = NULL;
|
||||
|
||||
if (! dlg)
|
||||
{
|
||||
dlg =
|
||||
gtk_file_chooser_dialog_new (_("Export XMP to file"),
|
||||
GTK_WINDOW (parent),
|
||||
GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_OPEN, GTK_RESPONSE_OK,
|
||||
|
||||
NULL);
|
||||
|
||||
/* FIXME: gimp_help_connect? */
|
||||
gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_OK);
|
||||
|
||||
g_signal_connect (dlg, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed),
|
||||
&dlg);
|
||||
g_signal_connect (dlg, "response",
|
||||
G_CALLBACK (export_dialog_response),
|
||||
mgui);
|
||||
}
|
||||
gtk_window_present (GTK_WINDOW (dlg));
|
||||
}
|
||||
|
||||
static void
|
||||
metadata_dialog_response (GtkWidget *widget,
|
||||
gint response_id,
|
||||
gpointer data)
|
||||
{
|
||||
MetadataGui *mgui = data;
|
||||
|
||||
g_return_if_fail (mgui != NULL);
|
||||
switch (response_id)
|
||||
{
|
||||
case RESPONSE_IMPORT:
|
||||
file_import_dialog (widget, mgui);
|
||||
break;
|
||||
|
||||
case RESPONSE_EXPORT:
|
||||
file_export_dialog (widget, mgui);
|
||||
break;
|
||||
|
||||
case GTK_RESPONSE_OK:
|
||||
mgui->run_ok = TRUE;
|
||||
/*fallthrough*/
|
||||
|
||||
default:
|
||||
gtk_widget_destroy (widget);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
metadata_dialog (gint32 image_ID,
|
||||
XMPModel *xmp_model)
|
||||
{
|
||||
MetadataGui mgui;
|
||||
GtkWidget *notebook;
|
||||
|
||||
gimp_ui_init ("metadata", FALSE);
|
||||
mgui.dlg = gimp_dialog_new (_("Image Properties"), "metadata",
|
||||
NULL, 0,
|
||||
gimp_standard_help_func, "plug-in-metadata",
|
||||
|
||||
_("_Import XMP"), RESPONSE_IMPORT,
|
||||
_("_Export XMP"), RESPONSE_EXPORT,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_OK, GTK_RESPONSE_OK,
|
||||
|
||||
NULL);
|
||||
|
||||
g_signal_connect (mgui.dlg, "response",
|
||||
G_CALLBACK (metadata_dialog_response),
|
||||
&mgui);
|
||||
g_signal_connect (mgui.dlg, "destroy",
|
||||
G_CALLBACK (gtk_main_quit),
|
||||
NULL);
|
||||
|
||||
notebook = gtk_notebook_new ();
|
||||
gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
|
||||
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (mgui.dlg)->vbox), notebook,
|
||||
TRUE, TRUE, 0);
|
||||
gtk_widget_show (notebook);
|
||||
|
||||
mgui.xmp_model = xmp_model;
|
||||
mgui.edit_icon = gtk_widget_render_icon (mgui.dlg, GIMP_STOCK_EDIT,
|
||||
GTK_ICON_SIZE_MENU, NULL);
|
||||
mgui.auto_icon = gtk_widget_render_icon (mgui.dlg, GIMP_STOCK_WILBER,
|
||||
GTK_ICON_SIZE_MENU, NULL);
|
||||
update_icons (&mgui);
|
||||
mgui.run_ok = FALSE;
|
||||
|
||||
/* add the tabs to the notebook */
|
||||
add_description_tab (notebook);
|
||||
add_copyright_tab (notebook);
|
||||
add_origin_tab (notebook);
|
||||
add_camera1_tab (notebook);
|
||||
add_camera2_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);
|
||||
gtk_widget_show (mgui.dlg);
|
||||
|
||||
/* run, baby, run! */
|
||||
gtk_main ();
|
||||
|
||||
/* clean up and return */
|
||||
g_object_unref (mgui.auto_icon);
|
||||
g_object_unref (mgui.edit_icon);
|
||||
return mgui.run_ok;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* interface.h - user interface for the metadata editor
|
||||
*
|
||||
* Copyright (C) 2004-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 INTERFACE_H
|
||||
#define INTERFACE_H
|
||||
|
||||
#include <glib.h>
|
||||
#include "xmp-model.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gboolean metadata_dialog (gint32 image_ID,
|
||||
XMPModel *xmp_model);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* INTERFACE_H */
|
|
@ -0,0 +1,481 @@
|
|||
/* metadata.c - main() for the metadata editor
|
||||
*
|
||||
* Copyright (C) 2004-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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <libgimp/gimp.h>
|
||||
#include <libgimp/gimpui.h>
|
||||
|
||||
#include "libgimp/stdplugins-intl.h"
|
||||
|
||||
#include "interface.h"
|
||||
#include "xmp-gen.h"
|
||||
/* FIXME: uncomment when these are working
|
||||
#include "exif-parse.h"
|
||||
#include "exif-gen.h"
|
||||
#include "iptc-parse.h"
|
||||
*/
|
||||
|
||||
#define METADATA_PARASITE "gimp-metadata"
|
||||
#define METADATA_MARKER "GIMP_XMP_1"
|
||||
#define METADATA_MARKER_LEN (sizeof (METADATA_MARKER) - 1)
|
||||
|
||||
#define HELP_ID "plug-in-metadata"
|
||||
|
||||
|
||||
/* prototypes of local functions */
|
||||
static void query (void);
|
||||
static void run (const gchar *name,
|
||||
gint nparams,
|
||||
const GimpParam *param,
|
||||
gint *nreturn_vals,
|
||||
GimpParam **return_vals);
|
||||
|
||||
/* local variables */
|
||||
GimpPlugInInfo PLUG_IN_INFO =
|
||||
{
|
||||
NULL, /* init_proc */
|
||||
NULL, /* quit_proc */
|
||||
query, /* query_proc */
|
||||
run, /* run_proc */
|
||||
};
|
||||
|
||||
/* local functions */
|
||||
MAIN ()
|
||||
|
||||
static void
|
||||
query (void)
|
||||
{
|
||||
static GimpParamDef editor_args[] =
|
||||
{
|
||||
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
|
||||
{ GIMP_PDB_IMAGE, "image", "Input image" },
|
||||
{ GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)" }
|
||||
};
|
||||
|
||||
static GimpParamDef decode_xmp_args[] =
|
||||
{
|
||||
{ GIMP_PDB_IMAGE, "image", "Input image" },
|
||||
{ GIMP_PDB_STRING, "xmp", "XMP packet" },
|
||||
};
|
||||
|
||||
static GimpParamDef encode_xmp_args[] =
|
||||
{
|
||||
{ GIMP_PDB_IMAGE, "image", "Input image" },
|
||||
};
|
||||
static GimpParamDef encode_xmp_return_vals[] =
|
||||
{
|
||||
{ GIMP_PDB_STRING, "xmp", "XMP packet" },
|
||||
};
|
||||
|
||||
/* FIXME: uncomment when these are working
|
||||
static GimpParamDef decode_exif_args[] =
|
||||
{
|
||||
{ GIMP_PDB_IMAGE, "image", "Input image" },
|
||||
{ GIMP_PDB_INT32, "exif_size", "size of the EXIF block" },
|
||||
{ GIMP_PDB_INT8ARRAY, "exif", "EXIF block" },
|
||||
};
|
||||
|
||||
static GimpParamDef encode_exif_args[] =
|
||||
{
|
||||
{ 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" },
|
||||
};
|
||||
*/
|
||||
|
||||
static GimpParamDef get_args[] =
|
||||
{
|
||||
{ GIMP_PDB_IMAGE, "image", "Input image" },
|
||||
{ GIMP_PDB_STRING, "schema", "XMP schema prefix or URI" },
|
||||
{ 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" },
|
||||
};
|
||||
|
||||
static GimpParamDef set_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_INT32, "type", "XMP property type" },
|
||||
{ GIMP_PDB_INT32, "num_vals", "number of 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" },
|
||||
};
|
||||
static GimpParamDef get_simple_return_vals[] =
|
||||
{
|
||||
{ GIMP_PDB_STRING, "value", "XMP property value" },
|
||||
};
|
||||
|
||||
static GimpParamDef set_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, "value", "XMP property value" },
|
||||
};
|
||||
|
||||
/* FIXME: uncomment when these are working
|
||||
static GimpParamDef delete_args[] =
|
||||
{
|
||||
{ GIMP_PDB_IMAGE, "image", "Input image" },
|
||||
{ GIMP_PDB_STRING, "schema", "XMP schema prefix or URI" },
|
||||
{ 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_install_procedure ("plug_in_metadata_editor",
|
||||
"View and edit metadata (EXIF, IPTC, XMP)",
|
||||
"View and edit metadata information attached to the "
|
||||
"current image. This can include EXIF, IPTC and/or "
|
||||
"XMP information. Some or all of this metadata "
|
||||
"will be saved in the file, depending on the output "
|
||||
"file format.",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"2004-2005",
|
||||
N_("Propert_ies"),
|
||||
"RGB*, INDEXED*, GRAY*",
|
||||
GIMP_PLUGIN,
|
||||
G_N_ELEMENTS (editor_args), 0,
|
||||
editor_args, NULL);
|
||||
gimp_plugin_menu_register ("plug_in_metadata_editor",
|
||||
N_("<Image>/File/Info"));
|
||||
gimp_plugin_icon_register ("plug_in_metadata_editor",
|
||||
GIMP_ICON_TYPE_STOCK_ID, GTK_STOCK_PROPERTIES);
|
||||
/* FIXME: The GNOME HIG recommends using the accel Alt+Return for this */
|
||||
|
||||
gimp_install_procedure ("plug_in_metadata_decode_xmp",
|
||||
"Decode an XMP packet",
|
||||
"Parse an XMP packet and merge the results with "
|
||||
"any metadata already attached to the image. This "
|
||||
"should be used when an XMP packet is read from an "
|
||||
"image file.",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"2005",
|
||||
NULL,
|
||||
NULL,
|
||||
GIMP_PLUGIN,
|
||||
G_N_ELEMENTS (decode_xmp_args), 0,
|
||||
decode_xmp_args, NULL);
|
||||
|
||||
gimp_install_procedure ("plug_in_metadata_encode_xmp",
|
||||
"Encode metadata into an XMP packet",
|
||||
"Generate an XMP packet from the metadata "
|
||||
"information attached to the image. The new XMP "
|
||||
"packet can then be saved into a file.",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"2005",
|
||||
NULL,
|
||||
NULL,
|
||||
GIMP_PLUGIN,
|
||||
G_N_ELEMENTS (encode_xmp_args),
|
||||
G_N_ELEMENTS (encode_xmp_return_vals),
|
||||
encode_xmp_args, encode_xmp_return_vals);
|
||||
|
||||
/* FIXME: uncomment when these are working
|
||||
gimp_install_procedure ("plug_in_metadata_decode_exif",
|
||||
"Decode an EXIF block",
|
||||
"Parse an EXIF block and merge the results with "
|
||||
"any metadata already attached to the image. This "
|
||||
"should be used when an EXIF block is read from an "
|
||||
"image file.",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"2005",
|
||||
NULL,
|
||||
NULL,
|
||||
GIMP_PLUGIN,
|
||||
G_N_ELEMENTS (decode_exif_args), 0,
|
||||
decode_exif_args, NULL);
|
||||
|
||||
gimp_install_procedure ("plug_in_metadata_encode_exif",
|
||||
"Encode metadata into an EXIF block",
|
||||
"Generate an EXIF block from the metadata "
|
||||
"information attached to the image. The new EXIF "
|
||||
"block can then be saved into a file.",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"2005",
|
||||
NULL,
|
||||
NULL,
|
||||
GIMP_PLUGIN,
|
||||
G_N_ELEMENTS (encode_exif_args),
|
||||
G_N_ELEMENTS (encode_exif_return_vals),
|
||||
encode_exif_args, encode_exif_return_vals);
|
||||
*/
|
||||
|
||||
gimp_install_procedure ("plug_in_metadata_get",
|
||||
"Retrieve the values of an XMP property",
|
||||
"Retrieve the list of values associated with "
|
||||
"an XMP property.",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"2005",
|
||||
NULL,
|
||||
NULL,
|
||||
GIMP_PLUGIN,
|
||||
G_N_ELEMENTS (get_args),
|
||||
G_N_ELEMENTS (get_return_vals),
|
||||
get_args, get_return_vals);
|
||||
|
||||
gimp_install_procedure ("plug_in_metadata_set",
|
||||
"Set the values of an XMP property",
|
||||
"Set the list of values associated with "
|
||||
"an XMP property. If a property with the same "
|
||||
"name already exists, it will be replaced.",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"2005",
|
||||
NULL,
|
||||
NULL,
|
||||
GIMP_PLUGIN,
|
||||
G_N_ELEMENTS (set_args), 0,
|
||||
set_args, NULL);
|
||||
|
||||
gimp_install_procedure ("plug_in_metadata_get_simple",
|
||||
"Retrieve the value of an XMP property",
|
||||
"Retrieve value associated with a scalar XMP "
|
||||
"property. This can only be done for simple "
|
||||
"property types such as text or integers. "
|
||||
"Structured types must be retrieved with "
|
||||
"plug_in_metadata_get().",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"2005",
|
||||
NULL,
|
||||
NULL,
|
||||
GIMP_PLUGIN,
|
||||
G_N_ELEMENTS (get_simple_args),
|
||||
G_N_ELEMENTS (get_simple_return_vals),
|
||||
get_simple_args, get_simple_return_vals);
|
||||
|
||||
gimp_install_procedure ("plug_in_metadata_set_simple",
|
||||
"Set the value of an XMP property",
|
||||
"Set the value of a scalar XMP property. This "
|
||||
"can only be done for simple property types such "
|
||||
"as text or integers. Structured types need to "
|
||||
"be set with plug_in_metadata_set().",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"Raphaël Quinet <raphael@gimp.org>",
|
||||
"2005",
|
||||
NULL,
|
||||
NULL,
|
||||
GIMP_PLUGIN,
|
||||
G_N_ELEMENTS (set_simple_args), 0,
|
||||
set_simple_args, NULL);
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
run (const gchar *name,
|
||||
gint nparams,
|
||||
const GimpParam *param,
|
||||
gint *nreturn_vals,
|
||||
GimpParam **return_vals)
|
||||
{
|
||||
static GimpParam values[4];
|
||||
gint32 image_ID;
|
||||
XMPModel *xmp_model;
|
||||
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
|
||||
GimpParasite *parasite = NULL;
|
||||
|
||||
*nreturn_vals = 1;
|
||||
*return_vals = values;
|
||||
|
||||
values[0].type = GIMP_PDB_STATUS;
|
||||
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
|
||||
|
||||
INIT_I18N();
|
||||
|
||||
if (! strcmp (name, "plug_in_metadata_editor"))
|
||||
image_ID = param[1].data.d_image;
|
||||
else
|
||||
image_ID = param[0].data.d_image;
|
||||
|
||||
xmp_model = xmp_model_new ();
|
||||
|
||||
/* if there is already a metadata parasite, load it */
|
||||
parasite = gimp_image_parasite_find (image_ID, METADATA_PARASITE);
|
||||
if (parasite)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!! strncmp (gimp_parasite_data (parasite),
|
||||
METADATA_MARKER, METADATA_MARKER_LEN)
|
||||
|| ! xmp_model_parse_buffer (xmp_model,
|
||||
gimp_parasite_data (parasite)
|
||||
+ METADATA_MARKER_LEN,
|
||||
gimp_parasite_data_size (parasite)
|
||||
- METADATA_MARKER_LEN,
|
||||
FALSE, &error))
|
||||
{
|
||||
g_warning ("Metadata parasite seems to be corrupt");
|
||||
/* continue anyway, we will attach a clean parasite later */
|
||||
}
|
||||
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 (xmp_model_is_empty (xmp_model))
|
||||
{
|
||||
const gchar *filename;
|
||||
GError *error = NULL;
|
||||
|
||||
filename = gimp_image_get_filename (image_ID);
|
||||
if (filename != NULL)
|
||||
if (xmp_model_parse_file (xmp_model, filename, &error))
|
||||
/* g_message ("XMP loaded from file '%s'\n", filename) */;
|
||||
}
|
||||
|
||||
/* Now check what we are supposed to do */
|
||||
if (! strcmp (name, "plug_in_metadata_editor"))
|
||||
{
|
||||
GimpRunMode run_mode;
|
||||
|
||||
run_mode = param[0].data.d_int32;
|
||||
if (run_mode == GIMP_RUN_INTERACTIVE)
|
||||
{
|
||||
/* Hello, user! */
|
||||
if (! metadata_dialog (image_ID, xmp_model))
|
||||
status = GIMP_PDB_CANCEL;
|
||||
}
|
||||
}
|
||||
else if (! strcmp (name, "plug_in_metadata_decode_xmp"))
|
||||
{
|
||||
const gchar *buffer;
|
||||
GError *error = NULL;
|
||||
|
||||
buffer = param[1].data.d_string;
|
||||
if (! xmp_model_parse_buffer (xmp_model, buffer, strlen (buffer),
|
||||
FALSE, &error))
|
||||
status = GIMP_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
else if (! strcmp (name, "plug_in_metadata_encode_xmp"))
|
||||
{
|
||||
/* done below together with the parasite */
|
||||
}
|
||||
else if (! strcmp (name, "plug_in_metadata_get"))
|
||||
{
|
||||
g_warning ("Not implemented yet\n"); /* FIXME */
|
||||
}
|
||||
else if (! strcmp (name, "plug_in_metadata_set"))
|
||||
{
|
||||
g_warning ("Not implemented yet\n"); /* FIXME */
|
||||
}
|
||||
else if (! strcmp (name, "plug_in_metadata_get_simple"))
|
||||
{
|
||||
const gchar *schema_name;
|
||||
const gchar *property_name;
|
||||
const gchar *value;
|
||||
|
||||
schema_name = param[1].data.d_string;
|
||||
property_name = param[2].data.d_string;
|
||||
value = xmp_model_get_scalar_property (xmp_model, schema_name,
|
||||
property_name);
|
||||
if (value)
|
||||
{
|
||||
*nreturn_vals = 2;
|
||||
values[1].type = GIMP_PDB_STRING;
|
||||
values[1].data.d_string = g_strdup (value);
|
||||
}
|
||||
else
|
||||
status = GIMP_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
else if (! strcmp (name, "plug_in_metadata_set_simple"))
|
||||
{
|
||||
const gchar *schema_name;
|
||||
const gchar *property_name;
|
||||
const gchar *property_value;
|
||||
|
||||
schema_name = param[1].data.d_string;
|
||||
property_name = param[2].data.d_string;
|
||||
property_value = param[3].data.d_string;
|
||||
if (! xmp_model_set_scalar_property (xmp_model, schema_name,
|
||||
property_name, property_value))
|
||||
status = GIMP_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = GIMP_PDB_CALLING_ERROR;
|
||||
}
|
||||
|
||||
if (status == GIMP_PDB_SUCCESS)
|
||||
{
|
||||
gchar *buffer;
|
||||
gssize buffer_size;
|
||||
gssize used_size;
|
||||
|
||||
/* Generate the updated parasite and attach it to the image */
|
||||
buffer_size = xmp_estimate_size (xmp_model);
|
||||
buffer = g_new (gchar, buffer_size + METADATA_MARKER_LEN);
|
||||
strcpy (buffer, METADATA_MARKER);
|
||||
used_size = xmp_generate_block (xmp_model,
|
||||
buffer + METADATA_MARKER_LEN,
|
||||
buffer_size);
|
||||
parasite = gimp_parasite_new (METADATA_PARASITE,
|
||||
GIMP_PARASITE_PERSISTENT,
|
||||
used_size + METADATA_MARKER_LEN,
|
||||
(gpointer) buffer);
|
||||
gimp_image_parasite_attach (image_ID, parasite);
|
||||
if (! strcmp (name, "plug_in_metadata_encode_xmp"))
|
||||
{
|
||||
*nreturn_vals = 2;
|
||||
values[1].type = GIMP_PDB_STRING;
|
||||
values[1].data.d_string = g_strdup (buffer + METADATA_MARKER_LEN);
|
||||
}
|
||||
g_free (buffer);
|
||||
xmp_model_free (xmp_model);
|
||||
}
|
||||
|
||||
values[0].data.d_status = status;
|
||||
}
|
|
@ -0,0 +1,331 @@
|
|||
/* xmp-gen.c - generate XMP metadata from the tree model
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <libgimp/gimp.h>
|
||||
#include <libgimp/gimpui.h>
|
||||
|
||||
#include "libgimp/stdplugins-intl.h"
|
||||
|
||||
#include "xmp-gen.h"
|
||||
#include "xmp-model.h"
|
||||
|
||||
static gssize
|
||||
size_schema (GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
const XMPSchema **schema_r)
|
||||
{
|
||||
gtk_tree_model_get (model, iter,
|
||||
COL_XMP_TYPE_XREF, schema_r,
|
||||
-1);
|
||||
return (sizeof (" <rdf:Description xmlns:%s='%s'>\n") - 5
|
||||
+ strlen ((*schema_r)->prefix)
|
||||
+ strlen ((*schema_r)->uri)
|
||||
+ sizeof (" </rdf:Description>\n\n") - 1);
|
||||
}
|
||||
|
||||
static gssize
|
||||
size_property (GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
const XMPSchema *schema)
|
||||
{
|
||||
const XMPProperty *property;
|
||||
const gchar **value_array;
|
||||
gssize length;
|
||||
gint i;
|
||||
|
||||
gtk_tree_model_get (model, iter,
|
||||
COL_XMP_TYPE_XREF, &property,
|
||||
COL_XMP_VALUE_RAW, &value_array,
|
||||
-1);
|
||||
|
||||
switch (property->type)
|
||||
{
|
||||
case XMP_TYPE_BOOLEAN:
|
||||
case XMP_TYPE_DATE:
|
||||
case XMP_TYPE_INTEGER:
|
||||
case XMP_TYPE_REAL:
|
||||
case XMP_TYPE_MIME_TYPE:
|
||||
case XMP_TYPE_TEXT:
|
||||
case XMP_TYPE_RATIONAL:
|
||||
return (sizeof (" <%s:%s>%s</%s:%s>\n") - 11
|
||||
+ 2 * strlen (schema->prefix)
|
||||
+ 2 * strlen (property->name)
|
||||
+ strlen (value_array[0]));
|
||||
|
||||
case XMP_TYPE_LOCALE_BAG:
|
||||
case XMP_TYPE_TEXT_BAG:
|
||||
case XMP_TYPE_XPATH_BAG:
|
||||
case XMP_TYPE_JOB_BAG:
|
||||
case XMP_TYPE_INTEGER_SEQ:
|
||||
case XMP_TYPE_TEXT_SEQ:
|
||||
case XMP_TYPE_RESOURCE_EVENT_SEQ:
|
||||
case XMP_TYPE_RATIONAL_SEQ:
|
||||
length = (sizeof (" <%s:%s>\n <rdf:Bag>\n") - 5
|
||||
+ sizeof (" </rdf:Bag>\n </%s:%s>\n") - 5
|
||||
+ 2 * strlen (schema->prefix)
|
||||
+ 2 * strlen (property->name));
|
||||
for (i = 0; value_array[i] != NULL; i++)
|
||||
length += (sizeof (" <rdf:li>%s</rdf:li>\n") - 3
|
||||
+ strlen (value_array[i]));
|
||||
return length;
|
||||
|
||||
case XMP_TYPE_LANG_ALT:
|
||||
length = (sizeof (" <%s:%s>\n <rdf:Alt>\n") - 5
|
||||
+ sizeof (" </rdf:Alt>\n </%s:%s>\n") - 5
|
||||
+ 2 * strlen (schema->prefix)
|
||||
+ 2 * strlen (property->name));
|
||||
for (i = 0; value_array[i] != NULL; i += 2)
|
||||
length += (sizeof (" <rdf:li xml:lang='%s'>%s</rdf:li>\n") - 5
|
||||
+ strlen (value_array[i])
|
||||
+ strlen (value_array[i + 1]));
|
||||
return length;
|
||||
|
||||
case XMP_TYPE_URI:
|
||||
return (sizeof (" <%s:%s rdf:resource='%s' />\n") - 7
|
||||
+ strlen (schema->prefix)
|
||||
+ strlen (property->name)
|
||||
+ strlen (value_array[0]));
|
||||
|
||||
case XMP_TYPE_RESOURCE_REF:
|
||||
case XMP_TYPE_DIMENSIONS:
|
||||
case XMP_TYPE_THUMBNAIL_ALT:
|
||||
case XMP_TYPE_GPS_COORDINATE:
|
||||
case XMP_TYPE_FLASH:
|
||||
case XMP_TYPE_OECF_SFR:
|
||||
case XMP_TYPE_CFA_PATTERN:
|
||||
case XMP_TYPE_DEVICE_SETTINGS:
|
||||
return 100; /* FIXME */
|
||||
|
||||
case XMP_TYPE_UNKNOWN:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xmp_estimate_size:
|
||||
* @xmp_model: An #XMPModel
|
||||
*
|
||||
* Return value: estimated size (upper bound) of the XMP (RDF) encoding of
|
||||
* the given model.
|
||||
**/
|
||||
gssize
|
||||
xmp_estimate_size (XMPModel *xmp_model)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeIter child;
|
||||
gssize buffer_size;
|
||||
const XMPSchema *schema;
|
||||
|
||||
model = xmp_model_get_tree_model (xmp_model);
|
||||
buffer_size = 158 + 44 + 1;
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
buffer_size += size_schema (model, &iter, &schema);
|
||||
if (gtk_tree_model_iter_children (model, &child, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
buffer_size += size_property (model, &child, schema);
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &child));
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
static gint
|
||||
gen_schema_start (GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gchar *buffer,
|
||||
const XMPSchema **schema_r)
|
||||
{
|
||||
gtk_tree_model_get (model, iter,
|
||||
COL_XMP_TYPE_XREF, schema_r,
|
||||
-1);
|
||||
return sprintf (buffer, " <rdf:Description xmlns:%s='%s'>\n",
|
||||
(*schema_r)->prefix, (*schema_r)->uri);
|
||||
}
|
||||
|
||||
static gint
|
||||
gen_schema_end (GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gchar *buffer)
|
||||
{
|
||||
return sprintf (buffer, " </rdf:Description>\n\n");
|
||||
}
|
||||
|
||||
static gint
|
||||
gen_property (GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gchar *buffer,
|
||||
const XMPSchema *schema)
|
||||
{
|
||||
const XMPProperty *property;
|
||||
const gchar **value_array;
|
||||
gssize length;
|
||||
gint i;
|
||||
|
||||
gtk_tree_model_get (model, iter,
|
||||
COL_XMP_TYPE_XREF, &property,
|
||||
COL_XMP_VALUE_RAW, &value_array,
|
||||
-1);
|
||||
g_return_val_if_fail (property->name != NULL, 0);
|
||||
switch (property->type)
|
||||
{
|
||||
case XMP_TYPE_BOOLEAN:
|
||||
case XMP_TYPE_DATE:
|
||||
case XMP_TYPE_INTEGER:
|
||||
case XMP_TYPE_REAL:
|
||||
case XMP_TYPE_MIME_TYPE:
|
||||
case XMP_TYPE_TEXT:
|
||||
case XMP_TYPE_RATIONAL:
|
||||
return sprintf (buffer, " <%s:%s>%s</%s:%s>\n",
|
||||
schema->prefix, property->name,
|
||||
value_array[0],
|
||||
schema->prefix, property->name);
|
||||
|
||||
case XMP_TYPE_LOCALE_BAG:
|
||||
case XMP_TYPE_TEXT_BAG:
|
||||
case XMP_TYPE_XPATH_BAG:
|
||||
case XMP_TYPE_JOB_BAG:
|
||||
length = sprintf (buffer, " <%s:%s>\n <rdf:Bag>\n",
|
||||
schema->prefix, property->name);
|
||||
for (i = 0; value_array[i] != NULL; i++)
|
||||
length += sprintf (buffer + length, " <rdf:li>%s</rdf:li>\n",
|
||||
value_array[i]);
|
||||
length += sprintf (buffer + length, " </rdf:Bag>\n </%s:%s>\n",
|
||||
schema->prefix, property->name);
|
||||
return length;
|
||||
|
||||
case XMP_TYPE_INTEGER_SEQ:
|
||||
case XMP_TYPE_TEXT_SEQ:
|
||||
case XMP_TYPE_RESOURCE_EVENT_SEQ:
|
||||
case XMP_TYPE_RATIONAL_SEQ:
|
||||
length = sprintf (buffer, " <%s:%s>\n <rdf:Seq>\n",
|
||||
schema->prefix, property->name);
|
||||
for (i = 0; value_array[i] != NULL; i++)
|
||||
length += sprintf (buffer + length, " <rdf:li>%s</rdf:li>\n",
|
||||
value_array[i]);
|
||||
length += sprintf (buffer + length, " </rdf:Seq>\n </%s:%s>\n",
|
||||
schema->prefix, property->name);
|
||||
return length;
|
||||
|
||||
case XMP_TYPE_LANG_ALT:
|
||||
length = sprintf (buffer, " <%s:%s>\n <rdf:Alt>\n",
|
||||
schema->prefix, property->name);
|
||||
for (i = 0; value_array[i] != NULL; i += 2)
|
||||
length += sprintf (buffer + length,
|
||||
" <rdf:li xml:lang='%s'>%s</rdf:li>\n",
|
||||
value_array[i], value_array[i + 1]);
|
||||
length += sprintf (buffer + length, " </rdf:Alt>\n </%s:%s>\n",
|
||||
schema->prefix, property->name);
|
||||
return length;
|
||||
|
||||
case XMP_TYPE_URI:
|
||||
return sprintf (buffer, " <%s:%s rdf:resource='%s' />\n",
|
||||
schema->prefix, property->name, value_array[0]);
|
||||
|
||||
case XMP_TYPE_RESOURCE_REF:
|
||||
case XMP_TYPE_DIMENSIONS:
|
||||
case XMP_TYPE_THUMBNAIL_ALT:
|
||||
case XMP_TYPE_GPS_COORDINATE:
|
||||
case XMP_TYPE_FLASH:
|
||||
case XMP_TYPE_OECF_SFR:
|
||||
case XMP_TYPE_CFA_PATTERN:
|
||||
case XMP_TYPE_DEVICE_SETTINGS:
|
||||
g_warning ("FIXME: output not implemented yet (%s)", property->name);
|
||||
break;
|
||||
|
||||
case XMP_TYPE_UNKNOWN:
|
||||
g_warning ("Unknown property type for %s", property->name);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xmp_generate_block:
|
||||
* @xmp_model: An #XMPModel
|
||||
* @buffer: buffer in which the XMP block will be written
|
||||
* @buffer_size: maximum size of the buffer
|
||||
*
|
||||
* Generate XMP block from xmp_model.
|
||||
*
|
||||
* Return value: number of characters stored in the buffer (not including the terminating NUL).
|
||||
*/
|
||||
gssize
|
||||
xmp_generate_block (XMPModel *xmp_model,
|
||||
gchar *buffer,
|
||||
gssize buffer_size)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeIter child;
|
||||
gssize n;
|
||||
const XMPSchema *schema;
|
||||
|
||||
model = xmp_model_get_tree_model (xmp_model);
|
||||
|
||||
strcpy (buffer,
|
||||
"<?xpacket begin='\357\273\277' id='W5M0MpCehiHzreSzNTczkc9d'?>\n"
|
||||
"<x:xmpmeta xmlns:x='adobe:ns:meta/'>\n"
|
||||
"<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>\n"
|
||||
"\n");
|
||||
n = sizeof (
|
||||
"<?xpacket begin='\357\273\277' id='W5M0MpCehiHzreSzNTczkc9d'?>\n"
|
||||
"<x:xmpmeta xmlns:x='adobe:ns:meta/'>\n"
|
||||
"<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>\n"
|
||||
"\n") - 1;
|
||||
/* generate the contents of the XMP block */
|
||||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
n += gen_schema_start (model, &iter, buffer + n, &schema);
|
||||
if (gtk_tree_model_iter_children (model, &child, &iter))
|
||||
{
|
||||
do
|
||||
{
|
||||
n += gen_property (model, &child, buffer + n, schema);
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &child));
|
||||
}
|
||||
n += gen_schema_end (model, &iter, buffer + n);
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &iter));
|
||||
}
|
||||
n = strlen (buffer);
|
||||
strcpy (buffer + n, "</rdf:RDF>\n</x:xmpmeta>\n<?xpacket end='r'?>\n");
|
||||
n += sizeof ("</rdf:RDF>\n</x:xmpmeta>\n<?xpacket end='r'?>\n") - 1;
|
||||
|
||||
return n;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/* xmp-gen.h - generate XMP metadata from the tree model
|
||||
*
|
||||
* 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 XMP_GEN_H
|
||||
#define XMP_GEN_H
|
||||
|
||||
#include <glib.h>
|
||||
#include "xmp-model.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gssize xmp_estimate_size (XMPModel *xmp_model);
|
||||
|
||||
gssize xmp_generate_block (XMPModel *xmp_model,
|
||||
gchar *buffer,
|
||||
gssize buffer_size);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* XMP_GEN_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,128 @@
|
|||
/* xmp-model.h - treeview model for XMP metadata
|
||||
*
|
||||
* Copyright (C) 2004, 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 XMP_MODEL_H
|
||||
#define XMP_MODEL_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _XMPModel XMPModel;
|
||||
|
||||
/* known data types for XMP properties, as found in the XMP specification */
|
||||
typedef enum
|
||||
{
|
||||
XMP_TYPE_BOOLEAN, /* TEXT */
|
||||
XMP_TYPE_DATE, /* TEXT */
|
||||
XMP_TYPE_DIMENSIONS, /* STRUCTURE */
|
||||
XMP_TYPE_INTEGER, /* TEXT */
|
||||
XMP_TYPE_INTEGER_SEQ, /* ORDERED_LIST */
|
||||
XMP_TYPE_LANG_ALT, /* ALT_LANG */
|
||||
XMP_TYPE_LOCALE_BAG, /* UNORDERED_LIST */
|
||||
XMP_TYPE_REAL, /* TEXT */
|
||||
XMP_TYPE_MIME_TYPE, /* TEXT */
|
||||
XMP_TYPE_TEXT, /* TEXT */
|
||||
XMP_TYPE_TEXT_BAG, /* UNORDERED_LIST */
|
||||
XMP_TYPE_TEXT_SEQ, /* ORDERED_LIST */
|
||||
XMP_TYPE_THUMBNAIL_ALT, /* (?) */
|
||||
XMP_TYPE_URI, /* TEXT or RESOURCE (?) */
|
||||
XMP_TYPE_XPATH_BAG, /* UNORDERED_LIST */
|
||||
XMP_TYPE_RESOURCE_EVENT_SEQ, /* ORDERED_LIST */
|
||||
XMP_TYPE_RESOURCE_REF, /* TEXT */
|
||||
XMP_TYPE_JOB_BAG, /* UNORDERED_LIST */
|
||||
XMP_TYPE_RATIONAL, /* TEXT */
|
||||
XMP_TYPE_RATIONAL_SEQ, /* ORDERED_LIST */
|
||||
XMP_TYPE_GPS_COORDINATE, /* (?) */
|
||||
XMP_TYPE_FLASH, /* STRUCTURE */
|
||||
XMP_TYPE_OECF_SFR, /* (?) */
|
||||
XMP_TYPE_CFA_PATTERN, /* (?) */
|
||||
XMP_TYPE_DEVICE_SETTINGS, /* (?) */
|
||||
XMP_TYPE_UNKNOWN
|
||||
} XMPType;
|
||||
|
||||
/* columns used in the GtkTreeStore model holding the XMP metadata */
|
||||
typedef enum
|
||||
{
|
||||
COL_XMP_NAME = 0, /* G_TYPE_STRING */
|
||||
COL_XMP_VALUE, /* G_TYPE_STRING */
|
||||
COL_XMP_VALUE_RAW, /* G_TYPE_POINTER */
|
||||
COL_XMP_TYPE_XREF, /* G_TYPE_POINTER */
|
||||
COL_XMP_WIDGET_XREF, /* G_TYPE_POINTER */
|
||||
COL_XMP_EDITABLE, /* G_TYPE_INT */
|
||||
COL_XMP_EDIT_ICON, /* GDK_TYPE_PIXBUF */
|
||||
COL_XMP_VISIBLE, /* G_TYPE_BOOLEAN */
|
||||
COL_XMP_WEIGHT, /* G_TYPE_INT */
|
||||
COL_XMP_WEIGHT_SET, /* G_TYPE_BOOLEAN */
|
||||
XMP_MODEL_NUM_COLUMNS
|
||||
} XMPModelColumns;
|
||||
|
||||
/* special value for the COL_XMP_EDITABLE column. not strictly boolean... */
|
||||
#define XMP_AUTO_UPDATE 2
|
||||
|
||||
/* XMP properties referenced in the tree via COL_XMP_TYPE_XREF (depth 2) */
|
||||
typedef struct
|
||||
{
|
||||
const gchar *name;
|
||||
XMPType type;
|
||||
gboolean editable;
|
||||
} XMPProperty;
|
||||
|
||||
/* XMP schemas referenced in the tree via COL_XMP_TYPE_XREF (depth 1) */
|
||||
typedef struct
|
||||
{
|
||||
const gchar *uri;
|
||||
const gchar *prefix;
|
||||
const gchar *name;
|
||||
XMPProperty *properties;
|
||||
} XMPSchema;
|
||||
|
||||
XMPModel *xmp_model_new (void);
|
||||
|
||||
void xmp_model_free (XMPModel *xmp_model);
|
||||
|
||||
gboolean xmp_model_is_empty (XMPModel *xmp_model);
|
||||
|
||||
gboolean xmp_model_parse_buffer (XMPModel *xmp_model,
|
||||
const gchar *buffer,
|
||||
gssize buffer_length,
|
||||
gboolean skip_other_data,
|
||||
GError **error);
|
||||
|
||||
gboolean xmp_model_parse_file (XMPModel *xmp_model,
|
||||
const gchar *filename,
|
||||
GError **error);
|
||||
|
||||
GtkTreeModel *xmp_model_get_tree_model (XMPModel *xmp_model);
|
||||
|
||||
const gchar *xmp_model_get_scalar_property (XMPModel *xmp_model,
|
||||
const gchar *schema_name,
|
||||
const gchar *property_name);
|
||||
|
||||
gboolean xmp_model_set_scalar_property (XMPModel *xmp_model,
|
||||
const gchar *schema_name,
|
||||
const gchar *property_name,
|
||||
const gchar *property_value);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* XMP_MODEL_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,130 @@
|
|||
/* xmp-parse.h - simple parser for XMP metadata
|
||||
*
|
||||
* Copyright (C) 2004, 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 XMP_PARSE_H
|
||||
#define XMP_PARSE_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum
|
||||
{
|
||||
XMP_ERROR_NO_XPACKET,
|
||||
XMP_ERROR_BAD_ENCODING,
|
||||
XMP_ERROR_PARSE,
|
||||
XMP_ERROR_MISSING_ABOUT,
|
||||
XMP_ERROR_UNKNOWN_ELEMENT,
|
||||
XMP_ERROR_UNKNOWN_ATTRIBUTE,
|
||||
XMP_ERROR_UNEXPECTED_ELEMENT,
|
||||
XMP_ERROR_INVALID_CONTENT,
|
||||
XMP_ERROR_INVALID_COMMENT
|
||||
} XMPParseError;
|
||||
|
||||
#define XMP_PARSE_ERROR xmp_parse_error_quark ()
|
||||
|
||||
GQuark xmp_parse_error_quark (void);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
XMP_FLAG_FIND_XPACKET = 1 << 0, /* allow text before <?xpacket */
|
||||
XMP_FLAG_NO_COMMENTS = 1 << 1, /* no XML comments allowed */
|
||||
XMP_FLAG_NO_UNKNOWN_ELEMENTS = 1 << 2, /* no unknown XML elements */
|
||||
XMP_FLAG_NO_UNKNOWN_ATTRIBUTES = 1 << 3, /* no unknown XML attributes */
|
||||
XMP_FLAG_NO_MISSING_ABOUT = 1 << 4, /* schemas must have rdf:about */
|
||||
XMP_FLAG_DEFER_VALUE_FREE = 1 << 5 /* prop. value[] freed by caller */
|
||||
} XMPParseFlags;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
XMP_PTYPE_TEXT, /* value in value[0] */
|
||||
XMP_PTYPE_RESOURCE, /* value in value[0] */
|
||||
XMP_PTYPE_ORDERED_LIST, /* values in value[0..n] */
|
||||
XMP_PTYPE_UNORDERED_LIST, /* values in value[0..n] */
|
||||
XMP_PTYPE_ALT_LANG, /* lang in value[0,2..n*2], text in value[1,3..n*2+1] */
|
||||
XMP_PTYPE_STRUCTURE, /* ns prefix in name[0], ns uri in name[1], */
|
||||
/* name in value[2,4..n*2+2], value in value[3,5..n*2+3] */
|
||||
XMP_PTYPE_UNKNOWN
|
||||
} XMPParseType;
|
||||
|
||||
typedef struct _XMPParseContext XMPParseContext;
|
||||
typedef struct _XMPParser XMPParser;
|
||||
|
||||
struct _XMPParser
|
||||
{
|
||||
/* Called whenever the parser sees a new namespace (usually an XMP
|
||||
* schema) except for the basic RDF and XMP namespaces. The value
|
||||
* returned by this callback will be passed as "ns_user_data" to the
|
||||
* callbacks end_schema and set_property. It is allowed to return
|
||||
* the pointers ns_uri or ns_prefix because they will remain valid
|
||||
* at least until end_schema is called.
|
||||
*/
|
||||
gpointer (*start_schema) (XMPParseContext *context,
|
||||
const gchar *ns_uri,
|
||||
const gchar *ns_prefix,
|
||||
gpointer user_data,
|
||||
GError **error);
|
||||
|
||||
/* Called when a namespace goes out of scope. The ns_user_data will
|
||||
* be the one that was returned by start_schema for the
|
||||
* corresponding schema.
|
||||
*/
|
||||
void (*end_schema) (XMPParseContext *context,
|
||||
gpointer ns_user_data,
|
||||
gpointer user_data,
|
||||
GError **error);
|
||||
|
||||
/* Called for each property that is defined in the XMP packet. The
|
||||
* way the value of the property is returned depends on its type.
|
||||
* See the definition of XMPParseType for details.
|
||||
*/
|
||||
void (*set_property) (XMPParseContext *context,
|
||||
const gchar *name,
|
||||
XMPParseType type,
|
||||
const gchar **value,
|
||||
gpointer ns_user_data,
|
||||
gpointer user_data,
|
||||
GError **error);
|
||||
|
||||
/* Called on error, including one set by other methods in the
|
||||
* vtable. The GError should not be freed.
|
||||
*/
|
||||
void (*error) (XMPParseContext *context,
|
||||
GError *error,
|
||||
gpointer user_data);
|
||||
};
|
||||
|
||||
XMPParseContext * xmp_parse_context_new (const XMPParser *parser,
|
||||
XMPParseFlags flags,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_data_dnotify);
|
||||
|
||||
void xmp_parse_context_free (XMPParseContext *context);
|
||||
|
||||
gboolean xmp_parse_context_parse (XMPParseContext *context,
|
||||
const gchar *text,
|
||||
gssize text_len,
|
||||
GError **error);
|
||||
|
||||
gboolean xmp_parse_context_end_parse (XMPParseContext *context,
|
||||
GError **error);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* XMP_PARSE_H */
|
Loading…
Reference in New Issue