mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-10-06 09:32:41 +02:00
Compare commits
66 Commits
wip/akkana
...
metadata-b
Author | SHA1 | Date | |
---|---|---|---|
|
47d8b68cd3 | ||
|
51d52dc9a1 | ||
|
9798eef3a5 | ||
|
96a49b90ab | ||
|
8bc8afdbb4 | ||
|
fc17f9cf42 | ||
|
9760b41486 | ||
|
6252cd8fa4 | ||
|
5f71c0a2d9 | ||
|
b540496b69 | ||
|
d085efe756 | ||
|
6fe3e365d2 | ||
|
8209f480c6 | ||
|
3993ff9aa8 | ||
|
9e2ec9cf55 | ||
|
c796242a7c | ||
|
9dc69dbfd8 | ||
|
a8c1babd73 | ||
|
6a86a91fe0 | ||
|
27cb8a06b6 | ||
|
5f2bd201df | ||
|
05cf2bf893 | ||
|
5925566d85 | ||
|
d60873cdb5 | ||
|
607a6581ca | ||
|
f905f42f0b | ||
|
482d1ad4e7 | ||
|
3914ac2480 | ||
|
8a80a00e65 | ||
|
893f990618 | ||
|
db44a29dcb | ||
|
36881974d1 | ||
|
540e3b392a | ||
|
ded93df773 | ||
|
fd3a20b993 | ||
|
cdc3527ad9 | ||
|
deaf61f332 | ||
|
b807a919a3 | ||
|
e1a6f31b5e | ||
|
6ebf31e7c9 | ||
|
9195f5c724 | ||
|
e1a39c4eff | ||
|
6c0d870fb8 | ||
|
fcd777f730 | ||
|
23a257335a | ||
|
0333090da5 | ||
|
c500d40a51 | ||
|
0f5a5917f9 | ||
|
c3edffd206 | ||
|
30c6574c74 | ||
|
166d7685bf | ||
|
c75a2c5d09 | ||
|
009fb5e76b | ||
|
be06d09260 | ||
|
1e81d9d05c | ||
|
62e8455318 | ||
|
744beb479f | ||
|
501db6b924 | ||
|
2ce13dc603 | ||
|
0d9b025eb0 | ||
|
ddab2dc394 | ||
|
9a001d543a | ||
|
190986a323 | ||
|
8b0c9172b6 | ||
|
4369ec96e6 | ||
|
9ee2ddc84d |
21
configure.ac
21
configure.ac
@@ -61,6 +61,7 @@ m4_define([libcurl_required_version], [7.15.1])
|
||||
m4_define([dbus_glib_required_version], [0.70])
|
||||
m4_define([libgudev_required_version], [167])
|
||||
m4_define([exif_required_version], [0.6.15])
|
||||
m4_define([gexiv2_required_version], [0.4])
|
||||
m4_define([lcms_required_version], [2.2])
|
||||
m4_define([libpng_required_version], [1.2.37])
|
||||
m4_define([liblzma_required_version], [5.0.0])
|
||||
@@ -1299,6 +1300,23 @@ AC_SUBST(EXIF_LIBS)
|
||||
AM_CONDITIONAL(HAVE_LIBEXIF, test "x$have_libexif" = xyes)
|
||||
|
||||
|
||||
#####################
|
||||
# Check for libgexiv2
|
||||
#####################
|
||||
|
||||
AC_ARG_WITH(libgexiv2, [ --without-libgexiv2 don't build the metadata plug-in])
|
||||
|
||||
have_libgexiv2=no
|
||||
if test "x$with_libgexiv2" != xno; then
|
||||
have_libgexiv2=yes
|
||||
PKG_CHECK_MODULES(GEXIV2, gexiv2 >= gexiv2_required_version,
|
||||
AC_DEFINE(HAVE_LIBGEXIV2, 1, [libgexi2v is available]),
|
||||
have_libgexiv2="no (libgexiv2 not found or too old)")
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_LIBGEXIV2, test "x$have_libgexiv2" = xyes)
|
||||
|
||||
|
||||
#################
|
||||
# Check for libaa
|
||||
#################
|
||||
@@ -2240,6 +2258,8 @@ plug-ins/lighting/images/Makefile
|
||||
plug-ins/map-object/Makefile
|
||||
plug-ins/maze/Makefile
|
||||
plug-ins/metadata/Makefile
|
||||
plug-ins/metadata/tests/Makefile
|
||||
plug-ins/metadata/tests/files/Makefile
|
||||
plug-ins/pagecurl/Makefile
|
||||
plug-ins/print/Makefile
|
||||
plug-ins/pygimp/Makefile
|
||||
@@ -2381,6 +2401,7 @@ Optional Plug-Ins:
|
||||
|
||||
Plug-In Features:
|
||||
EXIF support: $have_libexif
|
||||
XMP support: $have_libexiv2
|
||||
|
||||
Optional Modules:
|
||||
ALSA (MIDI Input): $have_alsa
|
||||
|
@@ -1,4 +1,7 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
SUBDIRS = \
|
||||
tests
|
||||
|
||||
|
||||
libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
|
||||
libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
|
||||
@@ -49,18 +52,6 @@ metadata_SOURCES = \
|
||||
# iptc-decode.h \
|
||||
# iptc-decode.c
|
||||
|
||||
noinst_PROGRAMS = xmpdump
|
||||
xmpdump_SOURCES = \
|
||||
xmpdump.c \
|
||||
xmp-schemas.h \
|
||||
xmp-schemas.c \
|
||||
xmp-encode.h \
|
||||
xmp-encode.c \
|
||||
xmp-model.h \
|
||||
xmp-model.c \
|
||||
xmp-parse.h \
|
||||
xmp-parse.c
|
||||
|
||||
INCLUDES = \
|
||||
-I$(top_srcdir) \
|
||||
$(GTK_CFLAGS) \
|
||||
@@ -77,6 +68,7 @@ LDADD = \
|
||||
$(libgimpcolor) \
|
||||
$(libgimpbase) \
|
||||
$(EXIF_LIBS) \
|
||||
$(GEXIV2_LIBS) \
|
||||
$(GTK_LIBS) \
|
||||
$(RT_LIBS) \
|
||||
$(INTLLIBS) \
|
||||
|
@@ -1,11 +1,27 @@
|
||||
What is this?
|
||||
-------------
|
||||
=============
|
||||
|
||||
This is the beginning of an editor for metadata. It is far from
|
||||
complete yet.
|
||||
|
||||
History
|
||||
-------
|
||||
|
||||
Raphaël Quinet is the original author of the editor, but couldn't
|
||||
finish it. I - Róman Joost - started over as a C beginner hacking on the
|
||||
editor. In the hope to improve the thing I continued following Raphaëls
|
||||
initial path.
|
||||
|
||||
I want to leave this document as is, since I think Raphaëls thoughts a
|
||||
still valid. Since his work a lot has changed in terms of parsing and
|
||||
generating XMP.
|
||||
|
||||
That's why I'll be integrating the parsing component with a metadata
|
||||
library instead of continuing the work of that component.
|
||||
|
||||
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
|
||||
@@ -14,63 +30,88 @@ Current status
|
||||
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 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.
|
||||
|
||||
|
||||
Design
|
||||
------
|
||||
|
||||
1. Scenario:
|
||||
1. A user opens a file (e.g. a jpeg)
|
||||
1. A file plug-in (e.g. jpeg-load) finds an XMP header and calls the
|
||||
metadata plug-in through the PDB to decode the XMP.
|
||||
1. The metadata plug-in parses the XMP and stores the result in a
|
||||
parasite.
|
||||
1. The file has finished loading.
|
||||
1. The user opens the metadata editor from the menu.
|
||||
1. The metadata editor finds the XMP packet stored in the parasite.
|
||||
1. The parasite is parsed again and the data read into the XMPModel
|
||||
1. The editor has finished loading.
|
||||
1. The data is displayed in the editor.
|
||||
|
||||
|
||||
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.
|
||||
The Exiv2 project provides quite a big amount of test images:
|
||||
|
||||
http://www.exiv2.org/
|
||||
|
||||
Work in progress:
|
||||
-----------------
|
||||
- improve XMP code generation (xmp-gen.c) - currently, some structured
|
||||
|
||||
- libgexiv2 integration
|
||||
|
||||
* Reducing amount of code to maintain
|
||||
* Less error prone and sharing of code.
|
||||
|
||||
- improve XMP code generation (xmp-encode.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)
|
||||
- fix the conversion to/from EXIF (exif-decode.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/exif-decode.c (converts EXIF into the XMP tree model)
|
||||
plug-ins/metadata/exif-decode.h
|
||||
plug-ins/metadata/interface.c (user interface: widgets and callbacks)
|
||||
plug-ins/metadata/xmp-model.h
|
||||
plug-ins/metadata/interface.h
|
||||
plug-ins/metadata/metadata.c (main part: registers the plug-in, etc.)
|
||||
plug-ins/metadata/xmp-encode.c (generates XMP metadata from the tree model)
|
||||
plug-ins/metadata/xmp-encode.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-model.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)
|
||||
plug-ins/metadata/xmp-parse.h
|
||||
plug-ins/metadata/xmp-schemas.c (holds schema definitions for XMP metadata)
|
||||
plug-ins/metadata/xmp-schemas.h
|
||||
|
||||
UI:
|
||||
plug-ins/metadata/gimpxmpmodelentry.c (Entry widget to string values.)
|
||||
plug-ins/metadata/gimpxmpmodelentry.h
|
||||
plug-ins/metadata/gimpxmpmodeltext.c (Entry widget for text values)
|
||||
plug-ins/metadata/gimpxmpmodeltext.h
|
||||
plug-ins/metadata/gimpxmpmodelwidget.c (Interface Widget which is subscribed to change events.)
|
||||
plug-ins/metadata/gimpxmpmodelwidget.h
|
||||
|
@@ -86,7 +86,15 @@ static void
|
||||
gimp_xmp_model_entry_set_text (GimpXmpModelWidget *widget,
|
||||
const gchar *tree_value)
|
||||
{
|
||||
g_signal_handlers_block_by_func (widget,
|
||||
gimp_xmp_model_entry_changed,
|
||||
NULL);
|
||||
|
||||
gtk_entry_set_text (GTK_ENTRY (widget), tree_value);
|
||||
|
||||
g_signal_handlers_unblock_by_func (widget,
|
||||
gimp_xmp_model_entry_changed,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -54,10 +54,6 @@ static void gimp_xmp_model_widget_xmpmodel_changed (XMPModel *xmp
|
||||
|
||||
const gchar * find_schema_prefix (const gchar *schema_uri);
|
||||
|
||||
void set_property_edit_icon (GtkWidget *widget,
|
||||
XMPModel *xmp_model,
|
||||
GtkTreeIter *iter);
|
||||
|
||||
|
||||
GType
|
||||
gimp_xmp_model_widget_interface_get_type (void)
|
||||
@@ -143,6 +139,7 @@ gimp_xmp_model_widget_constructor (GObject *object)
|
||||
GimpXmpModelWidget *widget = GIMP_XMP_MODEL_WIDGET (object);
|
||||
GimpXmpModelWidgetPrivate *priv;
|
||||
gchar *signal;
|
||||
const gchar *value;
|
||||
|
||||
priv = GIMP_XMP_MODEL_WIDGET_GET_PRIVATE (object);
|
||||
|
||||
@@ -154,6 +151,13 @@ gimp_xmp_model_widget_constructor (GObject *object)
|
||||
G_CALLBACK (gimp_xmp_model_widget_xmpmodel_changed),
|
||||
widget);
|
||||
|
||||
// update the widget in case the xmp-model has already a value set
|
||||
value = xmp_model_get_scalar_property (priv->xmp_model,
|
||||
priv->schema_uri,
|
||||
priv->property_name);
|
||||
if (value != NULL)
|
||||
gimp_xmp_model_widget_set_text (widget, value);
|
||||
|
||||
g_free (signal);
|
||||
}
|
||||
|
||||
@@ -258,20 +262,15 @@ gimp_xmp_model_widget_xmpmodel_changed (XMPModel *xmp_model,
|
||||
GimpXmpModelWidgetPrivate *priv = GIMP_XMP_MODEL_WIDGET_GET_PRIVATE (widget);
|
||||
const gchar *tree_value;
|
||||
const gchar *property_name;
|
||||
GdkPixbuf *icon;
|
||||
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (xmp_model), iter,
|
||||
COL_XMP_NAME, &property_name,
|
||||
COL_XMP_VALUE, &tree_value,
|
||||
COL_XMP_EDIT_ICON, &icon,
|
||||
-1);
|
||||
|
||||
if (! strcmp (priv->property_name, property_name))
|
||||
gimp_xmp_model_widget_set_text (widget, tree_value);
|
||||
|
||||
if (icon == NULL)
|
||||
set_property_edit_icon (GTK_WIDGET (widget), priv->xmp_model, iter);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -306,12 +305,22 @@ void
|
||||
gimp_xmp_model_widget_changed (GimpXmpModelWidget *widget,
|
||||
const gchar *value)
|
||||
{
|
||||
GimpXmpModelWidgetPrivate *priv = GIMP_XMP_MODEL_WIDGET_GET_PRIVATE (widget);
|
||||
GimpXmpModelWidgetPrivate *priv = GIMP_XMP_MODEL_WIDGET_GET_PRIVATE (widget);
|
||||
|
||||
|
||||
g_signal_handlers_block_by_func (priv->xmp_model,
|
||||
gimp_xmp_model_widget_xmpmodel_changed,
|
||||
widget);
|
||||
|
||||
xmp_model_set_scalar_property (priv->xmp_model,
|
||||
priv->schema_uri,
|
||||
priv->property_name,
|
||||
value);
|
||||
|
||||
g_signal_handlers_unblock_by_func (priv->xmp_model,
|
||||
gimp_xmp_model_widget_xmpmodel_changed,
|
||||
widget);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,35 +341,3 @@ find_schema_prefix (const gchar *schema_uri)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
set_property_edit_icon (GtkWidget *widget,
|
||||
XMPModel *xmp_model,
|
||||
GtkTreeIter *iter)
|
||||
{
|
||||
GdkPixbuf *icon;
|
||||
gboolean editable;
|
||||
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (xmp_model), iter,
|
||||
COL_XMP_EDITABLE, &editable,
|
||||
COL_XMP_EDIT_ICON, &icon,
|
||||
-1);
|
||||
|
||||
if (editable == XMP_AUTO_UPDATE)
|
||||
{
|
||||
icon = gtk_widget_render_icon (GTK_WIDGET (widget), GIMP_STOCK_WILBER,
|
||||
GTK_ICON_SIZE_MENU, NULL);
|
||||
gtk_tree_store_set (GTK_TREE_STORE (xmp_model), iter,
|
||||
COL_XMP_EDIT_ICON, icon,
|
||||
-1);
|
||||
}
|
||||
else if (editable == TRUE)
|
||||
{
|
||||
icon = gtk_widget_render_icon (GTK_WIDGET (widget), GTK_STOCK_EDIT,
|
||||
GTK_ICON_SIZE_MENU, NULL);
|
||||
gtk_tree_store_set (GTK_TREE_STORE (xmp_model), iter,
|
||||
COL_XMP_EDIT_ICON, icon,
|
||||
-1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@@ -65,8 +65,6 @@ typedef struct
|
||||
{
|
||||
GtkWidget *dlg;
|
||||
XMPModel *xmp_model;
|
||||
GdkPixbuf *edit_icon;
|
||||
GdkPixbuf *auto_icon;
|
||||
gboolean run_ok;
|
||||
} MetadataGui;
|
||||
|
||||
@@ -79,14 +77,9 @@ tree_value_edited (GtkCellRendererText *cell,
|
||||
GtkTreeModel *model = 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);
|
||||
@@ -161,35 +154,55 @@ icon_foreach_func (GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer user_data)
|
||||
{
|
||||
gboolean editable;
|
||||
MetadataGui *mgui = user_data;
|
||||
gboolean editable;
|
||||
GtkTreeView *treeview = GTK_TREE_VIEW (user_data);
|
||||
GdkPixbuf *icon = NULL;
|
||||
|
||||
gtk_tree_model_get (model, iter,
|
||||
COL_XMP_EDITABLE, &editable,
|
||||
-1);
|
||||
|
||||
if (editable == XMP_AUTO_UPDATE)
|
||||
{
|
||||
icon = gtk_widget_render_icon (GTK_WIDGET (treeview),
|
||||
GIMP_STOCK_WILBER,
|
||||
GTK_ICON_SIZE_MENU, NULL);
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model), iter,
|
||||
COL_XMP_EDIT_ICON, mgui->auto_icon,
|
||||
COL_XMP_EDIT_ICON, icon,
|
||||
-1);
|
||||
}
|
||||
else if (editable == TRUE)
|
||||
{
|
||||
icon = gtk_widget_render_icon (GTK_WIDGET (treeview),
|
||||
GTK_STOCK_EDIT,
|
||||
GTK_ICON_SIZE_MENU, NULL);
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model), iter,
|
||||
COL_XMP_EDIT_ICON, mgui->edit_icon,
|
||||
COL_XMP_EDIT_ICON, icon,
|
||||
-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model), iter,
|
||||
COL_XMP_EDIT_ICON, NULL,
|
||||
-1);
|
||||
}
|
||||
|
||||
if (icon != NULL)
|
||||
g_object_unref (icon);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
update_icons (MetadataGui *mgui)
|
||||
update_icons (GtkTreeView *treeview,
|
||||
GtkDirectionType direction,
|
||||
gpointer user_data)
|
||||
{
|
||||
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);
|
||||
model = gtk_tree_view_get_model (treeview);
|
||||
gtk_tree_model_foreach (GTK_TREE_MODEL (model), icon_foreach_func, treeview);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -243,6 +256,8 @@ add_description_tab (GtkWidget *notebook,
|
||||
"property-name", "description",
|
||||
"xmp-model", mgui->xmp_model,
|
||||
NULL);
|
||||
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view),
|
||||
GTK_WRAP_WORD_CHAR);
|
||||
gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
|
||||
gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
|
||||
_("_Description:"), 0.0, 0.5,
|
||||
@@ -264,8 +279,8 @@ add_description_tab (GtkWidget *notebook,
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
text_view = g_object_new (GIMP_TYPE_XMP_MODEL_TEXT,
|
||||
"schema-uri", XMP_SCHEMA_PDF,
|
||||
"property-name", "Keywords",
|
||||
"schema-uri", XMP_SCHEMA_DUBLIN_CORE,
|
||||
"property-name", "subject",
|
||||
"xmp-model", mgui->xmp_model,
|
||||
NULL);
|
||||
gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
|
||||
@@ -340,11 +355,14 @@ add_thumbnail_tab (GtkWidget *notebook)
|
||||
}
|
||||
|
||||
static void
|
||||
add_advanced_tab (GtkWidget *notebook,
|
||||
GtkTreeModel *model)
|
||||
add_advanced_tab (GtkWidget *notebook,
|
||||
MetadataGui *mgui)
|
||||
{
|
||||
GtkWidget *sw;
|
||||
GtkWidget *treeview;
|
||||
GtkWidget *sw;
|
||||
GtkWidget *treeview;
|
||||
GtkTreeModel *model;
|
||||
|
||||
model = xmp_model_get_tree_model (mgui->xmp_model);
|
||||
|
||||
/* Advanced tab */
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
@@ -364,6 +382,10 @@ add_advanced_tab (GtkWidget *notebook,
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (sw), treeview);
|
||||
|
||||
/* update property icons when the user views this tab */
|
||||
g_signal_connect (treeview, "focus",
|
||||
G_CALLBACK (update_icons), NULL);
|
||||
|
||||
/* expand all rows after the treeview widget has been realized */
|
||||
g_signal_connect (treeview, "realize",
|
||||
G_CALLBACK (gtk_tree_view_expand_all),
|
||||
@@ -428,8 +450,6 @@ import_dialog_response (GtkWidget *dlg,
|
||||
return;
|
||||
}
|
||||
|
||||
update_icons (mgui);
|
||||
|
||||
g_free (buffer);
|
||||
g_free (filename);
|
||||
}
|
||||
@@ -641,12 +661,6 @@ metadata_dialog (gint32 image_ID,
|
||||
gtk_widget_show (notebook);
|
||||
|
||||
mgui.xmp_model = xmp_model;
|
||||
mgui.edit_icon = gtk_widget_render_icon (mgui.dlg, GTK_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 */
|
||||
@@ -656,7 +670,7 @@ metadata_dialog (gint32 image_ID,
|
||||
add_camera1_tab (notebook);
|
||||
add_camera2_tab (notebook);
|
||||
add_thumbnail_tab (notebook);
|
||||
add_advanced_tab (notebook, xmp_model_get_tree_model (mgui.xmp_model));
|
||||
add_advanced_tab (notebook, &mgui);
|
||||
|
||||
gtk_window_set_default_size (GTK_WINDOW (mgui.dlg), 400, 500);
|
||||
gtk_widget_show (mgui.dlg);
|
||||
@@ -664,9 +678,5 @@ metadata_dialog (gint32 image_ID,
|
||||
/* 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;
|
||||
}
|
||||
|
@@ -526,12 +526,11 @@ run (const gchar *name,
|
||||
run_mode = param[0].data.d_int32;
|
||||
if (run_mode == GIMP_RUN_INTERACTIVE)
|
||||
{
|
||||
if (! metadata_dialog (image_ID, xmp_model))
|
||||
status = GIMP_PDB_CANCEL;
|
||||
status = GIMP_PDB_CANCEL;
|
||||
if (metadata_dialog (image_ID, xmp_model))
|
||||
status = GIMP_PDB_SUCCESS;
|
||||
}
|
||||
|
||||
g_printerr ("Not implemented yet (EDITOR_PROC)\n");
|
||||
status = GIMP_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
6
plug-ins/metadata/tests/.gitignore
vendored
Normal file
6
plug-ins/metadata/tests/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/Makefile
|
||||
/Makefile.in
|
||||
/.deps
|
||||
/.libs
|
||||
test-xmp-model
|
||||
test-xmp-model-functional
|
48
plug-ins/metadata/tests/Makefile.am
Normal file
48
plug-ins/metadata/tests/Makefile.am
Normal file
@@ -0,0 +1,48 @@
|
||||
SUBDIRS = \
|
||||
files
|
||||
|
||||
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
|
||||
libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
|
||||
libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
|
||||
libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
|
||||
libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
|
||||
|
||||
|
||||
LDADD = \
|
||||
$(libgimpui) \
|
||||
$(libgimpwidgets) \
|
||||
$(libgimp) \
|
||||
$(libgimpmath) \
|
||||
$(libgimpconfig) \
|
||||
$(libgimpcolor) \
|
||||
$(libgimpbase) \
|
||||
$(GEXIV2_LIBS) \
|
||||
$(GTK_LIBS) \
|
||||
$(GLIB_LIBS) \
|
||||
../xmp-parse.o \
|
||||
../xmp-schemas.o \
|
||||
../xmp-encode.o \
|
||||
../xmp-model.o
|
||||
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir) \
|
||||
$(GTK_CFLAGS) \
|
||||
$(EXIF_CFLAGS) \
|
||||
-I$(includedir) \
|
||||
-I ../
|
||||
|
||||
|
||||
TESTS_ENVIRONMENT = \
|
||||
GIMP_TESTING_ABS_TOP_SRCDIR=@abs_top_srcdir@
|
||||
|
||||
TESTS = \
|
||||
test-xmp-model \
|
||||
test-xmp-model-functional
|
||||
|
||||
EXTRA_PROGRAMS = $(TESTS)
|
||||
|
||||
CLEANFILES = $(EXTRA_PROGRAMS)
|
||||
|
2
plug-ins/metadata/tests/files/Makefile.am
Normal file
2
plug-ins/metadata/tests/files/Makefile.am
Normal file
@@ -0,0 +1,2 @@
|
||||
EXTRA_DIST = \
|
||||
test.xmp
|
89
plug-ins/metadata/tests/files/photoshop.xmp
Normal file
89
plug-ins/metadata/tests/files/photoshop.xmp
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
|
||||
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.1-c036 46.276720, Mon Feb 19 2007 22:40:08 ">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<dc:format>application/vnd.adobe.photoshop</dc:format>
|
||||
<dc:title>
|
||||
<rdf:Alt>
|
||||
<rdf:li xml:lang="x-default">Document Title</rdf:li>
|
||||
</rdf:Alt>
|
||||
</dc:title>
|
||||
<dc:creator>
|
||||
<rdf:Seq>
|
||||
<rdf:li>Roman Joost</rdf:li>
|
||||
</rdf:Seq>
|
||||
</dc:creator>
|
||||
<dc:description>
|
||||
<rdf:Alt>
|
||||
<rdf:li xml:lang="x-default">This is an example export from Photoshop</rdf:li>
|
||||
</rdf:Alt>
|
||||
</dc:description>
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>Test</rdf:li>
|
||||
<rdf:li>Image</rdf:li>
|
||||
<rdf:li>Photoshop</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
<dc:rights>
|
||||
<rdf:Alt>
|
||||
<rdf:li xml:lang="x-default">Don't copy!</rdf:li>
|
||||
</rdf:Alt>
|
||||
</dc:rights>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:xap="http://ns.adobe.com/xap/1.0/">
|
||||
<xap:CreatorTool>Adobe Photoshop CS3 Windows</xap:CreatorTool>
|
||||
<xap:CreateDate>2011-09-09T11:34:36+10:00</xap:CreateDate>
|
||||
<xap:ModifyDate>2011-09-09T11:34:36+10:00</xap:ModifyDate>
|
||||
<xap:MetadataDate>2011-09-09T11:34:36+10:00</xap:MetadataDate>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:xapMM="http://ns.adobe.com/xap/1.0/mm/">
|
||||
<xapMM:DocumentID>uuid:E50AF9014DB3E0118751F8D311BA6F96</xapMM:DocumentID>
|
||||
<xapMM:InstanceID>uuid:E50AF9014DB3E0118751F8D311BA6F96</xapMM:InstanceID>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/">
|
||||
<photoshop:ColorMode>0</photoshop:ColorMode>
|
||||
<photoshop:AuthorsPosition>Mr</photoshop:AuthorsPosition>
|
||||
<photoshop:DateCreated>2011-09-09</photoshop:DateCreated>
|
||||
<photoshop:City>Brisbane</photoshop:City>
|
||||
<photoshop:State>QLD</photoshop:State>
|
||||
<photoshop:Country>Australia</photoshop:Country>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:xapRights="http://ns.adobe.com/xap/1.0/rights/">
|
||||
<xapRights:Marked>True</xapRights:Marked>
|
||||
<xapRights:WebStatement>http://www.gimp.org</xapRights:WebStatement>
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/">
|
||||
<illustrator:StartupProfile>Print</illustrator:StartupProfile>
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<?xpacket end="w"?>
|
||||
|
37
plug-ins/metadata/tests/files/test.xmp
Normal file
37
plug-ins/metadata/tests/files/test.xmp
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>
|
||||
<x:xmpmeta xmlns:x='adobe:ns:meta/'>
|
||||
<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
|
||||
|
||||
<rdf:Description xmlns:dc='http://purl.org/dc/elements/1.1/'>
|
||||
<dc:title>
|
||||
<rdf:Alt>
|
||||
<rdf:li xml:lang='x-default'>image title</rdf:li>
|
||||
</rdf:Alt>
|
||||
</dc:title>
|
||||
<dc:creator>
|
||||
<rdf:Seq>
|
||||
<rdf:li>roman</rdf:li>
|
||||
</rdf:Seq>
|
||||
</dc:creator>
|
||||
<dc:description>
|
||||
<rdf:Alt>
|
||||
<rdf:li xml:lang='x-default'>Hello, World</rdf:li>
|
||||
<rdf:li xml:lang='de_DE'>Hallo, "Welt!"</rdf:li>
|
||||
</rdf:Alt>
|
||||
</dc:description>
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>Test</rdf:li>
|
||||
<rdf:li>Image</rdf:li>
|
||||
<rdf:li>Photoshop</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:exif="http://ns.adobe.com/exif/1.0/">
|
||||
<exif:ExifVersion>0220</exif:ExifVersion>
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
<?xpacket end='r'?>
|
14
plug-ins/metadata/tests/files/test_xmp.conf
Normal file
14
plug-ins/metadata/tests/files/test_xmp.conf
Normal file
@@ -0,0 +1,14 @@
|
||||
# Exiv2 command file
|
||||
# Run with exiv2 -m test_xmp.conf test_xmp.jpg
|
||||
|
||||
del Xmp.dc.creator
|
||||
set Xmp.dc.creator XmpSeq "1) Wilber"
|
||||
set Xmp.dc.creator "2) Wilma"
|
||||
|
||||
del Xmp.dc.contributor
|
||||
set Xmp.dc.contributor "Róman Joost"
|
||||
set Xmp.dc.contributor "Maiko Joost"
|
||||
|
||||
del Xmp.dc.title
|
||||
set Xmp.dc.title LangAlt Hello, World
|
||||
set Xmp.dc.title LangAlt lang=ja こ
|
BIN
plug-ins/metadata/tests/files/test_xmp.jpg
Normal file
BIN
plug-ins/metadata/tests/files/test_xmp.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
293
plug-ins/metadata/tests/test-xmp-model-functional.c
Normal file
293
plug-ins/metadata/tests/test-xmp-model-functional.c
Normal file
@@ -0,0 +1,293 @@
|
||||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 2011 Róman Joost <romanofski@gimp.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "xmp-parse.h"
|
||||
#include "xmp-encode.h"
|
||||
#include "xmp-model.h"
|
||||
|
||||
|
||||
#define ADD_TEST(function) \
|
||||
g_test_add ("/metadata-xmp-model/" #function, \
|
||||
GimpTestFixture, \
|
||||
NULL, \
|
||||
gimp_test_xmp_model_setup, \
|
||||
function, \
|
||||
gimp_test_xmp_model_teardown);
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
XMPModel *xmp_model;
|
||||
} GimpTestFixture;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const gchar *schema_name;
|
||||
const gchar *name;
|
||||
int pos;
|
||||
const gchar *expected_value;
|
||||
const gchar *expected_values[10];
|
||||
} TestDataEntry;
|
||||
|
||||
static TestDataEntry propertiestotest[] =
|
||||
{
|
||||
{ XMP_PREFIX_DUBLIN_CORE, "title", 1, NULL, { NULL } },
|
||||
{ XMP_PREFIX_DUBLIN_CORE, "creator", 0, NULL, { NULL } },
|
||||
{ XMP_PREFIX_DUBLIN_CORE, "description", 1, NULL, { NULL } },
|
||||
{ XMP_PREFIX_DUBLIN_CORE, "subject", 0, NULL, { NULL } },
|
||||
{ NULL, NULL, 0, NULL, { NULL } }
|
||||
};
|
||||
TestDataEntry * const import_exportdata = propertiestotest;
|
||||
|
||||
/**
|
||||
* This testdata tests different types in the #XMPModel and what they
|
||||
* return for the editor.
|
||||
* Note: The pos attribute is ignored.
|
||||
*/
|
||||
static TestDataEntry _xmp_property_values_view[] =
|
||||
{
|
||||
{ XMP_PREFIX_DUBLIN_CORE, "title", 0, "Hello, World,",
|
||||
{"x-default", "Hello, World,", "ja", "\xe3\x81\x93"} },
|
||||
{ XMP_PREFIX_DUBLIN_CORE, "creator", 0, "1) Wilber, 2) Wilma",
|
||||
{"1) Wilber, 2) Wilma"} },
|
||||
{ NULL, NULL, 0, NULL, { NULL } }
|
||||
};
|
||||
TestDataEntry * const xmp_property_values_view = _xmp_property_values_view;
|
||||
|
||||
|
||||
static void gimp_test_xmp_model_setup (GimpTestFixture *fixture,
|
||||
gconstpointer data);
|
||||
static void gimp_test_xmp_model_teardown (GimpTestFixture *fixture,
|
||||
gconstpointer data);
|
||||
|
||||
|
||||
/**
|
||||
* gimp_test_xmp_model_setup:
|
||||
* @fixture: GimpTestFixture fixture
|
||||
* @data:
|
||||
*
|
||||
* Test fixture to setup an XMPModel.
|
||||
**/
|
||||
static void
|
||||
gimp_test_xmp_model_setup (GimpTestFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
fixture->xmp_model = xmp_model_new ();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gimp_test_xmp_model_teardown (GimpTestFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
g_object_unref (fixture->xmp_model);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* test_xmp_model_value_types
|
||||
* @fixture:
|
||||
* @data:
|
||||
*
|
||||
* Test if different value types are correctly set. The string
|
||||
* representation of the value should be correct.
|
||||
**/
|
||||
static void
|
||||
test_xmp_model_value_types (GimpTestFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
int i, j;
|
||||
guint a, b;
|
||||
const gchar *value;
|
||||
const gchar **raw = NULL;
|
||||
TestDataEntry *testdata;
|
||||
GError **error = NULL;
|
||||
gchar *uri = NULL;
|
||||
|
||||
uri = g_build_filename (g_getenv ("GIMP_TESTING_ABS_TOP_SRCDIR"),
|
||||
"plug-ins/metadata/tests/files/test_xmp.jpg",
|
||||
NULL);
|
||||
|
||||
xmp_model_parse_file (fixture->xmp_model, uri, error);
|
||||
g_free (uri);
|
||||
|
||||
for (i = 0; xmp_property_values_view[i].name != NULL; ++i)
|
||||
{
|
||||
testdata = &(xmp_property_values_view[i]);
|
||||
|
||||
/* view representation for the editor */
|
||||
value = xmp_model_get_scalar_property (fixture->xmp_model,
|
||||
testdata->schema_name,
|
||||
testdata->name);
|
||||
g_assert_cmpstr (value, ==, testdata->expected_value);
|
||||
|
||||
/* internal data, which the view representation is created from */
|
||||
raw = xmp_model_get_raw_property_value (fixture->xmp_model,
|
||||
testdata->schema_name,
|
||||
testdata->name);
|
||||
a = g_strv_length ((gchar **) raw);
|
||||
b = g_strv_length ((gchar **) testdata->expected_values);
|
||||
g_assert_cmpuint (a, ==, b);
|
||||
for (j = 0; raw[j] != NULL; j++)
|
||||
{
|
||||
g_assert_cmpstr (raw[j], ==, testdata->expected_values[j]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* test_xmp_model_import_export_structures:
|
||||
* @fixture:
|
||||
* @data:
|
||||
*
|
||||
* Test to assure the round trip of data import, editing, export is
|
||||
* working.
|
||||
**/
|
||||
static void
|
||||
test_xmp_model_import_export_structures (GimpTestFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
int i;
|
||||
gboolean result;
|
||||
const gchar *before_value;
|
||||
const gchar *after_value;
|
||||
GString *buffer;
|
||||
TestDataEntry *testdata;
|
||||
const gchar *scalarvalue = "test";
|
||||
GError **error = NULL;
|
||||
gchar *uri = NULL;
|
||||
|
||||
|
||||
uri = g_build_filename (g_getenv ("GIMP_TESTING_ABS_TOP_SRCDIR"),
|
||||
"plug-ins/metadata/tests/files/test.xmp",
|
||||
NULL);
|
||||
|
||||
xmp_model_parse_file (fixture->xmp_model, uri, error);
|
||||
g_free (uri);
|
||||
|
||||
for (i = 0; import_exportdata[i].name != NULL; ++i)
|
||||
{
|
||||
testdata = &(import_exportdata[i]);
|
||||
|
||||
/* backup the original raw value */
|
||||
before_value = xmp_model_get_scalar_property (fixture->xmp_model,
|
||||
testdata->schema_name,
|
||||
testdata->name);
|
||||
g_assert (before_value != NULL);
|
||||
|
||||
/* set a new scalar value */
|
||||
result = xmp_model_set_scalar_property (fixture->xmp_model,
|
||||
testdata->schema_name,
|
||||
testdata->name,
|
||||
scalarvalue);
|
||||
g_assert (result == TRUE);
|
||||
|
||||
/* export */
|
||||
buffer = g_string_new ("GIMP_TEST");
|
||||
xmp_generate_packet (fixture->xmp_model, buffer);
|
||||
|
||||
/* import */
|
||||
xmp_model_parse_buffer (fixture->xmp_model,
|
||||
buffer->str,
|
||||
buffer->len,
|
||||
TRUE,
|
||||
error);
|
||||
after_value = xmp_model_get_scalar_property (fixture->xmp_model,
|
||||
testdata->schema_name,
|
||||
testdata->name);
|
||||
/* check that the scalar value is correctly exported */
|
||||
g_assert (after_value != NULL);
|
||||
g_assert_cmpstr (after_value, ==, scalarvalue);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* test_xmp_model_import_export:
|
||||
* @fixture:
|
||||
* @data:
|
||||
*
|
||||
* Functional test, which assures that changes in the string
|
||||
* representation is correctly merged on export. This test starts of
|
||||
* with inserting scalar values only.
|
||||
**/
|
||||
static void
|
||||
test_xmp_model_import_export (GimpTestFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
int i;
|
||||
gboolean result;
|
||||
GString *buffer;
|
||||
TestDataEntry *testdata;
|
||||
const gchar **after_value;
|
||||
const gchar *scalarvalue = "test";
|
||||
GError **error = NULL;
|
||||
|
||||
|
||||
for (i = 0; import_exportdata[i].name != NULL; ++i)
|
||||
{
|
||||
testdata = &(import_exportdata[i]);
|
||||
|
||||
/* set a new scalar value */
|
||||
result = xmp_model_set_scalar_property (fixture->xmp_model,
|
||||
testdata->schema_name,
|
||||
testdata->name,
|
||||
scalarvalue);
|
||||
g_assert (result == TRUE);
|
||||
|
||||
/* export */
|
||||
buffer = g_string_new ("GIMP_TEST");
|
||||
xmp_generate_packet (fixture->xmp_model, buffer);
|
||||
|
||||
/* import */
|
||||
xmp_model_parse_buffer (fixture->xmp_model,
|
||||
buffer->str,
|
||||
buffer->len,
|
||||
TRUE,
|
||||
error);
|
||||
after_value = xmp_model_get_raw_property_value (fixture->xmp_model,
|
||||
testdata->schema_name,
|
||||
testdata->name);
|
||||
|
||||
/* check that the scalar value is correctly exported */
|
||||
g_assert (after_value != NULL);
|
||||
g_assert_cmpstr (after_value[testdata->pos], ==, scalarvalue);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
gint result = -1;
|
||||
|
||||
g_type_init();
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
ADD_TEST (test_xmp_model_import_export);
|
||||
ADD_TEST (test_xmp_model_import_export_structures);
|
||||
ADD_TEST (test_xmp_model_value_types);
|
||||
|
||||
result = g_test_run ();
|
||||
|
||||
return result;
|
||||
}
|
291
plug-ins/metadata/tests/test-xmp-model.c
Normal file
291
plug-ins/metadata/tests/test-xmp-model.c
Normal file
@@ -0,0 +1,291 @@
|
||||
/* GIMP - The GNU Image Manipulation Program
|
||||
* Copyright (C) 2011 Róman Joost <romanofski@gimp.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "xmp-parse.h"
|
||||
#include "xmp-encode.h"
|
||||
#include "xmp-model.h"
|
||||
|
||||
|
||||
#define ADD_TEST(function) \
|
||||
g_test_add ("/metadata-xmp-model/" #function, \
|
||||
GimpTestFixture, \
|
||||
NULL, \
|
||||
gimp_test_xmp_model_setup, \
|
||||
function, \
|
||||
gimp_test_xmp_model_teardown);
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
XMPModel *xmp_model;
|
||||
} GimpTestFixture;
|
||||
|
||||
|
||||
static void gimp_test_xmp_model_setup (GimpTestFixture *fixture,
|
||||
gconstpointer data);
|
||||
static void gimp_test_xmp_model_teardown (GimpTestFixture *fixture,
|
||||
gconstpointer data);
|
||||
|
||||
|
||||
/**
|
||||
* gimp_test_xmp_model_setup:
|
||||
* @fixture: GimpTestFixture fixture
|
||||
* @data:
|
||||
*
|
||||
* Test fixture to setup an XMPModel.
|
||||
**/
|
||||
static void
|
||||
gimp_test_xmp_model_setup (GimpTestFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
fixture->xmp_model = xmp_model_new ();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gimp_test_xmp_model_teardown (GimpTestFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
g_object_unref (fixture->xmp_model);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* test_xmp_model_is_empty:
|
||||
* @fixture:
|
||||
* @data:
|
||||
*
|
||||
* Test to assert that newly created models are empty
|
||||
**/
|
||||
static void
|
||||
test_xmp_model_is_empty (GimpTestFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
XMPModel *xmp_model;
|
||||
|
||||
xmp_model = xmp_model_new ();
|
||||
|
||||
g_assert (xmp_model_is_empty (xmp_model));
|
||||
}
|
||||
|
||||
/**
|
||||
* test_xmp_model_set_get_scalar_property:
|
||||
* @fixture:
|
||||
* @data:
|
||||
*
|
||||
* The test asserts the API to get/set scalar values. This should also
|
||||
* work for many XMPProperties as we operate on one column in the
|
||||
* XMPModel. The COL_XMP_VALUE column is the string representation of
|
||||
* the COL_XMP_VALUE_RAW column, which will be updated upon export.
|
||||
**/
|
||||
static void
|
||||
test_xmp_model_set_get_scalar_property (GimpTestFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
const gchar *property_name = NULL;
|
||||
const gchar *scalar_value;
|
||||
gboolean result;
|
||||
|
||||
/* Schema is nonsense, so nothing is set */
|
||||
result = xmp_model_set_scalar_property (fixture->xmp_model,
|
||||
"SCHEMA",
|
||||
"key",
|
||||
"value");
|
||||
g_assert (result == FALSE);
|
||||
g_assert (xmp_model_is_empty (fixture->xmp_model) == TRUE);
|
||||
|
||||
/* Contributor is a scalar property. When set, we expect the XMPModel
|
||||
* not to be empty any more and that we can retrieve the same value.
|
||||
**/
|
||||
property_name = "me_text";
|
||||
result = xmp_model_set_scalar_property (fixture->xmp_model,
|
||||
"dc",
|
||||
"contributor",
|
||||
property_name);
|
||||
g_assert (result == TRUE);
|
||||
g_assert (xmp_model_is_empty (fixture->xmp_model) == FALSE);
|
||||
|
||||
scalar_value = xmp_model_get_scalar_property (fixture->xmp_model,
|
||||
"dc",
|
||||
"contributor");
|
||||
g_assert_cmpstr (scalar_value, ==, property_name);
|
||||
|
||||
/* Now we assure, that we can even set titles, which is of type
|
||||
* XMP_TYPE_LANG_ALT.
|
||||
* The scalar value returned is always the first value next to the
|
||||
* language specifier.
|
||||
**/
|
||||
property_name = "lang=\"x-default\" title_lang_alt";
|
||||
result = xmp_model_set_scalar_property (fixture->xmp_model,
|
||||
"dc",
|
||||
"title",
|
||||
property_name);
|
||||
g_assert (result == TRUE);
|
||||
|
||||
scalar_value = xmp_model_get_scalar_property (fixture->xmp_model,
|
||||
"dc",
|
||||
"title");
|
||||
g_assert_cmpstr (scalar_value, ==, "title_lang_alt");
|
||||
|
||||
/* Setting an XMP_TYPE_LANG_ALT value without a language identifier,
|
||||
* we'll assume it is 'x-default' and the accessor returns the same
|
||||
* value.
|
||||
*/
|
||||
result = xmp_model_set_scalar_property (fixture->xmp_model,
|
||||
"dc",
|
||||
"title",
|
||||
"me too");
|
||||
g_assert (result == TRUE);
|
||||
|
||||
scalar_value = xmp_model_get_scalar_property (fixture->xmp_model,
|
||||
"dc", "title");
|
||||
g_assert_cmpstr (scalar_value, ==, "me too");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* test_xmp_model_find_xmptype_by:
|
||||
* @fixture:
|
||||
* @data:
|
||||
*
|
||||
* Tests returning the correct property type by given schema and
|
||||
* property_name
|
||||
**/
|
||||
static void
|
||||
test_xmp_model_find_xmptype_by (GimpTestFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
XMPType type;
|
||||
|
||||
type = xmp_model_find_xmptype_by (fixture->xmp_model, "non", "sense");
|
||||
g_assert (type == -1);
|
||||
|
||||
type = xmp_model_find_xmptype_by (fixture->xmp_model, "dc", "title");
|
||||
g_assert (type == XMP_TYPE_LANG_ALT);
|
||||
|
||||
type = xmp_model_find_xmptype_by (fixture->xmp_model, "dc", "contributor");
|
||||
g_assert (type == XMP_TYPE_TEXT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* test_xmp_model_get_raw_property_value:
|
||||
* @fixture:
|
||||
* @data:
|
||||
*
|
||||
* Tests the xmp_model_get_raw_property_value, which returns raw values
|
||||
* from the XMPModel.
|
||||
**/
|
||||
static void
|
||||
test_xmp_model_get_raw_property_value (GimpTestFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
gchar *expected_alt[] = {"en_GB", "my title", NULL};
|
||||
gchar *expected_seq[] = {"Wilber", NULL};
|
||||
const gchar **result = NULL;
|
||||
|
||||
// NULL is returned if no value is set by given schema and property
|
||||
// name
|
||||
g_assert (xmp_model_get_raw_property_value (fixture->xmp_model,
|
||||
"dc", "title") == NULL);
|
||||
|
||||
// XMP_TYPE_LANG_ALT
|
||||
// Note: XMP_TYPE_TEXT is tested with wrapper function
|
||||
// xmp_model_set_scalar_property
|
||||
xmp_model_set_property (fixture->xmp_model,
|
||||
XMP_TYPE_LANG_ALT,
|
||||
"dc",
|
||||
"title",
|
||||
(const gchar **) g_strdupv (expected_alt));
|
||||
|
||||
result = xmp_model_get_raw_property_value (fixture->xmp_model,
|
||||
"dc", "title");
|
||||
g_assert (result != NULL);
|
||||
g_assert_cmpstr (result[0], ==, expected_alt[0]);
|
||||
|
||||
// XMP_TYPE_TEXT_SEQ
|
||||
xmp_model_set_property (fixture->xmp_model,
|
||||
XMP_TYPE_TEXT_SEQ,
|
||||
"dc",
|
||||
"creator",
|
||||
(const gchar**) g_strdupv (expected_seq));
|
||||
result = xmp_model_get_raw_property_value (fixture->xmp_model,
|
||||
"dc", "creator");
|
||||
g_assert (result != NULL);
|
||||
g_assert_cmpstr (result[0], ==, expected_seq[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* test_xmp_model_parse_file:
|
||||
* @fixture:
|
||||
* @data:
|
||||
*
|
||||
* A very simple test parsing a file with XMP DC metadata.
|
||||
**/
|
||||
static void
|
||||
test_xmp_model_parse_file (GimpTestFixture *fixture,
|
||||
gconstpointer data)
|
||||
{
|
||||
gchar *uri = NULL;
|
||||
const gchar *value = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
uri = g_build_filename (g_getenv ("GIMP_TESTING_ABS_TOP_SRCDIR"),
|
||||
"plug-ins/metadata/tests/files/test.xmp",
|
||||
NULL);
|
||||
g_assert (uri != NULL);
|
||||
|
||||
xmp_model_parse_file (fixture->xmp_model, uri, &error);
|
||||
g_assert (! xmp_model_is_empty (fixture->xmp_model));
|
||||
|
||||
// title
|
||||
value = xmp_model_get_scalar_property (fixture->xmp_model, "dc", "title");
|
||||
g_assert_cmpstr (value, == , "image title");
|
||||
|
||||
// creator
|
||||
value = xmp_model_get_scalar_property (fixture->xmp_model, "dc", "creator");
|
||||
g_assert_cmpstr (value, == , "roman");
|
||||
|
||||
// description
|
||||
value = xmp_model_get_scalar_property (fixture->xmp_model, "dc", "description");
|
||||
g_assert_cmpstr (value, == , "Hello, World,");
|
||||
|
||||
g_free (uri);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
gint result = -1;
|
||||
|
||||
g_type_init();
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
ADD_TEST (test_xmp_model_is_empty);
|
||||
ADD_TEST (test_xmp_model_find_xmptype_by);
|
||||
ADD_TEST (test_xmp_model_set_get_scalar_property);
|
||||
ADD_TEST (test_xmp_model_get_raw_property_value);
|
||||
ADD_TEST (test_xmp_model_parse_file);
|
||||
|
||||
result = g_test_run ();
|
||||
|
||||
return result;
|
||||
}
|
@@ -31,13 +31,45 @@
|
||||
#include "xmp-schemas.h"
|
||||
|
||||
|
||||
static void gen_element (GString *buffer,
|
||||
gint indent,
|
||||
const gchar *prefix,
|
||||
const gchar *name,
|
||||
const gchar *value,
|
||||
...) G_GNUC_NULL_TERMINATED;
|
||||
static void gen_element (GString *buffer,
|
||||
gint indent,
|
||||
const gchar *prefix,
|
||||
const gchar *name,
|
||||
const gchar *value,
|
||||
...) G_GNUC_NULL_TERMINATED;
|
||||
|
||||
gchar ** get_extracted_values (const gchar *value,
|
||||
const gchar **value_array);
|
||||
|
||||
/*
|
||||
* Helper function to return the updated array of values. In case value
|
||||
* != NULL the values are separated by ';' and cleaned of leading
|
||||
* whitespace, otherwise the value_array will be returned.
|
||||
*
|
||||
* Return value: a newly-allocated %NULL-terminated array of strings.
|
||||
* Use g_strfreev() to free it.
|
||||
*/
|
||||
gchar **
|
||||
get_extracted_values (const gchar *value,
|
||||
const gchar **value_array)
|
||||
{
|
||||
int i;
|
||||
gchar **str_array;
|
||||
|
||||
if (value != NULL)
|
||||
{
|
||||
str_array = g_strsplit (value, ";", 0);
|
||||
|
||||
for (i = 0; str_array[i] != NULL; i++)
|
||||
str_array[i] = g_strchug (str_array[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
str_array = (gchar **) value_array;
|
||||
}
|
||||
|
||||
return str_array;
|
||||
}
|
||||
|
||||
static void
|
||||
gen_schema_start (GString *buffer,
|
||||
@@ -105,10 +137,12 @@ static void
|
||||
gen_property (GString *buffer,
|
||||
const XMPSchema *schema,
|
||||
const XMPProperty *property,
|
||||
const gchar *value,
|
||||
const gchar **value_array)
|
||||
{
|
||||
gint i;
|
||||
const gchar *ns_prefix;
|
||||
gchar **updated_values = NULL;
|
||||
|
||||
switch (property->type)
|
||||
{
|
||||
@@ -120,7 +154,9 @@ gen_property (GString *buffer,
|
||||
case XMP_TYPE_TEXT:
|
||||
case XMP_TYPE_RATIONAL:
|
||||
gen_element (buffer, 2,
|
||||
schema->prefix, property->name, value_array[0],
|
||||
schema->prefix,
|
||||
property->name,
|
||||
(value != NULL) ? value : value_array[0],
|
||||
NULL);
|
||||
break;
|
||||
|
||||
@@ -130,10 +166,13 @@ gen_property (GString *buffer,
|
||||
case XMP_TYPE_JOB_BAG:
|
||||
g_string_append_printf (buffer, " <%s:%s>\n <rdf:Bag>\n",
|
||||
schema->prefix, property->name);
|
||||
for (i = 0; value_array[i] != NULL; i++)
|
||||
|
||||
updated_values = get_extracted_values (value, value_array);
|
||||
|
||||
for (i = 0; updated_values[i] != NULL; i++)
|
||||
{
|
||||
gen_element (buffer, 4,
|
||||
"rdf", "li", value_array[i],
|
||||
"rdf", "li", updated_values[i],
|
||||
NULL);
|
||||
}
|
||||
g_string_append_printf (buffer, " </rdf:Bag>\n </%s:%s>\n",
|
||||
@@ -146,12 +185,15 @@ gen_property (GString *buffer,
|
||||
case XMP_TYPE_RATIONAL_SEQ:
|
||||
g_string_append_printf (buffer, " <%s:%s>\n <rdf:Seq>\n",
|
||||
schema->prefix, property->name);
|
||||
for (i = 0; value_array[i] != NULL; i++)
|
||||
{
|
||||
gen_element (buffer, 4,
|
||||
"rdf", "li", value_array[i],
|
||||
NULL);
|
||||
}
|
||||
|
||||
updated_values = get_extracted_values (value, value_array);
|
||||
|
||||
for (i = 0; updated_values[i] != NULL; i++)
|
||||
{
|
||||
gen_element (buffer, 4,
|
||||
"rdf", "li", updated_values[i],
|
||||
NULL);
|
||||
}
|
||||
g_string_append_printf (buffer, " </rdf:Seq>\n </%s:%s>\n",
|
||||
schema->prefix, property->name);
|
||||
break;
|
||||
@@ -159,10 +201,31 @@ gen_property (GString *buffer,
|
||||
case XMP_TYPE_LANG_ALT:
|
||||
g_string_append_printf (buffer, " <%s:%s>\n <rdf:Alt>\n",
|
||||
schema->prefix, property->name);
|
||||
gen_element (buffer, 4,
|
||||
"rdf", "li", value_array[0],
|
||||
"xml:lang", "x-default",
|
||||
NULL);
|
||||
if (value_array == NULL && value != NULL)
|
||||
{
|
||||
gen_element (buffer, 4,
|
||||
"rdf", "li", value,
|
||||
"xml:lang", "x-default",
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; value_array[i] != NULL; i += 2)
|
||||
{
|
||||
if (value_array[i+1] == NULL)
|
||||
{
|
||||
g_printerr("Bailing out:%s:%s", schema->prefix, property->name);
|
||||
break;
|
||||
}
|
||||
if (i == 0 && value_array[i+1] != value)
|
||||
value_array[i+1] = g_strdup (value);
|
||||
|
||||
gen_element (buffer, 4,
|
||||
"rdf", "li", value_array[i + 1],
|
||||
"xml:lang", value_array[i],
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
g_string_append_printf (buffer, " </rdf:Alt>\n </%s:%s>\n",
|
||||
schema->prefix, property->name);
|
||||
break;
|
||||
@@ -188,8 +251,9 @@ gen_property (GString *buffer,
|
||||
case XMP_TYPE_DEVICE_SETTINGS:
|
||||
case XMP_TYPE_CONTACT_INFO:
|
||||
case XMP_TYPE_GENERIC_STRUCTURE:
|
||||
if (value_array[0] && value_array[1]
|
||||
&& !! strcmp (value_array[1], schema->uri))
|
||||
if ((value_array != NULL)
|
||||
&& (value_array[0] && value_array[1]
|
||||
&& !! strcmp (value_array[1], schema->uri)))
|
||||
{
|
||||
g_string_append_printf (buffer,
|
||||
" <%s:%s rdf:parseType='Resource'\n"
|
||||
@@ -205,13 +269,21 @@ gen_property (GString *buffer,
|
||||
schema->prefix, property->name);
|
||||
ns_prefix = schema->prefix;
|
||||
}
|
||||
if (value_array[0] && value_array[1])
|
||||
if (value_array != NULL && value_array[0] && value_array[1])
|
||||
{
|
||||
for (i = 2; value_array[i] != NULL; i += 2)
|
||||
{
|
||||
gen_element (buffer, 3,
|
||||
ns_prefix, value_array[i], value_array[i + 1],
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gen_element (buffer, 3,
|
||||
ns_prefix, property->name, value,
|
||||
NULL);
|
||||
}
|
||||
g_string_append_printf (buffer, " </%s:%s>\n",
|
||||
schema->prefix, property->name);
|
||||
break;
|
||||
@@ -225,6 +297,9 @@ gen_property (GString *buffer,
|
||||
g_printerr ("Unknown property type for %s", property->name);
|
||||
break;
|
||||
}
|
||||
|
||||
g_strfreev (updated_values);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,6 +319,7 @@ xmp_generate_packet (XMPModel *xmp_model,
|
||||
GtkTreeIter child;
|
||||
const XMPSchema *schema;
|
||||
gpointer saved_ref;
|
||||
const gchar *saved_value;
|
||||
|
||||
g_return_if_fail (xmp_model != NULL);
|
||||
g_return_if_fail (buffer != NULL);
|
||||
@@ -268,21 +344,26 @@ xmp_generate_packet (XMPModel *xmp_model,
|
||||
if (gtk_tree_model_iter_children (model, &child, &iter))
|
||||
{
|
||||
saved_ref = NULL;
|
||||
saved_value = NULL;
|
||||
do
|
||||
{
|
||||
const XMPProperty *property;
|
||||
const gchar **value_array;
|
||||
const gchar *value;
|
||||
|
||||
gtk_tree_model_get (model, &child,
|
||||
COL_XMP_TYPE_XREF, &property,
|
||||
COL_XMP_VALUE, &value,
|
||||
COL_XMP_VALUE_RAW, &value_array,
|
||||
-1);
|
||||
/* do not process structured types multiple times */
|
||||
if (saved_ref != value_array)
|
||||
if (saved_ref != value_array
|
||||
|| (value != NULL && saved_value != value))
|
||||
{
|
||||
saved_ref = value_array;
|
||||
saved_value = value;
|
||||
g_return_if_fail (property->name != NULL);
|
||||
gen_property (buffer, schema, property, value_array);
|
||||
gen_property (buffer, schema, property, value, value_array);
|
||||
}
|
||||
}
|
||||
while (gtk_tree_model_iter_next (model, &child));
|
||||
|
@@ -25,6 +25,8 @@
|
||||
|
||||
#include <libgimp/gimp.h>
|
||||
|
||||
#include <gexiv2/gexiv2.h>
|
||||
|
||||
#include "libgimp/stdplugins-intl.h"
|
||||
|
||||
#include "xmp-schemas.h"
|
||||
@@ -45,6 +47,8 @@ static void tree_model_row_changed (GtkTreeModel *model,
|
||||
static XMPSchema * find_xmp_schema_by_iter (XMPModel *xmp_model,
|
||||
GtkTreeIter *iter);
|
||||
|
||||
gchar** convert_to_xmp_model_raw_value (const gchar *in);
|
||||
|
||||
enum
|
||||
{
|
||||
PROPERTY_CHANGED,
|
||||
@@ -135,7 +139,8 @@ xmp_model_finalize (GObject *object)
|
||||
gtk_tree_model_get (model, &child,
|
||||
COL_XMP_VALUE_RAW, &value_array,
|
||||
-1);
|
||||
if (value_array != last_value_array)
|
||||
if (value_array != last_value_array
|
||||
&& value_array != NULL)
|
||||
{
|
||||
/* FIXME: this does not free everything */
|
||||
for (i = 0; value_array[i] != NULL; i++)
|
||||
@@ -212,6 +217,70 @@ tree_model_row_changed (GtkTreeModel *model,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* convert_to_xmp_model_raw_value:
|
||||
* @in: a string which is converted in to a raw value for the #XMPModel
|
||||
*
|
||||
* Utility function in order to translate XMP_TYPE_LANG_ALT values to an
|
||||
* array of strings. Each odd index in the array is the language
|
||||
* specifier (e.g. x-default, de_DE, etc). Each even array index is the
|
||||
* value.
|
||||
*
|
||||
* A single string is converted to a two element array and the string is
|
||||
* marked as 'x-default'.
|
||||
*
|
||||
* TODO: Possibly this function can be ignored after the full
|
||||
* integration of gexiv2
|
||||
*
|
||||
* Return value: array of strings.
|
||||
*/
|
||||
gchar**
|
||||
convert_to_xmp_model_raw_value (const gchar *in)
|
||||
{
|
||||
gchar **splitted = NULL;
|
||||
gchar **result;
|
||||
gchar *value = NULL;
|
||||
GSList *list = NULL;
|
||||
GSList *list_iter;
|
||||
int i;
|
||||
|
||||
/* extract the language and corresponding value.
|
||||
* TODO: Fix up values ending with a separator charactor (e.g. ',')
|
||||
*/
|
||||
splitted = g_regex_split_simple ("lang=\"([\\w_\\-]+)\"", in, 0, 0);
|
||||
for (i=0; splitted[i] != NULL; i++)
|
||||
{
|
||||
value = g_strstrip (splitted[i]);
|
||||
if (strcmp (value, ""))
|
||||
list = g_slist_append (list, g_strdup (value));
|
||||
}
|
||||
|
||||
/* in case we only extracted one element, the language identifier is
|
||||
* missing. We'll simply append one */
|
||||
i = g_slist_length (list);
|
||||
if (i == 1)
|
||||
{
|
||||
list = g_slist_prepend (list, "x-default");
|
||||
i++;
|
||||
}
|
||||
|
||||
/* put it back into an array and return it
|
||||
*/
|
||||
result = g_new (gchar*, i + 1);
|
||||
result[i--] = NULL;
|
||||
list = g_slist_reverse(list);
|
||||
for (list_iter = list; list_iter != NULL; list_iter = list_iter->next)
|
||||
{
|
||||
result[i--] = list_iter->data;
|
||||
}
|
||||
|
||||
g_slist_free (list);
|
||||
g_strfreev (splitted);
|
||||
return g_strdupv(result);
|
||||
}
|
||||
|
||||
|
||||
static XMPSchema *
|
||||
find_xmp_schema_by_iter (XMPModel *xmp_model,
|
||||
GtkTreeIter *child)
|
||||
@@ -516,12 +585,12 @@ parse_set_property (XMPParseContext *context,
|
||||
{
|
||||
XMPModel *xmp_model = user_data;
|
||||
XMPSchema *schema = ns_user_data;
|
||||
XMPType xmptype;
|
||||
int i;
|
||||
XMPProperty *property;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeIter child_iter;
|
||||
gchar *tmp_name;
|
||||
gchar *tmp_value;
|
||||
|
||||
g_return_if_fail (xmp_model != NULL);
|
||||
g_return_if_fail (schema != NULL);
|
||||
@@ -553,35 +622,12 @@ parse_set_property (XMPParseContext *context,
|
||||
#ifdef DEBUG_XMP_MODEL
|
||||
g_print ("\t%s:%s = \"%s\"\n", schema->prefix, name, value[0]);
|
||||
#endif
|
||||
if (property != NULL)
|
||||
/* FIXME */;
|
||||
else
|
||||
{
|
||||
property = g_new (XMPProperty, 1);
|
||||
property->name = g_strdup (name);
|
||||
property->type = XMP_TYPE_TEXT;
|
||||
property->editable = TRUE;
|
||||
xmp_model->custom_properties =
|
||||
g_slist_prepend (xmp_model->custom_properties, property);
|
||||
}
|
||||
gtk_tree_store_append (GTK_TREE_STORE (xmp_model), &child_iter, &iter);
|
||||
gtk_tree_store_set (GTK_TREE_STORE (xmp_model), &child_iter,
|
||||
COL_XMP_NAME, name,
|
||||
COL_XMP_VALUE, value[0],
|
||||
COL_XMP_VALUE_RAW, value,
|
||||
COL_XMP_TYPE_XREF, property,
|
||||
COL_XMP_WIDGET_XREF, NULL,
|
||||
COL_XMP_EDITABLE, property->editable,
|
||||
COL_XMP_EDIT_ICON, NULL,
|
||||
COL_XMP_VISIBLE, TRUE,
|
||||
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
|
||||
COL_XMP_WEIGHT_SET, FALSE,
|
||||
-1);
|
||||
xmp_model_set_property (xmp_model, XMP_TYPE_TEXT, schema->uri, name, value);
|
||||
break;
|
||||
|
||||
case XMP_PTYPE_RESOURCE:
|
||||
#ifdef DEBUG_XMP_MODEL
|
||||
g_print ("\t%s:%s @ = \"%s\"\n", ns_prefix, name,
|
||||
g_print ("\t%s:%s @ = \"%s\"\n", schema->prefix, name,
|
||||
value[0]);
|
||||
#endif
|
||||
if (property != NULL)
|
||||
@@ -614,52 +660,16 @@ parse_set_property (XMPParseContext *context,
|
||||
|
||||
case XMP_PTYPE_ORDERED_LIST:
|
||||
case XMP_PTYPE_UNORDERED_LIST:
|
||||
#ifdef DEBUG_XMP_MODEL
|
||||
g_print ("\t%s:%s [] =", ns_prefix, name);
|
||||
for (i = 0; value[i] != NULL; i++)
|
||||
if (i == 0)
|
||||
g_print (" \"%s\"", value[i]);
|
||||
else
|
||||
g_print (", \"%s\"", value[i]);
|
||||
g_print ("\n");
|
||||
#endif
|
||||
if (property != NULL)
|
||||
/* FIXME */;
|
||||
else
|
||||
{
|
||||
property = g_new (XMPProperty, 1);
|
||||
property->name = g_strdup (name);
|
||||
property->type = ((type == XMP_PTYPE_ORDERED_LIST)
|
||||
? XMP_TYPE_TEXT_BAG
|
||||
: XMP_TYPE_TEXT_SEQ);
|
||||
property->editable = TRUE;
|
||||
xmp_model->custom_properties =
|
||||
g_slist_prepend (xmp_model->custom_properties, property);
|
||||
}
|
||||
|
||||
tmp_name = g_strconcat (name, " []", NULL);
|
||||
tmp_value = g_strjoinv ("; ", (gchar **) value);
|
||||
gtk_tree_store_append (GTK_TREE_STORE (xmp_model), &child_iter, &iter);
|
||||
gtk_tree_store_set (GTK_TREE_STORE (xmp_model), &child_iter,
|
||||
COL_XMP_NAME, tmp_name,
|
||||
COL_XMP_VALUE, tmp_value,
|
||||
COL_XMP_VALUE_RAW, value,
|
||||
COL_XMP_TYPE_XREF, property,
|
||||
COL_XMP_WIDGET_XREF, NULL,
|
||||
COL_XMP_EDITABLE, property->editable,
|
||||
COL_XMP_EDIT_ICON, NULL,
|
||||
COL_XMP_VISIBLE, TRUE,
|
||||
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
|
||||
COL_XMP_WEIGHT_SET, FALSE,
|
||||
-1);
|
||||
g_free (tmp_value);
|
||||
g_free (tmp_name);
|
||||
xmptype = ((type == XMP_PTYPE_ORDERED_LIST)
|
||||
? XMP_TYPE_TEXT_BAG
|
||||
: XMP_TYPE_TEXT_SEQ);
|
||||
xmp_model_set_property (xmp_model, xmptype, schema->uri, name, value);
|
||||
break;
|
||||
|
||||
case XMP_PTYPE_ALT_THUMBS:
|
||||
#ifdef DEBUG_XMP_MODEL
|
||||
for (i = 0; value[i] != NULL; i += 2)
|
||||
g_print ("\t%s:%s [size:%d] = \"...\"\n", ns_prefix, name,
|
||||
g_print ("\t%s:%s [size:%d] = \"...\"\n", schema->prefix, name,
|
||||
*(int *)(value[i]));
|
||||
g_print ("\n");
|
||||
#endif
|
||||
@@ -695,44 +705,16 @@ parse_set_property (XMPParseContext *context,
|
||||
case XMP_PTYPE_ALT_LANG:
|
||||
#ifdef DEBUG_XMP_MODEL
|
||||
for (i = 0; value[i] != NULL; i += 2)
|
||||
g_print ("\t%s:%s [lang:%s] = \"%s\"\n", ns_prefix, name,
|
||||
g_print ("\t%s:%s [lang:%s] = \"%s\"\n", schema->prefix, name,
|
||||
value[i], value[i + 1]);
|
||||
#endif
|
||||
if (property != NULL)
|
||||
/* FIXME */;
|
||||
else
|
||||
{
|
||||
property = g_new (XMPProperty, 1);
|
||||
property->name = g_strdup (name);
|
||||
property->type = XMP_TYPE_LANG_ALT;
|
||||
property->editable = TRUE;
|
||||
xmp_model->custom_properties =
|
||||
g_slist_prepend (xmp_model->custom_properties, property);
|
||||
}
|
||||
for (i = 0; value[i] != NULL; i += 2)
|
||||
{
|
||||
tmp_name = g_strconcat (name, " [", value[i], "]", NULL);
|
||||
gtk_tree_store_append (GTK_TREE_STORE (xmp_model), &child_iter, &iter);
|
||||
gtk_tree_store_set (GTK_TREE_STORE (xmp_model), &child_iter,
|
||||
COL_XMP_NAME, tmp_name,
|
||||
COL_XMP_VALUE, value[i + 1],
|
||||
COL_XMP_VALUE_RAW, value,
|
||||
COL_XMP_TYPE_XREF, property,
|
||||
COL_XMP_WIDGET_XREF, NULL,
|
||||
COL_XMP_EDITABLE, property->editable,
|
||||
COL_XMP_EDIT_ICON, NULL,
|
||||
COL_XMP_VISIBLE, TRUE,
|
||||
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
|
||||
COL_XMP_WEIGHT_SET, FALSE,
|
||||
-1);
|
||||
g_free (tmp_name);
|
||||
}
|
||||
xmp_model_set_property (xmp_model, XMP_TYPE_LANG_ALT, schema->uri, name, value);
|
||||
break;
|
||||
|
||||
case XMP_PTYPE_STRUCTURE:
|
||||
#ifdef DEBUG_XMP_MODEL
|
||||
for (i = 2; value[i] != NULL; i += 2)
|
||||
g_print ("\t%s:%s [%s] = \"%s\"\n", ns_prefix, name,
|
||||
g_print ("\t%s:%s [%s] = \"%s\"\n", schema->prefix, name,
|
||||
value[i], value[i + 1]);
|
||||
#endif
|
||||
if (property != NULL)
|
||||
@@ -768,7 +750,7 @@ parse_set_property (XMPParseContext *context,
|
||||
|
||||
default:
|
||||
#ifdef DEBUG_XMP_MODEL
|
||||
g_print ("\t%s:%s = ?\n", ns_prefix, name);
|
||||
g_print ("\t%s:%s = ?\n", schema->prefix, name);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -858,19 +840,41 @@ xmp_model_parse_file (XMPModel *xmp_model,
|
||||
const gchar *filename,
|
||||
GError **error)
|
||||
{
|
||||
gchar *buffer;
|
||||
gsize buffer_length;
|
||||
int i = 0;
|
||||
GExiv2Metadata *metadata = NULL;
|
||||
gchar **tags = NULL;
|
||||
gchar *tag = NULL;
|
||||
gchar *value = NULL;
|
||||
gchar **val_array = NULL;
|
||||
|
||||
g_return_val_if_fail (filename != NULL, FALSE);
|
||||
|
||||
if (! g_file_get_contents (filename, &buffer, &buffer_length, error))
|
||||
metadata = gexiv2_metadata_new ();
|
||||
if (! gexiv2_metadata_open_path (metadata, filename, error))
|
||||
return FALSE;
|
||||
|
||||
if (! xmp_model_parse_buffer (xmp_model, buffer, buffer_length, TRUE, error))
|
||||
return FALSE;
|
||||
tags = gexiv2_metadata_get_xmp_tags (metadata);
|
||||
|
||||
g_free (buffer);
|
||||
for (i = 0; tags[i] != NULL; i++)
|
||||
{
|
||||
tag = tags[i];
|
||||
|
||||
/* the value is of the form: Xmp.<schema>.<property_name>. Split and
|
||||
* use the last items of the array to set the value.
|
||||
*/
|
||||
value = gexiv2_metadata_get_xmp_tag_string (metadata, tag);
|
||||
val_array = g_strsplit (tag, ".", 0);
|
||||
if (! xmp_model_set_scalar_property (xmp_model,
|
||||
val_array[1],
|
||||
val_array[2],
|
||||
value))
|
||||
g_printerr ("\n Unable to set XMP tag: %s:%s - %s\n",
|
||||
val_array[1], val_array[2], value);
|
||||
}
|
||||
|
||||
g_free (val_array);
|
||||
g_free (value);
|
||||
g_free (tag);
|
||||
g_free (tags);
|
||||
gexiv2_metadata_free (metadata);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -954,6 +958,72 @@ xmp_model_get_scalar_property (XMPModel *xmp_model,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* xmp_model_get_raw_property_value:
|
||||
* @xmp_model: pointer to an #XMPModel
|
||||
* @schema_name: full URI or usual prefix of the schema
|
||||
* @property_name: name of the raw property value to return
|
||||
*
|
||||
* Returns a copy of the currently stored value.
|
||||
*
|
||||
* Return value: raw value or %NULL otherwise
|
||||
**/
|
||||
const gchar **
|
||||
xmp_model_get_raw_property_value (XMPModel *xmp_model,
|
||||
const gchar *schema_name,
|
||||
const gchar *property_name)
|
||||
{
|
||||
XMPSchema *schema;
|
||||
GtkTreeIter iter;
|
||||
XMPProperty *property = NULL;
|
||||
GtkTreeIter child_iter;
|
||||
int i;
|
||||
XMPProperty *property_xref;
|
||||
const gchar **value;
|
||||
|
||||
g_return_val_if_fail (xmp_model != NULL, NULL);
|
||||
g_return_val_if_fail (schema_name != NULL, NULL);
|
||||
g_return_val_if_fail (property_name != NULL, NULL);
|
||||
schema = find_xmp_schema_by_uri (xmp_model, schema_name);
|
||||
if (! schema)
|
||||
schema = find_xmp_schema_prefix (xmp_model, schema_name);
|
||||
|
||||
if (! schema)
|
||||
return NULL;
|
||||
|
||||
if (! find_iter_for_schema (xmp_model, schema, &iter))
|
||||
return NULL;
|
||||
|
||||
if (schema->properties != NULL)
|
||||
for (i = 0; schema->properties[i].name != NULL; ++i)
|
||||
if (! strcmp (schema->properties[i].name, property_name))
|
||||
{
|
||||
property = &(schema->properties[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (property == NULL)
|
||||
return NULL;
|
||||
|
||||
if (! gtk_tree_model_iter_children (GTK_TREE_MODEL (xmp_model),
|
||||
&child_iter, &iter))
|
||||
return NULL;
|
||||
|
||||
do
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (xmp_model), &child_iter,
|
||||
COL_XMP_TYPE_XREF, &property_xref,
|
||||
COL_XMP_VALUE_RAW, &value,
|
||||
-1);
|
||||
if (property_xref == property)
|
||||
return value;
|
||||
}
|
||||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL(xmp_model),
|
||||
&child_iter));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* xmp_model_set_scalar_property:
|
||||
* @xmp_model: pointer to an #XMPModel
|
||||
@@ -962,6 +1032,9 @@ xmp_model_get_scalar_property (XMPModel *xmp_model,
|
||||
* @property_value: value to store
|
||||
*
|
||||
* Store a new value for the specified XMP property.
|
||||
* This is a wrapper function for xmp_model_set_property which sets the
|
||||
* raw values. The @property_value is converted to a raw value and
|
||||
* passed to xmp_model_set_property.
|
||||
*
|
||||
* Return value: %TRUE if the property was set, %FALSE if an error
|
||||
* occurred (for example, the @schema_name is invalid)
|
||||
@@ -972,17 +1045,68 @@ xmp_model_set_scalar_property (XMPModel *xmp_model,
|
||||
const gchar *property_name,
|
||||
const gchar *property_value)
|
||||
{
|
||||
XMPSchema *schema;
|
||||
XMPProperty *property = NULL;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeIter child_iter;
|
||||
int i;
|
||||
gchar **value;
|
||||
gchar **value = NULL;
|
||||
XMPType type;
|
||||
|
||||
g_return_val_if_fail (xmp_model != NULL, FALSE);
|
||||
g_return_val_if_fail (schema_name != NULL, FALSE);
|
||||
g_return_val_if_fail (property_name != NULL, FALSE);
|
||||
g_return_val_if_fail (property_value != NULL, FALSE);
|
||||
|
||||
type = xmp_model_find_xmptype_by (xmp_model, schema_name, property_name);
|
||||
|
||||
/* TODO: Ugh - perhaps this can be done better */
|
||||
if (type == XMP_TYPE_LANG_ALT)
|
||||
value = convert_to_xmp_model_raw_value (property_value);
|
||||
else
|
||||
{
|
||||
value = g_new (gchar *, 2);
|
||||
value[0] = g_strdup (property_value);
|
||||
value[1] = NULL;
|
||||
}
|
||||
|
||||
return xmp_model_set_property (xmp_model,
|
||||
type,
|
||||
schema_name,
|
||||
property_name,
|
||||
(const gchar**) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* xmp_model_set_property:
|
||||
* @xmp_model: pointer to an #XMPModel
|
||||
* @schema_name: full URI or usual prefix of the schema
|
||||
* @type: An #XMPTYPE used in the #XMPProperty
|
||||
* @property_name: name of the property to store
|
||||
* @value: value to store
|
||||
*
|
||||
* Store a new value for the specified XMP property.
|
||||
*
|
||||
* Note: Currently only sets property values for XMP_TYPE_TEXT,
|
||||
* XMP_TYPE_LANG_ALT, XMP_TYPE_TEXT_SEQ, XMP_TYPE_TEXT_BAG.
|
||||
*
|
||||
* Return value: %TRUE if the property was set, %FALSE if an error
|
||||
* occurred (for example, the @schema_name is invalid)
|
||||
**/
|
||||
gboolean
|
||||
xmp_model_set_property (XMPModel *xmp_model,
|
||||
XMPType type,
|
||||
const gchar *schema_name,
|
||||
const gchar *property_name,
|
||||
const gchar **value)
|
||||
{
|
||||
XMPSchema *schema;
|
||||
XMPProperty *property = NULL;
|
||||
gboolean result = FALSE;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeIter child_iter;
|
||||
int i;
|
||||
gchar *tmp_value;
|
||||
|
||||
g_return_val_if_fail (xmp_model != NULL, FALSE);
|
||||
g_return_val_if_fail (schema_name != NULL, FALSE);
|
||||
g_return_val_if_fail (property_name != NULL, FALSE);
|
||||
g_return_val_if_fail (value != NULL, FALSE);
|
||||
schema = find_xmp_schema_by_uri (xmp_model, schema_name);
|
||||
if (! schema)
|
||||
schema = find_xmp_schema_prefix (xmp_model, schema_name);
|
||||
@@ -1002,37 +1126,162 @@ xmp_model_set_scalar_property (XMPModel *xmp_model,
|
||||
}
|
||||
|
||||
if (property != NULL)
|
||||
{
|
||||
find_and_remove_property (xmp_model, property, &iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
property = g_new (XMPProperty, 1);
|
||||
property->name = g_strdup (property_name);
|
||||
property->type = XMP_TYPE_TEXT;
|
||||
property->editable = TRUE;
|
||||
find_and_remove_property (xmp_model, property, &iter);
|
||||
|
||||
xmp_model->custom_properties =
|
||||
g_slist_prepend (xmp_model->custom_properties, property);
|
||||
switch (type)
|
||||
{
|
||||
case XMP_TYPE_TEXT_BAG:
|
||||
case XMP_TYPE_TEXT_SEQ:
|
||||
#ifdef DEBUG_XMP_MODEL
|
||||
g_print ("\t%s:%s [] =", schema->prefix, property_name);
|
||||
for (i = 0; value[i] != NULL; i++)
|
||||
if (i == 0)
|
||||
g_print (" \"%s\"", value[i]);
|
||||
else
|
||||
g_print (", \"%s\"", value[i]);
|
||||
g_print ("\n");
|
||||
#endif
|
||||
if (property != NULL)
|
||||
/* FIXME */;
|
||||
else
|
||||
{
|
||||
property = g_new (XMPProperty, 1);
|
||||
property->name = g_strdup (property_name);
|
||||
property->type = type;
|
||||
property->editable = TRUE;
|
||||
xmp_model->custom_properties =
|
||||
g_slist_prepend (xmp_model->custom_properties, property);
|
||||
}
|
||||
|
||||
tmp_value = g_strjoinv ("; ", (gchar **) value);
|
||||
gtk_tree_store_append (GTK_TREE_STORE (xmp_model), &child_iter, &iter);
|
||||
gtk_tree_store_set (GTK_TREE_STORE (xmp_model), &child_iter,
|
||||
COL_XMP_NAME, property_name,
|
||||
COL_XMP_VALUE, tmp_value,
|
||||
COL_XMP_VALUE_RAW, value,
|
||||
COL_XMP_TYPE_XREF, property,
|
||||
COL_XMP_WIDGET_XREF, NULL,
|
||||
COL_XMP_EDITABLE, property->editable,
|
||||
COL_XMP_EDIT_ICON, NULL,
|
||||
COL_XMP_VISIBLE, TRUE,
|
||||
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
|
||||
COL_XMP_WEIGHT_SET, FALSE,
|
||||
-1);
|
||||
g_free (tmp_value);
|
||||
result = TRUE;
|
||||
break;
|
||||
|
||||
case XMP_TYPE_TEXT:
|
||||
#ifdef DEBUG_XMP_MODEL
|
||||
g_print ("\t%s:%s = \"%s\"\n", schema->prefix, property_name, value[0]);
|
||||
#endif
|
||||
if (property != NULL)
|
||||
/* FIXME */;
|
||||
else
|
||||
{
|
||||
property = g_new (XMPProperty, 1);
|
||||
property->name = g_strdup (property_name);
|
||||
property->type = XMP_TYPE_TEXT;
|
||||
property->editable = TRUE;
|
||||
xmp_model->custom_properties =
|
||||
g_slist_prepend (xmp_model->custom_properties, property);
|
||||
}
|
||||
gtk_tree_store_append (GTK_TREE_STORE (xmp_model), &child_iter, &iter);
|
||||
gtk_tree_store_set (GTK_TREE_STORE (xmp_model), &child_iter,
|
||||
COL_XMP_NAME, property_name,
|
||||
COL_XMP_VALUE, value[0],
|
||||
COL_XMP_VALUE_RAW, value,
|
||||
COL_XMP_TYPE_XREF, property,
|
||||
COL_XMP_WIDGET_XREF, NULL,
|
||||
COL_XMP_EDITABLE, property->editable,
|
||||
COL_XMP_EDIT_ICON, NULL,
|
||||
COL_XMP_VISIBLE, TRUE,
|
||||
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
|
||||
COL_XMP_WEIGHT_SET, FALSE,
|
||||
-1);
|
||||
result = TRUE;
|
||||
break;
|
||||
|
||||
case XMP_TYPE_LANG_ALT:
|
||||
#ifdef DEBUG_XMP_MODEL
|
||||
for (i = 0; value[i] != NULL; i += 2)
|
||||
g_print ("\t%s:%s [lang:%s] = \"%s\"\n", schema->prefix, property_name,
|
||||
value[i], value[i + 1]);
|
||||
#endif
|
||||
if (property != NULL)
|
||||
/* FIXME */;
|
||||
else
|
||||
{
|
||||
property = g_new (XMPProperty, 1);
|
||||
property->name = g_strdup (property_name);
|
||||
property->type = XMP_TYPE_LANG_ALT;
|
||||
property->editable = TRUE;
|
||||
xmp_model->custom_properties =
|
||||
g_slist_prepend (xmp_model->custom_properties, property);
|
||||
}
|
||||
gtk_tree_store_append (GTK_TREE_STORE (xmp_model), &child_iter, &iter);
|
||||
gtk_tree_store_set (GTK_TREE_STORE (xmp_model), &child_iter,
|
||||
COL_XMP_NAME, property_name,
|
||||
COL_XMP_VALUE, value[1],
|
||||
COL_XMP_VALUE_RAW, value,
|
||||
COL_XMP_TYPE_XREF, property,
|
||||
COL_XMP_WIDGET_XREF, NULL,
|
||||
COL_XMP_EDITABLE, property->editable,
|
||||
COL_XMP_EDIT_ICON, NULL,
|
||||
COL_XMP_VISIBLE, TRUE,
|
||||
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
|
||||
COL_XMP_WEIGHT_SET, FALSE,
|
||||
-1);
|
||||
result = TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef DEBUG_XMP_MODEL
|
||||
g_print ("\t%s:%s = ?\n", schema->prefix, property_name);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
value = g_new (gchar *, 2);
|
||||
value[0] = g_strdup (property_value);
|
||||
value[1] = NULL;
|
||||
gtk_tree_store_append (GTK_TREE_STORE (xmp_model), &child_iter, &iter);
|
||||
gtk_tree_store_set (GTK_TREE_STORE (xmp_model), &child_iter,
|
||||
COL_XMP_NAME, g_strdup (property_name),
|
||||
COL_XMP_VALUE, value[0],
|
||||
COL_XMP_VALUE_RAW, value,
|
||||
COL_XMP_TYPE_XREF, property,
|
||||
COL_XMP_WIDGET_XREF, NULL,
|
||||
COL_XMP_EDITABLE, property->editable,
|
||||
COL_XMP_EDIT_ICON, NULL,
|
||||
COL_XMP_VISIBLE, TRUE,
|
||||
COL_XMP_WEIGHT, PANGO_WEIGHT_NORMAL,
|
||||
COL_XMP_WEIGHT_SET, FALSE,
|
||||
-1);
|
||||
return TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* xmp_model_find_xmptype_by:
|
||||
* @xmp_model: An #XMPModel
|
||||
* @schema_name: An #XMPSchema the property belongs to. Can be a schema
|
||||
* URI or prefix.
|
||||
* @property_name: The name of the property to find the type for
|
||||
*
|
||||
* Finds the corresponding XMPType for the given schema and property
|
||||
* name.
|
||||
*
|
||||
* Return value: XMPType or -1 if the property type can not be found.
|
||||
**/
|
||||
XMPType
|
||||
xmp_model_find_xmptype_by (XMPModel *xmp_model,
|
||||
const gchar *schema_name,
|
||||
const gchar *property_name)
|
||||
{
|
||||
XMPSchema *schema;
|
||||
XMPProperty *property = NULL;
|
||||
int i;
|
||||
|
||||
schema = find_xmp_schema_by_uri (xmp_model, schema_name);
|
||||
if (! schema)
|
||||
schema = find_xmp_schema_prefix (xmp_model, schema_name);
|
||||
|
||||
if (schema)
|
||||
{
|
||||
if (schema->properties != NULL)
|
||||
for (i = 0; schema->properties[i].name != NULL; ++i)
|
||||
if (! strcmp (schema->properties[i].name, property_name))
|
||||
{
|
||||
property = &(schema->properties[i]);
|
||||
return property->type;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -105,16 +105,29 @@ gboolean xmp_model_parse_file (XMPModel *xmp_model,
|
||||
|
||||
GtkTreeModel *xmp_model_get_tree_model (XMPModel *xmp_model);
|
||||
|
||||
const gchar **xmp_model_get_raw_property_value (XMPModel *xmp_model,
|
||||
const gchar *schema_name,
|
||||
const gchar *property_name);
|
||||
|
||||
const gchar *xmp_model_get_scalar_property (XMPModel *xmp_model,
|
||||
const gchar *schema_name,
|
||||
const gchar *property_name);
|
||||
|
||||
XMPType xmp_model_find_xmptype_by (XMPModel *xmp_model,
|
||||
const gchar *schema_name,
|
||||
const gchar *property_name);
|
||||
|
||||
gboolean xmp_model_set_property (XMPModel *xmp_model,
|
||||
XMPType type,
|
||||
const gchar *schema_name,
|
||||
const gchar *property_name,
|
||||
const gchar **value);
|
||||
|
||||
gboolean xmp_model_set_scalar_property (XMPModel *xmp_model,
|
||||
const gchar *schema_name,
|
||||
const gchar *property_name,
|
||||
const gchar *property_value);
|
||||
|
||||
|
||||
/* Signals */
|
||||
void xmp_model_property_changed (XMPModel *xmp_model,
|
||||
XMPSchema *schema,
|
||||
|
@@ -617,10 +617,10 @@ start_element_handler (GMarkupParseContext *markup_context,
|
||||
{
|
||||
case STATE_INSIDE_XPACKET:
|
||||
if (! strcmp (element_name, "x:xmpmeta")
|
||||
|| ! strcmp (element_name, "x:xapmeta")
|
||||
|| ! strcmp (element_name, "x:xapmeta")
|
||||
|| matches_with_prefix (element_name, context->xmp_prefix,
|
||||
context->xmp_prefix_len, "xmpmeta"))
|
||||
context->state = STATE_INSIDE_XMPMETA;
|
||||
context->state = STATE_INSIDE_XMPMETA;
|
||||
else if (matches_rdf (element_name, context, "RDF"))
|
||||
{
|
||||
/* the x:xmpmeta element is missing, but this is allowed */
|
||||
@@ -628,33 +628,33 @@ start_element_handler (GMarkupParseContext *markup_context,
|
||||
context->state = STATE_INSIDE_RDF;
|
||||
}
|
||||
else
|
||||
parse_error_element (context, error, "x:xmpmeta",
|
||||
parse_error_element (context, error, "x:xmpmeta",
|
||||
FALSE, element_name);
|
||||
break;
|
||||
|
||||
case STATE_INSIDE_XMPMETA:
|
||||
if (matches_rdf (element_name, context, "RDF"))
|
||||
context->state = STATE_INSIDE_RDF;
|
||||
context->state = STATE_INSIDE_RDF;
|
||||
else
|
||||
parse_error_element (context, error, "rdf:RDF",
|
||||
parse_error_element (context, error, "rdf:RDF",
|
||||
FALSE, element_name);
|
||||
break;
|
||||
|
||||
case STATE_INSIDE_RDF:
|
||||
if (matches_rdf (element_name, context, "Description"))
|
||||
{
|
||||
{
|
||||
XMLNameSpace *ns;
|
||||
gboolean about_seen = FALSE;
|
||||
|
||||
context->state = STATE_INSIDE_TOPLEVEL_DESC;
|
||||
for (attr = 0; attribute_names[attr] != NULL; ++attr)
|
||||
{
|
||||
context->state = STATE_INSIDE_TOPLEVEL_DESC;
|
||||
for (attr = 0; attribute_names[attr] != NULL; ++attr)
|
||||
{
|
||||
if (matches_rdf (attribute_names[attr], context, "about")
|
||||
|| ! strcmp (attribute_names[attr], "about") /* old style */)
|
||||
about_seen = TRUE;
|
||||
else if (g_str_has_prefix (attribute_names[attr], "xmlns"))
|
||||
else if (g_str_has_prefix (attribute_names[attr], "xmlns"))
|
||||
; /* the namespace has already been pushed on the stack */
|
||||
else
|
||||
else
|
||||
{
|
||||
ns = new_property_in_ns (context, attribute_names[attr]);
|
||||
if (ns != NULL)
|
||||
@@ -675,9 +675,9 @@ start_element_handler (GMarkupParseContext *markup_context,
|
||||
parse_error (context, error, XMP_ERROR_MISSING_ABOUT,
|
||||
_("Required attribute rdf:about missing in <%s>"),
|
||||
element_name);
|
||||
}
|
||||
}
|
||||
else
|
||||
parse_error_element (context, error, "rdf:Description",
|
||||
parse_error_element (context, error, "rdf:Description",
|
||||
FALSE, element_name);
|
||||
break;
|
||||
|
||||
@@ -714,37 +714,37 @@ start_element_handler (GMarkupParseContext *markup_context,
|
||||
|
||||
case STATE_INSIDE_PROPERTY:
|
||||
if (matches_rdf (element_name, context, "Description"))
|
||||
{
|
||||
{
|
||||
context->saved_state = context->state;
|
||||
context->state = STATE_INSIDE_QDESC;
|
||||
for (attr = 0; attribute_names[attr] != NULL; ++attr)
|
||||
{
|
||||
if (g_str_has_prefix (attribute_names[attr], "xmlns"))
|
||||
{
|
||||
/* this desc. is a structure, not a property qualifier */
|
||||
context->state = STATE_INSIDE_STRUCT_ADD_NS;
|
||||
}
|
||||
else
|
||||
context->state = STATE_INSIDE_QDESC;
|
||||
for (attr = 0; attribute_names[attr] != NULL; ++attr)
|
||||
{
|
||||
if (g_str_has_prefix (attribute_names[attr], "xmlns"))
|
||||
{
|
||||
/* this desc. is a structure, not a property qualifier */
|
||||
context->state = STATE_INSIDE_STRUCT_ADD_NS;
|
||||
}
|
||||
else
|
||||
unknown_attribute (context, error, element_name,
|
||||
attribute_names[attr],
|
||||
attribute_values[attr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (matches_rdf (element_name, context, "Alt"))
|
||||
context->state = STATE_INSIDE_ALT;
|
||||
context->state = STATE_INSIDE_ALT;
|
||||
else if (matches_rdf (element_name, context, "Bag"))
|
||||
context->state = STATE_INSIDE_BAG;
|
||||
context->state = STATE_INSIDE_BAG;
|
||||
else if (matches_rdf (element_name, context, "Seq"))
|
||||
context->state = STATE_INSIDE_SEQ;
|
||||
context->state = STATE_INSIDE_SEQ;
|
||||
else
|
||||
unknown_element (context, error, element_name);
|
||||
break;
|
||||
|
||||
case STATE_INSIDE_QDESC:
|
||||
if (matches_rdf (element_name, context, "value"))
|
||||
context->state = STATE_INSIDE_QDESC_VALUE;
|
||||
context->state = STATE_INSIDE_QDESC_VALUE;
|
||||
else
|
||||
context->state = STATE_INSIDE_QDESC_QUAL;
|
||||
context->state = STATE_INSIDE_QDESC_QUAL;
|
||||
break;
|
||||
|
||||
case STATE_INSIDE_STRUCT_ADD_NS:
|
||||
@@ -788,22 +788,22 @@ start_element_handler (GMarkupParseContext *markup_context,
|
||||
|
||||
case STATE_INSIDE_ALT:
|
||||
if (matches_rdf (element_name, context, "li"))
|
||||
{
|
||||
context->state = STATE_INSIDE_ALT_LI;
|
||||
for (attr = 0; attribute_names[attr] != NULL; ++attr)
|
||||
{
|
||||
if (matches_rdf (attribute_names[attr], context, "parseType")
|
||||
{
|
||||
context->state = STATE_INSIDE_ALT_LI;
|
||||
for (attr = 0; attribute_names[attr] != NULL; ++attr)
|
||||
{
|
||||
if (matches_rdf (attribute_names[attr], context, "parseType")
|
||||
&& ! strcmp (attribute_values[attr], "Resource"))
|
||||
context->state = STATE_INSIDE_ALT_LI_RSC;
|
||||
else if (! strcmp (attribute_names[attr], "xml:lang"))
|
||||
else if (! strcmp (attribute_names[attr], "xml:lang"))
|
||||
add_property_value (context, XMP_PTYPE_ALT_LANG,
|
||||
g_strdup (attribute_values[attr]),
|
||||
NULL);
|
||||
else
|
||||
else
|
||||
unknown_attribute (context, error, element_name,
|
||||
attribute_names[attr],
|
||||
attribute_values[attr]);
|
||||
}
|
||||
}
|
||||
/* rdf:Alt is not an ordered list, but some broken XMP files use */
|
||||
/* it instead of rdf:Seq. Workaround: if we did not find some */
|
||||
/* attributes for the valid cases ALT_LANG or ALT_LI_RSC, then */
|
||||
@@ -812,55 +812,55 @@ start_element_handler (GMarkupParseContext *markup_context,
|
||||
&& (context->state != STATE_INSIDE_ALT_LI_RSC))
|
||||
add_property_value (context, XMP_PTYPE_ORDERED_LIST,
|
||||
NULL, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
parse_error_element (context, error, "rdf:li",
|
||||
parse_error_element (context, error, "rdf:li",
|
||||
FALSE, element_name);
|
||||
break;
|
||||
|
||||
case STATE_INSIDE_BAG:
|
||||
if (matches_rdf (element_name, context, "li"))
|
||||
{
|
||||
{
|
||||
context->state = STATE_INSIDE_BAG_LI;
|
||||
for (attr = 0; attribute_names[attr] != NULL; ++attr)
|
||||
{
|
||||
if (matches_rdf (attribute_names[attr], context, "parseType")
|
||||
for (attr = 0; attribute_names[attr] != NULL; ++attr)
|
||||
{
|
||||
if (matches_rdf (attribute_names[attr], context, "parseType")
|
||||
&& ! strcmp (attribute_values[attr], "Resource"))
|
||||
context->state = STATE_INSIDE_BAG_LI_RSC;
|
||||
else
|
||||
else
|
||||
unknown_attribute (context, error, element_name,
|
||||
attribute_names[attr],
|
||||
attribute_values[attr]);
|
||||
}
|
||||
}
|
||||
if (context->state != STATE_INSIDE_BAG_LI_RSC)
|
||||
add_property_value (context, XMP_PTYPE_UNORDERED_LIST,
|
||||
NULL, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
parse_error_element (context, error, "rdf:li",
|
||||
parse_error_element (context, error, "rdf:li",
|
||||
FALSE, element_name);
|
||||
break;
|
||||
|
||||
case STATE_INSIDE_SEQ:
|
||||
if (matches_rdf (element_name, context, "li"))
|
||||
{
|
||||
{
|
||||
context->state = STATE_INSIDE_SEQ_LI;
|
||||
for (attr = 0; attribute_names[attr] != NULL; ++attr)
|
||||
{
|
||||
if (matches_rdf (attribute_names[attr], context, "parseType")
|
||||
for (attr = 0; attribute_names[attr] != NULL; ++attr)
|
||||
{
|
||||
if (matches_rdf (attribute_names[attr], context, "parseType")
|
||||
&& ! strcmp (attribute_values[attr], "Resource"))
|
||||
context->state = STATE_INSIDE_SEQ_LI_RSC;
|
||||
else
|
||||
else
|
||||
unknown_attribute (context, error, element_name,
|
||||
attribute_names[attr],
|
||||
attribute_values[attr]);
|
||||
}
|
||||
}
|
||||
if (context->state != STATE_INSIDE_SEQ_LI_RSC)
|
||||
add_property_value (context, XMP_PTYPE_ORDERED_LIST,
|
||||
NULL, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
parse_error_element (context, error, "rdf:li",
|
||||
parse_error_element (context, error, "rdf:li",
|
||||
FALSE, element_name);
|
||||
break;
|
||||
|
||||
@@ -872,7 +872,7 @@ start_element_handler (GMarkupParseContext *markup_context,
|
||||
context->state = STATE_INSIDE_QDESC;
|
||||
}
|
||||
else
|
||||
parse_error_element (context, error, "rdf:Description",
|
||||
parse_error_element (context, error, "rdf:Description",
|
||||
TRUE, element_name);
|
||||
break;
|
||||
|
||||
@@ -1028,7 +1028,7 @@ end_element_handler (GMarkupParseContext *markup_context,
|
||||
|
||||
default:
|
||||
parse_error (context, error, XMP_ERROR_PARSE,
|
||||
_("End of element <%s> not expected in this context"),
|
||||
_("End of element <%s> not expected in this context"),
|
||||
element_name);
|
||||
break;
|
||||
}
|
||||
@@ -1123,8 +1123,8 @@ text_handler (GMarkupParseContext *markup_context,
|
||||
case STATE_INSIDE_QDESC_QUAL:
|
||||
#ifdef DEBUG_XMP_PARSER
|
||||
g_print ("ignoring qualifier for part of \"%s\"[]: \"%.*s\"\n",
|
||||
context->property,
|
||||
(int)text_len, text);
|
||||
context->property,
|
||||
(int)text_len, text);
|
||||
#endif
|
||||
/* FIXME: notify the user? add a way to collect qualifiers? */
|
||||
break;
|
||||
@@ -1135,8 +1135,8 @@ text_handler (GMarkupParseContext *markup_context,
|
||||
|
||||
default:
|
||||
if (! is_whitespace_string (text))
|
||||
parse_error (context, error, XMP_ERROR_INVALID_CONTENT,
|
||||
_("The current element (<%s>) cannot contain text"),
|
||||
parse_error (context, error, XMP_ERROR_INVALID_CONTENT,
|
||||
_("The current element (<%s>) cannot contain text"),
|
||||
g_markup_parse_context_get_element (markup_context));
|
||||
break;
|
||||
}
|
||||
@@ -1158,9 +1158,9 @@ passthrough_handler (GMarkupParseContext *markup_context,
|
||||
case STATE_AFTER_XPACKET:
|
||||
if ((text_len >= 21)
|
||||
&& (! strncmp (passthrough_text, "<?xpacket begin=", 16)))
|
||||
context->state = STATE_INSIDE_XPACKET;
|
||||
context->state = STATE_INSIDE_XPACKET;
|
||||
else
|
||||
parse_error (context, error, XMP_ERROR_PARSE,
|
||||
parse_error (context, error, XMP_ERROR_PARSE,
|
||||
_("XMP packets must start with <?xpacket begin=...?>"));
|
||||
break;
|
||||
|
||||
@@ -1172,9 +1172,9 @@ passthrough_handler (GMarkupParseContext *markup_context,
|
||||
case STATE_AFTER_XMPMETA:
|
||||
if ((text_len >= 19)
|
||||
&& (! strncmp (passthrough_text, "<?xpacket end=", 14)))
|
||||
context->state = STATE_AFTER_XPACKET;
|
||||
context->state = STATE_AFTER_XPACKET;
|
||||
else
|
||||
parse_error (context, error, XMP_ERROR_PARSE,
|
||||
parse_error (context, error, XMP_ERROR_PARSE,
|
||||
_("XMP packets must end with <?xpacket end=...?>"));
|
||||
break;
|
||||
|
||||
|
Reference in New Issue
Block a user