Fixed crash occuring after a double free of some structured XMP

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

	* plug-ins/metadata/xmp-model.[ch]: Fixed crash occuring after a
	double free of some structured XMP properties.  Added new type
	XMP_TYPE_GENERIC_STRUCTURE for unknown structured properties.  Be
	more tolerant in parsing incorrectly written schema URIs and try
	to extract a valid URI from them.  Converted to use g_print().
	Moved the definitions of standard XMP schemas from here...

	* plug-ins/metadata/xmp-schemas.[ch]: ...to these new files.

	* plug-ins/metadata/xmp-encode.[ch]: Rewritten using GString
	instead of fixed buffers.

	* plug-ins/metadata/metadata.c
	* plug-ins/metadata/interface.c: Adapted to the new function
	xmp_generate_block() using GString.

	* plug-ins/metadata/base64.c (base64_encode): Fixed incorrect
	encoding of bytes with the sign bit set.

	* plug-ins/metadata/testbase64.c
	* plug-ins/metadata/Makefile.am: Added xmp-schema.[ch] and test
	program testbase64.c (testing base64 encoding and decoding).

	* plug-ins/metadata/xmpdump.c: Converted to use g_print().

	* plug-ins/metadata/xmp-parse.c: Added some #ifdef's around
	debugging code, added more comments.
This commit is contained in:
Raphael Quinet 2005-04-22 15:04:29 +00:00 committed by Raphaël Quinet
parent ee1cac1475
commit c30453f49d
14 changed files with 937 additions and 745 deletions

View File

@ -1,3 +1,33 @@
2005-04-22 Raphaël Quinet <raphael@gimp.org>
* plug-ins/metadata/xmp-model.[ch]: Fixed crash occuring after a
double free of some structured XMP properties. Added new type
XMP_TYPE_GENERIC_STRUCTURE for unknown structured properties. Be
more tolerant in parsing incorrectly written schema URIs and try
to extract a valid URI from them. Converted to use g_print().
Moved the definitions of standard XMP schemas from here...
* plug-ins/metadata/xmp-schemas.[ch]: ...to these new files.
* plug-ins/metadata/xmp-encode.[ch]: Rewritten using GString
instead of fixed buffers.
* plug-ins/metadata/metadata.c
* plug-ins/metadata/interface.c: Adapted to the new function
xmp_generate_block() using GString.
* plug-ins/metadata/base64.c (base64_encode): Fixed incorrect
encoding of bytes with the sign bit set.
* plug-ins/metadata/testbase64.c
* plug-ins/metadata/Makefile.am: Added xmp-schema.[ch] and test
program testbase64.c (testing base64 encoding and decoding).
* plug-ins/metadata/xmpdump.c: Converted to use g_print().
* plug-ins/metadata/xmp-parse.c: Added some #ifdef's around
debugging code, added more comments.
2005-04-22 Sven Neumann <sven@gimp.org>
* libgimpbase/gimpbaseenums.h (GimpTransformDirection): removed

View File

@ -20,14 +20,16 @@ metadata_SOURCES = \
metadata.c \
interface.h \
interface.c \
base64.h \
base64.c \
xmp-model.h \
xmp-model.c \
xmp-parse.h \
xmp-parse.c \
xmp-encode.h \
xmp-encode.c \
base64.h \
base64.c
xmp-schemas.h \
xmp-schemas.c
# exif-decode.h \
# exif-decode.c \
# exif-encode.h \
@ -38,10 +40,10 @@ metadata_SOURCES = \
noinst_PROGRAMS = xmpdump
xmpdump_SOURCES = \
xmpdump.c \
xmp-parse.h \
xmp-parse.c \
base64.h \
base64.c
base64.c \
xmp-parse.h \
xmp-parse.c
INCLUDES = \
-I$(top_srcdir) \
@ -58,3 +60,13 @@ LDADD = \
$(GTK_LIBS) \
$(RT_LIBS) \
$(INTLLIBS)
# test program, not built by default
TESTS = testbase64$(EXEEXT)
EXTRA_PROGRAMS = testbase64
testbase64_SOURCES = \
base64.h \
base64.c \
testbase64.c

View File

@ -1,4 +1,4 @@
/* base64.h - encode and decode base64 encoding according to RFC 1521
/* base64.h - encode and decode base64 encoding according to RFC 2045
*
* Copyright (C) 2005, Raphaël Quinet <raphael@gimp.org>
*
@ -24,7 +24,7 @@
* ignore whitespace (especially those written for HTTP usage) and the
* rest were not compatible with the LGPL (some were GPL, not LGPL).
* Or at least I haven't been able to find LGPL implementations.
* Writing this according to RFC 1521 did not take long anyway.
* Writing this according to RFC 2045 did not take long anyway.
*/
#ifndef WITHOUT_GIMP
@ -86,7 +86,7 @@ static const gint base64_6bits[256] =
* specified correctly. The decoder will stop at the first nul byte
* or at the first '=' (padding byte) so you should ensure that one of
* these is present if you supply -1 for @src_size. For more details
* about the base64 encoding, see RFC 1521.
* about the base64 encoding, see RFC 2045, chapter 6.8.
*
* Returns: the number of bytes stored in @dest, or -1 if invalid data was found.
*/
@ -160,14 +160,11 @@ base64_decode (const gchar *src_b64,
* Since the base64 encoding uses 4 bytes for every 3 bytes of input,
* @dest_size should be at least 4/3 of @src_size, plus optional line
* breaks if @columns > 0 and up to two padding bytes at the end. For
* more details about the base64 encoding, see RFC 1521.
* more details about the base64 encoding, see RFC 2045, chapter 6.8.
* Note that RFC 2045 recommends setting @columns to 76.
*
* Returns: the number of bytes stored in @dest.
*/
/*
* FIXME: docs!
* if columns <= 0, no line breaks
*/
gssize
base64_encode (const gchar *src,
gsize src_size,
@ -175,10 +172,10 @@ base64_encode (const gchar *src,
gsize dest_size,
gint columns)
{
gint32 bits;
gssize i;
gint n;
gint c;
guint32 bits;
gssize i;
gint n;
gint c;
g_return_val_if_fail (src != NULL, -1);
g_return_val_if_fail (dest_b64 != NULL, -1);
@ -188,7 +185,7 @@ base64_encode (const gchar *src,
c = 0;
for (i = 0; (src_size != 0) && (i + 4 <= dest_size); src++, src_size--)
{
bits += *src;
bits += *(guchar *)src;
if (++n == 3)
{
dest_b64[i++] = base64_code[(bits >> 18) & 0x3f];

View File

@ -50,7 +50,7 @@
#include "libgimp/stdplugins-intl.h"
#include "interface.h"
#include "xmp-model.h"
#include "xmp-schemas.h"
#include "xmp-encode.h"
@ -532,25 +532,21 @@ export_dialog_response (GtkWidget *dlg,
if (response_id == GTK_RESPONSE_OK)
{
gchar *filename;
gchar *buffer;
gssize buffer_length;
int fd;
GString *buffer;
gchar *filename;
int fd;
buffer = g_string_new (NULL);
xmp_generate_packet (mgui->xmp_model, buffer);
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_string_free (buffer, TRUE);
g_free (filename);
return;
}
@ -560,12 +556,12 @@ export_dialog_response (GtkWidget *dlg,
strlen (buffer), filename);
*/
if (write (fd, buffer, strlen (buffer)) < 0)
if (write (fd, buffer->str, buffer->len) < 0)
{
metadata_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dlg),
_("Save failed"),
_("Some error occurred while saving"));
g_free (buffer);
g_string_free (buffer, TRUE);
g_free (filename);
return;
}
@ -575,12 +571,12 @@ export_dialog_response (GtkWidget *dlg,
metadata_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dlg),
_("Save failed"),
_("Could not close the file"));
g_free (buffer);
g_string_free (buffer, TRUE);
g_free (filename);
return;
}
g_free (buffer);
g_string_free (buffer, TRUE);
g_free (filename);
}

View File

@ -526,29 +526,24 @@ run (const gchar *name,
if (status == GIMP_PDB_SUCCESS)
{
gchar *buffer;
gssize buffer_size;
gssize used_size;
GString *buffer;
/* 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);
buffer = g_string_new (METADATA_MARKER);
xmp_generate_packet (xmp_model, buffer);
parasite = gimp_parasite_new (METADATA_PARASITE,
GIMP_PARASITE_PERSISTENT,
used_size + METADATA_MARKER_LEN,
(gpointer) buffer);
buffer->len,
(gpointer) buffer->str);
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);
values[1].data.d_string = g_strdup (buffer->str
+ METADATA_MARKER_LEN);
}
g_free (buffer);
g_string_free (buffer, TRUE);
xmp_model_free (xmp_model);
}

View File

@ -0,0 +1,117 @@
/* Small test program to test the base64 encoding and decoding */
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include "base64.h"
static int
string_encode_decode (char *s)
{
int n;
char encoded[300];
char decoded[400];
n = base64_encode (s, strlen (s), encoded, sizeof (encoded) - 1, 0);
if (n < 0)
{
g_print ("base64 encoding failed for '%s'", s);
return 1;
}
g_print ("'%s' -> '%s' (%d) ", s, encoded, n);
n = base64_decode (encoded, strlen (encoded), decoded, sizeof (decoded) - 1);
if (n < 0)
{
g_print ("\nbase64 decoding failed for '%s'", s);
return 1;
}
if (! strcmp (s, decoded))
g_print ("-> '%s' (%d)\n", decoded, n);
else
{
g_print ("-> '%s' (%d) MISMATCH!\n", decoded, n);
g_print ("decoded buffer does not match original!\n");
return 1;
}
return 0;
}
static int
buffer_encode_decode (char *buf,
gint buf_len,
gint columns)
{
int n;
char encoded[3000];
char decoded[4000];
n = base64_encode (buf, buf_len, encoded, sizeof (encoded) - 1, columns);
if (n < 0)
{
g_print ("base64 encoding failed");
return 1;
}
g_print ("buffer length %d -> encoded %d (columns: %d) ", buf_len, n,
columns);
n = base64_decode (encoded, strlen (encoded), decoded, sizeof (decoded) - 1);
if (n < 0)
{
g_print ("\nbase64 decoding failed");
return 1;
}
if ((n == buf_len) && ! memcmp (buf, decoded, buf_len))
g_print ("-> decoded %d match OK\n", n);
else
{
g_print ("-> decoded %d MISMATCH!\n", n);
g_print ("decoded buffer does not match original!\n");
return 1;
}
return 0;
}
int
main (int argc,
char *argv[])
{
int failed = 0;
int i;
char buf[1000];
g_print ("Testing base64 encoding ...\n");
failed += string_encode_decode ("");
failed += string_encode_decode ("A");
failed += string_encode_decode ("AB");
failed += string_encode_decode ("ABC");
failed += string_encode_decode ("ABCD");
failed += string_encode_decode ("ABCDE");
failed += string_encode_decode ("ABCDEF");
failed += string_encode_decode ("ABCDEFG");
failed += string_encode_decode ("ABCDEFGH");
failed += string_encode_decode ("ABCDEFGHI");
failed += string_encode_decode ("abcdefghik");
failed += string_encode_decode ("1234567890abcdefghijklmnopqrstuvwxyz");
failed += string_encode_decode ("«© Raphaël»");
for (i = 0; i < sizeof (buf); i++)
buf[i] = (char) (i % 0xff);
failed += buffer_encode_decode (buf, sizeof (buf), 0);
failed += buffer_encode_decode (buf, sizeof (buf), 76);
failed += buffer_encode_decode (buf, sizeof (buf), 4);
failed += buffer_encode_decode (buf, sizeof (buf), 1);
for (i = 0; i < sizeof (buf); i++)
buf[i] = (char) (0xff - (i % 0xff));
failed += buffer_encode_decode (buf, 600, 0);
failed += buffer_encode_decode (buf, 500, 0);
failed += buffer_encode_decode (buf, 400, 0);
if (failed > 0)
{
g_print ("%d test(s) failed!\n", failed);
return EXIT_FAILURE;
}
g_print ("No problems detected.\n");
return EXIT_SUCCESS;
}

View File

@ -1,4 +1,4 @@
/* xmp-gen.c - generate XMP metadata from the tree model
/* xmp-encode.c - generate XMP metadata from the tree model
*
* Copyright (C) 2005, Raphaël Quinet <raphael@gimp.org>
*
@ -30,36 +30,30 @@
#include "libgimp/stdplugins-intl.h"
#include "xmp-encode.h"
#include "xmp-model.h"
#include "xmp-schemas.h"
static gssize
size_schema (GtkTreeModel *model,
GtkTreeIter *iter,
const XMPSchema **schema_r)
static void
gen_schema_start (GString *buffer,
const XMPSchema *schema)
{
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);
g_string_append_printf (buffer, " <rdf:Description xmlns:%s='%s'>\n",
schema->prefix, schema->uri);
}
static gssize
size_property (GtkTreeModel *model,
GtkTreeIter *iter,
const XMPSchema *schema)
static void
gen_schema_end (GString *buffer)
{
const XMPProperty *property;
const gchar **value_array;
gssize length;
gint i;
g_string_append (buffer, " </rdf:Description>\n\n");
}
gtk_tree_model_get (model, iter,
COL_XMP_TYPE_XREF, &property,
COL_XMP_VALUE_RAW, &value_array,
-1);
static void
gen_property (GString *buffer,
const XMPSchema *schema,
const XMPProperty *property,
const gchar **value_array)
{
gint i;
const gchar *ns_prefix;
switch (property->type)
{
@ -70,262 +64,162 @@ size_property (GtkTreeModel *model,
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]));
g_string_append_printf (buffer, " <%s:%s>%s</%s:%s>\n",
schema->prefix, property->name,
value_array[0],
schema->prefix, property->name);
break;
case XMP_TYPE_LOCALE_BAG:
case XMP_TYPE_TEXT_BAG:
case XMP_TYPE_XPATH_BAG:
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++)
g_string_append_printf (buffer, " <rdf:li>%s</rdf:li>\n",
value_array[i]);
g_string_append_printf (buffer, " </rdf:Bag>\n </%s:%s>\n",
schema->prefix, property->name);
break;
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));
g_string_append_printf (buffer, " <%s:%s>\n <rdf:Seq>\n",
schema->prefix, 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;
g_string_append_printf (buffer, " <rdf:li>%s</rdf:li>\n",
value_array[i]);
g_string_append_printf (buffer, " </rdf:Seq>\n </%s:%s>\n",
schema->prefix, property->name);
break;
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));
g_string_append_printf (buffer, " <%s:%s>\n <rdf:Alt>\n",
schema->prefix, 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;
g_string_append_printf (buffer,
" <rdf:li xml:lang='%s'>%s</rdf:li>\n",
value_array[i], value_array[i + 1]);
g_string_append_printf (buffer, " </rdf:Alt>\n </%s:%s>\n",
schema->prefix, property->name);
break;
case XMP_TYPE_URI:
return (sizeof (" <%s:%s rdf:resource='%s' />\n") - 7
+ strlen (schema->prefix)
+ strlen (property->name)
+ strlen (value_array[0]));
g_string_append_printf (buffer, " <%s:%s rdf:resource='%s' />\n",
schema->prefix, property->name, value_array[0]);
break;
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
case XMP_TYPE_GENERIC_STRUCTURE:
if (value_array[0] && value_array[1]
&& !! strcmp (value_array[1], schema->uri))
{
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));
}
g_string_append_printf (buffer,
" <%s:%s rdf:parseType='Resource'\n"
" xmlns:%s='%s'>\n",
schema->prefix, property->name,
value_array[0], value_array[1]);
ns_prefix = value_array[0];
}
while (gtk_tree_model_iter_next (model, &iter));
}
return buffer_size;
}
else
{
g_string_append_printf (buffer,
" <%s:%s rdf:parseType='Resource'>\n",
schema->prefix, property->name);
ns_prefix = schema->prefix;
}
for (i = 2; value_array[i] != NULL; i += 2)
g_string_append_printf (buffer, " <%s:%s>%s</%s:%s>\n",
ns_prefix, value_array[i],
value_array[i + 1],
ns_prefix, value_array[i]);
g_string_append_printf (buffer, " </%s:%s>\n",
schema->prefix, property->name);
break;
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);
g_warning ("FIXME: output not implemented yet (%s:%s)",
schema->prefix, 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
* @buffer: A #GString in which the generated XMP packet will be stored.
*
* Generate XMP block from xmp_model.
* Generate XMP packet from xmp_model and store it in the supplied buffer.
*
* 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)
void
xmp_generate_packet (XMPModel *xmp_model,
GString *buffer)
{
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeIter child;
gssize n;
const XMPSchema *schema;
gpointer saved_ref;
g_return_if_fail (xmp_model != NULL);
g_return_if_fail (buffer != NULL);
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 */
g_return_if_fail (model != NULL);
if (! buffer)
buffer = g_string_new (NULL);
buffer = g_string_append (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");
/* generate the contents of the XMP packet */
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter))
{
do
{
n += gen_schema_start (model, &iter, buffer + n, &schema);
gtk_tree_model_get (model, &iter,
COL_XMP_TYPE_XREF, &schema,
-1);
gen_schema_start (buffer, schema);
if (gtk_tree_model_iter_children (model, &child, &iter))
{
saved_ref = NULL;
do
{
n += gen_property (model, &child, buffer + n, schema);
const XMPProperty *property;
const gchar **value_array;
gtk_tree_model_get (model, &child,
COL_XMP_TYPE_XREF, &property,
COL_XMP_VALUE_RAW, &value_array,
-1);
/* do not process structured types multiple times */
if (saved_ref != value_array)
{
saved_ref = value_array;
g_return_if_fail (property->name != NULL);
gen_property (buffer, schema, property, value_array);
}
}
while (gtk_tree_model_iter_next (model, &child));
}
n += gen_schema_end (model, &iter, buffer + n);
gen_schema_end (buffer);
}
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;
g_string_append (buffer, "</rdf:RDF>\n</x:xmpmeta>\n<?xpacket end='r'?>\n");
}

View File

@ -1,4 +1,4 @@
/* xmp-gen.h - generate XMP metadata from the tree model
/* xmp-encode.h - generate XMP metadata from the tree model
*
* Copyright (C) 2005, Raphaël Quinet <raphael@gimp.org>
*
@ -17,20 +17,17 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef XMP_GEN_H
#define XMP_GEN_H
#ifndef XMP_ENCODE_H
#define XMP_ENCODE_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);
void xmp_generate_packet (XMPModel *xmp_model,
GString *buffer);
G_END_DECLS
#endif /* XMP_GEN_H */
#endif /* XMP_ENCODE_H */

View File

@ -21,9 +21,6 @@
#include "config.h"
#include <string.h>
#ifdef DEBUG_XMP_PARSER
# include <stdio.h>
#endif
#include <gtk/gtk.h>
#include <libgimp/gimp.h>
@ -31,6 +28,7 @@
#include "libgimp/stdplugins-intl.h"
#include "xmp-schemas.h"
#include "xmp-parse.h"
#include "xmp-model.h"
@ -40,11 +38,12 @@
* children of the schemas are the XMP properties.
*
* If the XMP file contains a schema that is not part of the XMP
* specification, it will be included in the custom_schemas list and
* the corresponding element in the tree will get a reference to that
* list element instead of a reference to one of the static schema
* definitions included below. Same for custom properties inside a
* known or custom schema.
* specification or a known extension (e.g., IPTC Core), it will be
* included in the custom_schemas list and the corresponding element
* in the tree will get a reference to that list element instead of a
* reference to one of the static schema definitions found in
* xmp-schemas.c. Same for custom properties inside a known or custom
* schema.
*/
struct _XMPModel
{
@ -52,272 +51,8 @@ struct _XMPModel
GSList *custom_schemas;
GSList *custom_properties;
XMPSchema *current_schema;
GtkTreeIter current_schema_iter;
};
static XMPProperty dc_properties[] =
{
{ "contributor", XMP_TYPE_TEXT, TRUE },
{ "coverage", XMP_TYPE_TEXT, TRUE },
{ "creator", XMP_TYPE_TEXT_SEQ, TRUE },
{ "date", XMP_TYPE_DATE, TRUE },
{ "description", XMP_TYPE_LANG_ALT, TRUE },
{ "format", XMP_TYPE_MIME_TYPE, XMP_AUTO_UPDATE },
{ "identifier", XMP_TYPE_TEXT, TRUE }, /*xmp:Identifier*/
{ "language", XMP_TYPE_LOCALE_BAG, FALSE },
{ "publisher", XMP_TYPE_TEXT_BAG, TRUE },
{ "relation", XMP_TYPE_TEXT_BAG, TRUE },
{ "rights", XMP_TYPE_LANG_ALT, TRUE },
{ "source", XMP_TYPE_TEXT, TRUE },
{ "subject", XMP_TYPE_TEXT_BAG, TRUE },
{ "title", XMP_TYPE_LANG_ALT, TRUE },
{ "type", XMP_TYPE_TEXT_BAG, TRUE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty xmp_properties[] =
{
{ "Advisory", XMP_TYPE_XPATH_BAG, TRUE },
{ "BaseURL", XMP_TYPE_URI, FALSE },
{ "CreateDate", XMP_TYPE_DATE, TRUE },
{ "CreatorTool", XMP_TYPE_TEXT, FALSE },
{ "Identifier", XMP_TYPE_TEXT_BAG, TRUE },
{ "MetadataDate", XMP_TYPE_DATE, XMP_AUTO_UPDATE },
{ "ModifyDate", XMP_TYPE_DATE, XMP_AUTO_UPDATE },
{ "NickName", XMP_TYPE_TEXT, TRUE },
{ "Thumbnails", XMP_TYPE_THUMBNAIL_ALT, TRUE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty xmprights_properties[] =
{
{ "Certificate", XMP_TYPE_URI, TRUE },
{ "Marked", XMP_TYPE_BOOLEAN, TRUE },
{ "Owner", XMP_TYPE_TEXT_BAG, TRUE },
{ "UsageTerms", XMP_TYPE_LANG_ALT, TRUE },
{ "WebStatement", XMP_TYPE_URI, TRUE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty xmpmm_properties[] =
{
{ "DerivedFrom", XMP_TYPE_RESOURCE_REF, FALSE },
{ "DocumentID", XMP_TYPE_URI, FALSE },
{ "History", XMP_TYPE_RESOURCE_EVENT_SEQ, FALSE },
{ "ManagedFrom", XMP_TYPE_RESOURCE_REF, FALSE },
{ "Manager", XMP_TYPE_TEXT, FALSE },
{ "ManageTo", XMP_TYPE_URI, FALSE },
{ "ManageUI", XMP_TYPE_URI, FALSE },
{ "ManagerVariant", XMP_TYPE_TEXT, FALSE },
{ "RenditionClass", XMP_TYPE_TEXT, FALSE },
{ "RenditionParams", XMP_TYPE_TEXT, FALSE },
{ "VersionID", XMP_TYPE_TEXT, FALSE },
{ "Versions", XMP_TYPE_TEXT_SEQ, FALSE },
{ "LastURL", XMP_TYPE_URI, FALSE }, /*deprecated*/
{ "RenditionOf", XMP_TYPE_RESOURCE_REF, FALSE }, /*deprecated*/
{ "SaveID", XMP_TYPE_INTEGER, FALSE }, /*deprecated*/
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty xmpbj_properties[] =
{
{ "JobRef", XMP_TYPE_JOB_BAG, TRUE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty xmptpg_properties[] =
{
{ "MaxPageSize", XMP_TYPE_DIMENSIONS, FALSE },
{ "NPages", XMP_TYPE_INTEGER, FALSE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty pdf_properties[] =
{
{ "Keywords", XMP_TYPE_TEXT, TRUE },
{ "PDFVersion", XMP_TYPE_TEXT, FALSE },
{ "Producer", XMP_TYPE_TEXT, FALSE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty photoshop_properties[] =
{
{ "AuthorsPosition", XMP_TYPE_TEXT, TRUE },
{ "CaptionWriter", XMP_TYPE_TEXT, TRUE },
{ "Category", XMP_TYPE_TEXT, TRUE },/* 3 ascii chars */
{ "City", XMP_TYPE_TEXT, TRUE },
{ "Country", XMP_TYPE_TEXT, TRUE },
{ "Credit", XMP_TYPE_TEXT, TRUE },
{ "DateCreated", XMP_TYPE_DATE, TRUE },
{ "Headline", XMP_TYPE_TEXT, TRUE },
{ "Instructions", XMP_TYPE_TEXT, TRUE },
{ "Source", XMP_TYPE_TEXT, TRUE },
{ "State", XMP_TYPE_TEXT, TRUE },
{ "SupplementalCategories",XMP_TYPE_TEXT, TRUE },
{ "TransmissionReference",XMP_TYPE_TEXT, TRUE },
{ "Urgency", XMP_TYPE_INTEGER, TRUE },/* range: 1-8 */
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty tiff_properties[] =
{
{ "ImageWidth", XMP_TYPE_INTEGER, XMP_AUTO_UPDATE },
{ "ImageLength", XMP_TYPE_INTEGER, XMP_AUTO_UPDATE },
{ "BitsPerSample", XMP_TYPE_INTEGER_SEQ, FALSE },
{ "Compression", XMP_TYPE_INTEGER, FALSE },/* 1 or 6 */
{ "PhotometricInterpretation",XMP_TYPE_INTEGER, FALSE },/* 2 or 6 */
{ "Orientation", XMP_TYPE_INTEGER, FALSE },/* 1-8 */
{ "SamplesPerPixel", XMP_TYPE_INTEGER, FALSE },
{ "PlanarConfiguration",XMP_TYPE_INTEGER, FALSE },/* 1 or 2 */
{ "YCbCrSubSampling",XMP_TYPE_INTEGER_SEQ, FALSE },/* 2,1 or 2,2 */
{ "YCbCrPositioning",XMP_TYPE_INTEGER, FALSE },/* 1 or 2 */
{ "XResolution", XMP_TYPE_RATIONAL, XMP_AUTO_UPDATE },
{ "YResolution", XMP_TYPE_RATIONAL, XMP_AUTO_UPDATE },
{ "ResolutionUnit", XMP_TYPE_INTEGER, XMP_AUTO_UPDATE },/*2or3*/
{ "TransferFunction",XMP_TYPE_INTEGER_SEQ, FALSE },/* 3 * 256 ints */
{ "WhitePoint", XMP_TYPE_RATIONAL_SEQ, FALSE },
{ "PrimaryChromaticities",XMP_TYPE_RATIONAL_SEQ, FALSE },
{ "YCbCrCoefficients",XMP_TYPE_RATIONAL_SEQ, FALSE },
{ "ReferenceBlackWhite",XMP_TYPE_RATIONAL_SEQ, FALSE },
{ "DateTime", XMP_TYPE_DATE, FALSE },/*xmp:ModifyDate*/
{ "ImageDescription",XMP_TYPE_LANG_ALT, TRUE },/*dc:description*/
{ "Make", XMP_TYPE_TEXT, FALSE },
{ "Model", XMP_TYPE_TEXT, FALSE },
{ "Software", XMP_TYPE_TEXT, FALSE },/*xmp:CreatorTool*/
{ "Artist", XMP_TYPE_TEXT, TRUE },/*dc:creator*/
{ "Copyright", XMP_TYPE_TEXT, TRUE },/*dc:rights*/
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty exif_properties[] =
{
{ "ExifVersion", XMP_TYPE_TEXT, XMP_AUTO_UPDATE },/*"0210*/
{ "FlashpixVersion", XMP_TYPE_TEXT, FALSE },/* "0100" */
{ "ColorSpace", XMP_TYPE_INTEGER, FALSE },/* 1 or -32768 */
{ "ComponentsConfiguration",XMP_TYPE_INTEGER_SEQ, FALSE },/* 4 ints */
{ "CompressedBitsPerPixel",XMP_TYPE_RATIONAL, FALSE },
{ "PixelXDimension", XMP_TYPE_INTEGER, XMP_AUTO_UPDATE },
{ "PixelYDimension", XMP_TYPE_INTEGER, XMP_AUTO_UPDATE },
{ "MakerNote", XMP_TYPE_TEXT, FALSE },/* base64 enc.? */
{ "UserComment", XMP_TYPE_TEXT, TRUE },
{ "RelatedSoundFile",XMP_TYPE_TEXT, FALSE },/* DOS 8.3 fname */
{ "DateTimeOriginal",XMP_TYPE_DATE, FALSE },
{ "DateTimeDigitized",XMP_TYPE_DATE, FALSE },
{ "ExposureTime", XMP_TYPE_RATIONAL, FALSE },
{ "FNumber", XMP_TYPE_RATIONAL, FALSE },
{ "ExposureProgram", XMP_TYPE_INTEGER, FALSE },/* 0-8 */
{ "SpectralSensitivity",XMP_TYPE_TEXT, FALSE },/* ? */
{ "ISOSpeedRatings", XMP_TYPE_INTEGER_SEQ, FALSE },
{ "OECF", XMP_TYPE_OECF_SFR, FALSE },
{ "ShutterSpeedValue",XMP_TYPE_RATIONAL, FALSE },
{ "ApertureValue", XMP_TYPE_RATIONAL, FALSE },
{ "BrightnessValue", XMP_TYPE_RATIONAL, FALSE },
{ "ExposureBiasValue",XMP_TYPE_RATIONAL, FALSE },
{ "MaxApertureValue",XMP_TYPE_RATIONAL, FALSE },
{ "SubjectDistance", XMP_TYPE_RATIONAL, FALSE },/* in meters */
{ "MeteringMode", XMP_TYPE_INTEGER, FALSE },/* 0-6 or 255 */
{ "LightSource", XMP_TYPE_INTEGER, FALSE },/* 0-3,17-22,255*/
{ "Flash", XMP_TYPE_FLASH, FALSE },
{ "FocalLength", XMP_TYPE_RATIONAL, FALSE },
{ "SubjectArea", XMP_TYPE_INTEGER_SEQ, FALSE },
{ "FlashEnergy", XMP_TYPE_RATIONAL, FALSE },
{ "SpatialFrequencyResponse",XMP_TYPE_OECF_SFR, FALSE },
{ "FocalPlaneXResolution",XMP_TYPE_RATIONAL, FALSE },
{ "FocalPlaneYResolution",XMP_TYPE_RATIONAL, FALSE },
{ "FocalPlaneResolutionUnit",XMP_TYPE_INTEGER, FALSE },/* unit: 2 or 3 */
{ "SubjectLocation", XMP_TYPE_INTEGER_SEQ, FALSE },/* 2 ints: X, Y */
{ "ExposureIndex", XMP_TYPE_RATIONAL, FALSE },
{ "SensingMethod", XMP_TYPE_INTEGER, FALSE },/* 1-8 */
{ "FileSource", XMP_TYPE_INTEGER, FALSE },/* 3 */
{ "SceneType", XMP_TYPE_INTEGER, FALSE },/* 1 */
{ "CFAPattern", XMP_TYPE_CFA_PATTERN, FALSE },
{ "CustomRendered", XMP_TYPE_INTEGER, FALSE },/* 0-1 */
{ "ExposureMode", XMP_TYPE_INTEGER, FALSE },/* 0-2 */
{ "WhiteBalance", XMP_TYPE_INTEGER, FALSE },/* 0-1 */
{ "DigitalZoomRatio",XMP_TYPE_RATIONAL, FALSE },
{ "FocalLengthIn35mmFilm",XMP_TYPE_INTEGER, FALSE },/* in mm */
{ "SceneCaptureType",XMP_TYPE_INTEGER, FALSE },/* 0-3 */
{ "GainControl", XMP_TYPE_INTEGER, FALSE },/* 0-4 */
{ "Contrast", XMP_TYPE_INTEGER, FALSE },/* 0-2 */
{ "Saturation", XMP_TYPE_INTEGER, FALSE },/* 0-2 */
{ "Sharpness", XMP_TYPE_INTEGER, FALSE },/* 0-2 */
{ "DeviceSettingDescription",XMP_TYPE_DEVICE_SETTINGS, FALSE },
{ "SubjectDistanceRange",XMP_TYPE_INTEGER, FALSE },/* 0-3 */
{ "ImageUniqueID", XMP_TYPE_TEXT, FALSE },/* 32 chars */
{ "GPSVersionID", XMP_TYPE_TEXT, FALSE },/* "2.0.0.0" */
{ "GPSLatitude", XMP_TYPE_GPS_COORDINATE, FALSE },
{ "GPSLongitude", XMP_TYPE_GPS_COORDINATE, FALSE },
{ "GPSAltitudeRef", XMP_TYPE_INTEGER, FALSE },/* 0-1 */
{ "GPSAltitude", XMP_TYPE_RATIONAL, FALSE },/* in meters */
{ "GPSTimeStamp", XMP_TYPE_DATE, FALSE },
{ "GPSSatellites", XMP_TYPE_TEXT, FALSE },/* ? */
{ "GPSStatus", XMP_TYPE_TEXT, FALSE },/* "A" or "V" */
{ "GPSMeasureMode", XMP_TYPE_INTEGER, FALSE },/* 2-3 */
{ "GPSDOP", XMP_TYPE_RATIONAL, FALSE },
{ "GPSSpeedRef", XMP_TYPE_TEXT, FALSE },/* "K","M","N" */
{ "GPSSpeed", XMP_TYPE_RATIONAL, FALSE },
{ "GPSTrackRef", XMP_TYPE_TEXT, FALSE },/* "T" or "M"" */
{ "GPSTrack", XMP_TYPE_RATIONAL, FALSE },
{ "GPSImgDirectionRef",XMP_TYPE_TEXT, FALSE },/* "T" or "M"" */
{ "GPSImgDirection", XMP_TYPE_RATIONAL, FALSE },
{ "GPSMapDatum", XMP_TYPE_TEXT, FALSE },
{ "GPSDestLatitude", XMP_TYPE_GPS_COORDINATE, FALSE },
{ "GPSDestLongitude",XMP_TYPE_GPS_COORDINATE, FALSE },
{ "GPSDestBearingRef",XMP_TYPE_TEXT, FALSE },/* "T" or "M"" */
{ "GPSDestBearing", XMP_TYPE_RATIONAL, FALSE },
{ "GPSDestDistanceRef",XMP_TYPE_TEXT, FALSE },/* "K","M","N" */
{ "GPSDestDistance", XMP_TYPE_RATIONAL, FALSE },
{ "GPSProcessingMethod",XMP_TYPE_TEXT, FALSE },
{ "GPSAreaInformation",XMP_TYPE_TEXT, FALSE },
{ "GPSDifferential", XMP_TYPE_INTEGER, FALSE },/* 0-1 */
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPSchema xmp_schemas[] =
{
/* XMP schemas defined as of January 2004 */
{ XMP_SCHEMA_DUBLIN_CORE, "dc",
"Dublin Core", dc_properties },
{ XMP_SCHEMA_XMP_BASIC, "xmp",
"XMP Basic", xmp_properties },
{ XMP_SCHEMA_XMP_RIGHTS, "xmpRights",
"XMP Rights Management", xmprights_properties },
{ XMP_SCHEMA_XMP_MM, "xmpMM",
"XMP Media Management", xmpmm_properties },
{ XMP_SCHEMA_XMP_BJ, "xmpBJ",
"XMP Basic Job Ticket", xmpbj_properties },
{ XMP_SCHEMA_XMP_TPG, "xmpTPg",
"XMP Paged-Text", xmptpg_properties },
{ XMP_SCHEMA_PDF, "pdf",
"Adobe PDF", pdf_properties },
{ XMP_SCHEMA_PHOTOSHOP, "photoshop",
"Photoshop", photoshop_properties },
{ XMP_SCHEMA_TIFF, "tiff",
"EXIF (TIFF Properties)", tiff_properties },
{ XMP_SCHEMA_EXIF, "exif",
"EXIF (EXIF-specific Properties)", exif_properties },
/* XMP sub-types */
{ "http://ns.adobe.com/xmp/Identifier/qual/1.0/", "xmpidq",
NULL, NULL },
{ "http://ns.adobe.com/xap/1.0/g/img/", "xapGImg",
NULL, NULL },
{ "http://ns.adobe.com/xap/1.0/sType/Dimensions#", "stDim",
NULL, NULL },
{ "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#", "stEvt",
NULL, NULL },
{ "http://ns.adobe.com/xap/1.0/sType/ResourceRef#", "stRef",
NULL, NULL },
{ "http://ns.adobe.com/xap/1.0/sType/Version#", "stVer",
NULL, NULL },
{ "http://ns.adobe.com/xap/1.0/sType/Job#", "stJob",
NULL, NULL },
/* other useful namespaces */
{ "http://web.resource.org/cc/", "cc",
"Creative Commons", NULL },
{ "http://ns.adobe.com/iX/1.0/", "iX",
NULL, NULL },
{ NULL, NULL, NULL }
XMPSchema *cached_schema;
GtkTreeIter cached_schema_iter;
};
/**
@ -334,20 +69,20 @@ xmp_model_new (void)
/* columns defined by the XMPModelColumns enum */
xmp_model->treestore =
gtk_tree_store_new (XMP_MODEL_NUM_COLUMNS,
G_TYPE_STRING, /* name */
G_TYPE_STRING, /* value as string (for viewing) */
G_TYPE_POINTER, /* value as array (from parser) */
G_TYPE_POINTER, /* XMPProperty or XMPSchema */
G_TYPE_POINTER, /* GtkWidget cross-reference */
G_TYPE_INT, /* editable? */
GDK_TYPE_PIXBUF, /* edit icon */
G_TYPE_BOOLEAN, /* visible? */
G_TYPE_INT, /* font weight */
G_TYPE_BOOLEAN /* font weight set? */
G_TYPE_STRING, /* COL_XMP_NAME */
G_TYPE_STRING, /* COL_XMP_VALUE */
G_TYPE_POINTER, /* COL_XMP_VALUE_RAW */
G_TYPE_POINTER, /* COL_XMP_TYPE_XREF */
G_TYPE_POINTER, /* COL_XMP_WIDGET_XREF */
G_TYPE_INT, /* COL_XMP_EDITABLE */
GDK_TYPE_PIXBUF, /* COL_XMP_EDIT_ICON */
G_TYPE_BOOLEAN, /* COL_XMP_VISIBLE */
G_TYPE_INT, /* COL_XMP_WEIGHT */
G_TYPE_BOOLEAN /* COL_XMP_WEIGHT_SET */
);
xmp_model->custom_schemas = NULL;
xmp_model->custom_properties = NULL;
xmp_model->current_schema = NULL;
xmp_model->cached_schema = NULL;
return xmp_model;
}
@ -376,15 +111,21 @@ xmp_model_free (XMPModel *xmp_model)
{
if (gtk_tree_model_iter_children (model, &child, &iter))
{
gchar **last_value_array = NULL;
do
{
gtk_tree_model_get (model, &child,
COL_XMP_VALUE_RAW, &value_array,
-1);
/* FIXME: this does not free everything */
for (i = 0; value_array[i] != NULL; i++)
g_free (value_array[i]);
g_free (value_array);
if (value_array != last_value_array)
{
/* FIXME: this does not free everything */
for (i = 0; value_array[i] != NULL; i++)
g_free (value_array[i]);
g_free (value_array);
}
last_value_array = value_array;
}
while (gtk_tree_model_iter_next (model, &child));
}
@ -420,34 +161,20 @@ static XMPSchema *
find_xmp_schema (XMPModel *xmp_model,
const gchar *schema_uri)
{
int i;
GSList *list;
int i;
GSList *list;
const gchar *c;
/* check if we know about this schema (exact match for URI) */
for (i = 0; xmp_schemas[i].uri != NULL; ++i)
{
if (! strcmp (xmp_schemas[i].uri, schema_uri))
{
#ifdef DEBUG_XMP_PARSER
#ifdef DEBUG_XMP_MODEL
if (xmp_schemas[i].name != NULL)
printf ("%s \t[%s]\n", xmp_schemas[i].name, xmp_schemas[i].uri);
g_print ("%s \t[%s]\n", xmp_schemas[i].name, xmp_schemas[i].uri);
else
printf ("*** \t[%s]\n", xmp_schemas[i].uri);
#endif
return &(xmp_schemas[i]);
}
}
/* try again but accept "http:" without "//", or missing "http://" */
for (i = 0; xmp_schemas[i].uri != NULL; ++i)
{
if (g_str_has_prefix (xmp_schemas[i].uri, "http://")
&& ((! strcmp (xmp_schemas[i].uri + 7, schema_uri))
|| (g_str_has_prefix (schema_uri, "http:")
&& ! strcmp (xmp_schemas[i].uri + 7, schema_uri + 5))
))
{
#ifdef DEBUG_XMP_PARSER
printf ("%s \t~~~[%s]\n", xmp_schemas[i].name, xmp_schemas[i].uri);
g_print ("(no name) \t[%s]\n", xmp_schemas[i].uri);
#endif
return &(xmp_schemas[i]);
}
@ -457,16 +184,58 @@ find_xmp_schema (XMPModel *xmp_model,
{
if (! strcmp (((XMPSchema *)(list->data))->uri, schema_uri))
{
#ifdef DEBUG_XMP_PARSER
printf ("CUSTOM %s \t[%s]\n",
#ifdef DEBUG_XMP_MODEL
g_print ("CUSTOM %s \t[%s]\n",
((XMPSchema *)(list->data))->name,
((XMPSchema *)(list->data))->uri);
#endif
return (XMPSchema *)(list->data);
}
}
#ifdef DEBUG_XMP_PARSER
printf ("Unknown schema URI %s\n", schema_uri);
/* now check for some common errors and results of bad encoding: */
/* - check for "http:" without "//", or missing "http://" */
for (i = 0; xmp_schemas[i].uri != NULL; ++i)
{
if (g_str_has_prefix (xmp_schemas[i].uri, "http://")
&& ((! strcmp (xmp_schemas[i].uri + 7, schema_uri))
|| (g_str_has_prefix (schema_uri, "http:")
&& ! strcmp (xmp_schemas[i].uri + 7, schema_uri + 5))
))
{
#ifdef DEBUG_XMP_MODEL
g_print ("%s \t~~~[%s]\n", xmp_schemas[i].name, xmp_schemas[i].uri);
#endif
return &(xmp_schemas[i]);
}
}
/* - check for errors such as "name (uri)" or "name (prefix, uri)" */
for (c = schema_uri; *c; c++)
if ((*c == '(') || (*c == ' ') || (*c == ','))
{
int len;
c++;
while (*c == ' ')
c++;
if (! *c)
break;
for (len = 1; c[len]; len++)
if ((c[len] == ')') || (c[len] == ' '))
break;
for (i = 0; xmp_schemas[i].uri != NULL; ++i)
{
if (! strncmp (xmp_schemas[i].uri, c, len))
{
#ifdef DEBUG_XMP_MODEL
g_print ("%s \t~~~[%s]\n", xmp_schemas[i].name,
xmp_schemas[i].uri);
#endif
return &(xmp_schemas[i]);
}
}
}
#ifdef DEBUG_XMP_MODEL
g_print ("Unknown schema URI %s\n", schema_uri);
#endif
return NULL;
}
@ -490,13 +259,13 @@ find_xmp_schema_prefix (XMPModel *xmp_model,
/* make the next lookup a bit faster if the tree is not modified */
static void
save_iter_for_schema (XMPModel *xmp_model,
cache_iter_for_schema (XMPModel *xmp_model,
XMPSchema *schema,
GtkTreeIter *iter)
{
xmp_model->current_schema = schema;
xmp_model->cached_schema = schema;
if (iter != NULL)
memcpy (&(xmp_model->current_schema_iter), iter, sizeof (GtkTreeIter));
memcpy (&(xmp_model->cached_schema_iter), iter, sizeof (GtkTreeIter));
}
/* find the GtkTreeIter for the given schema and return TRUE if the schema was
@ -508,16 +277,16 @@ find_iter_for_schema (XMPModel *xmp_model,
{
XMPSchema *schema_xref;
if (schema == xmp_model->current_schema)
/* common case: return the cached iter */
if (schema == xmp_model->cached_schema)
{
memcpy (iter, &(xmp_model->current_schema_iter), sizeof (GtkTreeIter));
memcpy (iter, &(xmp_model->cached_schema_iter), sizeof (GtkTreeIter));
return TRUE;
}
/* check where this schema has been stored in the tree */
/* find where this schema has been stored in the tree */
if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (xmp_model->treestore),
iter))
return FALSE;
do
{
gtk_tree_model_get (GTK_TREE_MODEL (xmp_model->treestore), iter,
@ -525,7 +294,7 @@ find_iter_for_schema (XMPModel *xmp_model,
-1);
if (schema_xref == schema)
{
save_iter_for_schema (xmp_model, schema, iter);
cache_iter_for_schema (xmp_model, schema, iter);
return TRUE;
}
}
@ -585,7 +354,7 @@ add_known_schema (XMPModel *xmp_model,
COL_XMP_WEIGHT, PANGO_WEIGHT_BOLD,
COL_XMP_WEIGHT_SET, TRUE,
-1);
save_iter_for_schema (xmp_model, schema, iter);
cache_iter_for_schema (xmp_model, schema, iter);
}
/* called by the XMP parser - new schema */
@ -621,7 +390,7 @@ parse_start_schema (XMPParseContext *context,
/* schemas with NULL names are special and should not go in the tree */
if (schema->name == NULL)
{
save_iter_for_schema (xmp_model, NULL, NULL);
cache_iter_for_schema (xmp_model, NULL, NULL);
return schema;
}
/* if the schema is not in the tree yet, add it now */
@ -641,8 +410,11 @@ parse_end_schema (XMPParseContext *context,
g_return_if_fail (xmp_model != NULL);
g_return_if_fail (schema != NULL);
xmp_model->current_schema = NULL;
/* printf ("End of %s\n", schema->name); */
xmp_model->cached_schema = NULL;
#ifdef DEBUG_XMP_MODEL
if (schema->name)
g_print ("End of %s\n", schema->name);
#endif
}
/* called by the XMP parser - new property */
@ -689,8 +461,8 @@ parse_set_property (XMPParseContext *context,
switch (type)
{
case XMP_PTYPE_TEXT:
#ifdef DEBUG_XMP_PARSER
printf ("\t%s:%s = \"%s\"\n", ns_prefix, name, value[0]);
#ifdef DEBUG_XMP_MODEL
g_print ("\t%s:%s = \"%s\"\n", ns_prefix, name, value[0]);
#endif
if (property != NULL)
/* FIXME */;
@ -719,8 +491,8 @@ parse_set_property (XMPParseContext *context,
break;
case XMP_PTYPE_RESOURCE:
#ifdef DEBUG_XMP_PARSER
printf ("\t%s:%s @ = \"%s\"\n", ns_prefix, name,
#ifdef DEBUG_XMP_MODEL
g_print ("\t%s:%s @ = \"%s\"\n", ns_prefix, name,
value[0]);
#endif
if (property != NULL)
@ -753,14 +525,14 @@ parse_set_property (XMPParseContext *context,
case XMP_PTYPE_ORDERED_LIST:
case XMP_PTYPE_UNORDERED_LIST:
#ifdef DEBUG_XMP_PARSER
printf ("\t%s:%s [] =", ns_prefix, name);
#ifdef DEBUG_XMP_MODEL
g_print ("\t%s:%s [] =", ns_prefix, name);
for (i = 0; value[i] != NULL; i++)
if (i == 0)
printf (" \"%s\"", value[i]);
g_print (" \"%s\"", value[i]);
else
printf (", \"%s\"", value[i]);
printf ("\n");
g_print (", \"%s\"", value[i]);
g_print ("\n");
#endif
if (property != NULL)
/* FIXME */;
@ -796,11 +568,11 @@ parse_set_property (XMPParseContext *context,
break;
case XMP_PTYPE_ALT_THUMBS:
#ifdef DEBUG_XMP_PARSER
#ifdef DEBUG_XMP_MODEL
for (i = 0; value[i] != NULL; i += 2)
printf ("\t%s:%s [size:%d] = \"...\"\n", ns_prefix, name,
g_print ("\t%s:%s [size:%d] = \"...\"\n", ns_prefix, name,
*(int *)(value[i]));
printf ("\n");
g_print ("\n");
#endif
if (property != NULL)
/* FIXME */;
@ -832,9 +604,9 @@ parse_set_property (XMPParseContext *context,
break;
case XMP_PTYPE_ALT_LANG:
#ifdef DEBUG_XMP_PARSER
#ifdef DEBUG_XMP_MODEL
for (i = 0; value[i] != NULL; i += 2)
printf ("\t%s:%s [lang:%s] = \"%s\"\n", ns_prefix, name,
g_print ("\t%s:%s [lang:%s] = \"%s\"\n", ns_prefix, name,
value[i], value[i + 1]);
#endif
if (property != NULL)
@ -869,9 +641,9 @@ parse_set_property (XMPParseContext *context,
break;
case XMP_PTYPE_STRUCTURE:
#ifdef DEBUG_XMP_PARSER
#ifdef DEBUG_XMP_MODEL
for (i = 2; value[i] != NULL; i += 2)
printf ("\t%s:%s [%s] = \"%s\"\n", ns_prefix, name,
g_print ("\t%s:%s [%s] = \"%s\"\n", ns_prefix, name,
value[i], value[i + 1]);
#endif
if (property != NULL)
@ -880,7 +652,7 @@ parse_set_property (XMPParseContext *context,
{
property = g_new (XMPProperty, 1);
property->name = g_strdup (name);
property->type = XMP_TYPE_UNKNOWN;
property->type = XMP_TYPE_GENERIC_STRUCTURE;
property->editable = TRUE;
xmp_model->custom_properties =
g_slist_prepend (xmp_model->custom_properties, property);
@ -906,8 +678,8 @@ parse_set_property (XMPParseContext *context,
break;
default:
#ifdef DEBUG_XMP_PARSER
printf ("\t%s:%s = ?\n", ns_prefix, name);
#ifdef DEBUG_XMP_MODEL
g_print ("\t%s:%s = ?\n", ns_prefix, name);
#endif
break;
}

View File

@ -28,85 +28,25 @@ 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, /* ALT_THUMBS */
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 */
COL_XMP_NAME = 0, /* G_TYPE_STRING - name */
COL_XMP_VALUE, /* G_TYPE_STRING - value as string (for viewing) */
COL_XMP_VALUE_RAW, /* G_TYPE_POINTER - value as array (from parser) */
COL_XMP_TYPE_XREF, /* G_TYPE_POINTER - XMPProperty or XMPSchema */
COL_XMP_WIDGET_XREF, /* G_TYPE_POINTER - GtkWidget cross-reference */
COL_XMP_EDITABLE, /* G_TYPE_INT - editable? */
COL_XMP_EDIT_ICON, /* GDK_TYPE_PIXBUF - edit icon */
COL_XMP_VISIBLE, /* G_TYPE_BOOLEAN - visible? */
COL_XMP_WEIGHT, /* G_TYPE_INT - font weight */
COL_XMP_WEIGHT_SET, /* G_TYPE_BOOLEAN - font weight set? */
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;
/* URIs of standard XMP schemas (as of January 2004) */
#define XMP_SCHEMA_DUBLIN_CORE "http://purl.org/dc/elements/1.1/"
#define XMP_SCHEMA_XMP_BASIC "http://ns.adobe.com/xap/1.0/"
#define XMP_SCHEMA_XMP_RIGHTS "http://ns.adobe.com/xap/1.0/rights/"
#define XMP_SCHEMA_XMP_MM "http://ns.adobe.com/xap/1.0/mm/"
#define XMP_SCHEMA_XMP_BJ "http://ns.adobe.com/xap/1.0/bj/"
#define XMP_SCHEMA_XMP_TPG "http://ns.adobe.com/xap/1.0/t/pg/"
#define XMP_SCHEMA_PDF "http://ns.adobe.com/pdf/1.3/"
#define XMP_SCHEMA_PHOTOSHOP "http://ns.adobe.com/photoshop/1.0/"
#define XMP_SCHEMA_TIFF "http://ns.adobe.com/tiff/1.0/"
#define XMP_SCHEMA_EXIF "http://ns.adobe.com/exif/1.0/"
XMPModel *xmp_model_new (void);
void xmp_model_free (XMPModel *xmp_model);

View File

@ -43,7 +43,7 @@
* in the XMP specification (including support for UCS-2 and UCS-4)
* - provide an API for passing unknown elements or tags to the caller
* - think about re-writing this using a better XML parser (expat?)
* instead of GMarkupParser
* instead of the GMarkup parser
*/
#ifndef WITHOUT_GIMP
@ -116,7 +116,7 @@ xmp_parse_error_quark (void)
* qualifiers (when <rdf:Description> is used deeper than at the top
* level inside <rdf:RDF>). In that case, QDESC_VALUE contains the
* value of the property and QDESC_QUAL is used for each of the
* optional qualifiers.
* optional qualifiers (which are currently ignored).
*/
typedef enum
{
@ -186,7 +186,7 @@ struct _XMPParseContext
GMarkupParseContext *markup_context;
};
/* FIXME - debugging
#ifdef DEBUG_XMP_PARSER
static const char *state_names[] =
{
"START",
@ -218,8 +218,9 @@ static const char *state_names[] =
"SKIPPING_IGNORED_ELEMENTS",
"ERROR",
};
*/
#endif
/* report an error and propagate it */
static void
parse_error (XMPParseContext *context,
GError **error,
@ -228,37 +229,37 @@ parse_error (XMPParseContext *context,
...)
{
GError *tmp_error;
gchar *s;
va_list args;
va_start (args, format);
s = g_strdup_vprintf (format, args);
va_end (args);
if (code == XMP_ERROR_NO_XPACKET)
tmp_error = g_error_new (XMP_PARSE_ERROR, code, _("Error: %s"), s);
tmp_error = g_error_new (XMP_PARSE_ERROR, code,
_("Error: No XMP packet found"));
else
{
gint line_number;
gint char_number;
gchar *s;
va_list args;
gint line_number;
gint char_number;
va_start (args, format);
s = g_strdup_vprintf (format, args);
va_end (args);
g_markup_parse_context_get_position (context->markup_context,
&line_number,
&char_number);
tmp_error = g_error_new (XMP_PARSE_ERROR, code,
_("Error on line %d char %d: %s"),
line_number, char_number, s);
g_free (s);
}
g_free (s);
context->state = STATE_ERROR;
if (context->parser->error)
(*context->parser->error) (context, tmp_error,
context->user_data);
(*context->parser->error) (context, tmp_error, context->user_data);
g_propagate_error (error, tmp_error);
}
/* report an error if an unexpected element is found in the wrong context */
static void
parse_error_element (XMPParseContext *context,
GError **error,
@ -276,12 +277,15 @@ parse_error_element (XMPParseContext *context,
expected_element, found_element);
}
/* skip an unknown element (unknown property) and its contents */
static void
unknown_element (XMPParseContext *context,
GError **error,
const gchar *element_name)
{
g_print ("XMP: SKIPPING %s\n", element_name); /* FIXME - debugging */
#ifdef DEBUG_XMP_PARSER
g_print ("XMP: SKIPPING %s\n", element_name);
#endif
if (context->flags & XMP_FLAG_NO_UNKNOWN_ELEMENTS)
parse_error (context, error, XMP_ERROR_UNKNOWN_ELEMENT,
_("Unknown element <%s>"),
@ -294,6 +298,7 @@ unknown_element (XMPParseContext *context,
}
}
/* skip and element and all other elements that it may contain */
static void
ignore_element (XMPParseContext *context)
{
@ -302,6 +307,7 @@ ignore_element (XMPParseContext *context)
context->state = STATE_SKIPPING_IGNORED_ELEMENTS;
}
/* skip an unknown attribute (or abort if flags forbid unknown attributes) */
static void
unknown_attribute (XMPParseContext *context,
GError **error,
@ -313,6 +319,10 @@ unknown_attribute (XMPParseContext *context,
parse_error (context, error, XMP_ERROR_UNKNOWN_ATTRIBUTE,
_("Unknown attribute \"%s\"=\"%s\" in element <%s>"),
attribute_name, attribute_value, element_name);
#ifdef DEBUG_XMP_PARSER
g_print ("skipping unknown attribute \"%s\"=\"%s\" in element <%s>\n",
attribute_name, attribute_value, element_name);
#endif
}
static gboolean
@ -329,6 +339,7 @@ is_whitespace_string (const gchar *string)
return TRUE;
}
/* new namespace/schema seen - add it to the list of namespaces */
static void
push_namespace (XMPParseContext *context,
const gchar *uri,
@ -356,6 +367,7 @@ push_namespace (XMPParseContext *context,
ns->ns_user_data = NULL;
}
/* free all namespaces that are deeper than the current element depth */
static void
pop_namespaces (XMPParseContext *context,
GError **error)
@ -364,7 +376,6 @@ pop_namespaces (XMPParseContext *context,
if (context->namespaces == NULL)
return;
/* free all namespaces that are deeper than the current element depth */
ns = context->namespaces->data;
while (ns->depth >= context->depth)
{
@ -383,6 +394,7 @@ pop_namespaces (XMPParseContext *context,
}
}
/* checks if an element name starts with the prefix of the given namespace */
static gboolean
has_ns_prefix (const gchar *name,
XMLNameSpace *ns)
@ -393,6 +405,8 @@ has_ns_prefix (const gchar *name,
&& (name[ns->prefix_len] == ':'));
}
/* add a new property to the schema referenced by its prefix */
/* the value(s) of the property will be added later by add_property_value() */
static XMLNameSpace *
new_property_in_ns (XMPParseContext *context,
const gchar *element_name)
@ -420,6 +434,10 @@ new_property_in_ns (XMPParseContext *context,
return NULL;
}
/* store a value for the current property - if the element containing the */
/* value is being parsed but the actual value has not been seen yet, then */
/* call this function with a NULL value so that its data structure is */
/* allocated now; it will be updated later with update_property_value() */
static void
add_property_value (XMPParseContext *context,
XMPParseType type,
@ -454,9 +472,11 @@ add_property_value (XMPParseContext *context,
context->prop_cur_value++;
context->prop_value[context->prop_cur_value] = value;
context->prop_value[context->prop_cur_value + 1] = NULL;
/* if value was NULL, then we must update it later */
context->prop_missing_value = (value == NULL);
}
/* update a value that has been allocated but not stored yet */
static void
update_property_value (XMPParseContext *context,
gchar *value)
@ -468,6 +488,7 @@ update_property_value (XMPParseContext *context,
context->prop_missing_value = FALSE;
}
/* invoke the 'set_property' callback and free the temporary structures */
static void
propagate_property (XMPParseContext *context,
GError **error)
@ -511,18 +532,14 @@ start_element_handler (GMarkupParseContext *markup_context,
XMPParseContext *context = user_data;
gint attr;
/*
g_print ("[%02d/%02d] %d <%s>\n", context->state, context->saved_state,
context->depth, element_name);
*/
/* FIXME - debugging
#ifdef DEBUG_XMP_PARSER
g_print ("[%25s/%17s] %d <%s>\n",
state_names[context->state],
(context->saved_state == STATE_ERROR
? "-"
: state_names[context->saved_state]),
context->depth, element_name);
*/
#endif
context->depth++;
for (attr = 0; attribute_names[attr] != NULL; ++attr)
if (g_str_has_prefix (attribute_names[attr], "xmlns:"))
@ -818,18 +835,18 @@ end_element_handler (GMarkupParseContext *markup_context,
{
XMPParseContext *context = user_data;
#ifdef DEBUG_XMP_PARSER
/*
g_print ("[%02d/%02d] %d </%s>\n", context->state, context->saved_state,
context->depth, element_name);
*/
/* FIXME - debugging
g_print ("[%25s/%17s] %d </%s>\n",
state_names[context->state],
(context->saved_state == STATE_ERROR
? "-"
: state_names[context->saved_state]),
context->depth, element_name);
*/
#endif
switch (context->state)
{
case STATE_INSIDE_PROPERTY:
@ -965,10 +982,13 @@ text_handler (GMarkupParseContext *markup_context,
gchar *decoded;
gint decoded_size;
#ifdef DEBUG_XMP_PARSER
/* g_print ("XMP: Pushing text:\n%s\n", text); */
#endif
max_size = text_len - text_len / 4 + 1;
decoded = g_malloc (max_size);
decoded_size = base64_decode (text, text_len, decoded, max_size);
#ifdef DEBUG_XMP_PARSER
if (decoded_size > 0)
{
/* FIXME: remove this debugging code */
@ -981,6 +1001,7 @@ text_handler (GMarkupParseContext *markup_context,
*/
g_print ("XMP: Thumb text len: %d (1/4 = %d)\nMax size: %d\nUsed size: %d\n", (int) text_len, (int) text_len / 4, max_size, decoded_size);
}
#endif
if (decoded_size > 0)
{
gint *size_p;
@ -1007,11 +1028,11 @@ text_handler (GMarkupParseContext *markup_context,
break;
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);
*/
#endif
/* FIXME: notify the user? add a way to collect qualifiers? */
break;
@ -1234,8 +1255,7 @@ xmp_parse_context_parse (XMPParseContext *context,
return g_markup_parse_context_parse (context->markup_context,
text + i, e - i + 1, error);
}
parse_error (context, error, XMP_ERROR_NO_XPACKET,
_("No XMP packet found"));
parse_error (context, error, XMP_ERROR_NO_XPACKET, NULL);
return FALSE;
}
return g_markup_parse_context_parse (context->markup_context,
@ -1263,7 +1283,6 @@ xmp_parse_context_end_parse (XMPParseContext *context,
g_return_val_if_fail (context->state != STATE_ERROR, FALSE);
if (context->state == STATE_START)
parse_error (context, error, XMP_ERROR_NO_XPACKET,
_("No XMP packet found"));
parse_error (context, error, XMP_ERROR_NO_XPACKET, NULL);
return g_markup_parse_context_end_parse (context->markup_context, error);
}

View File

@ -0,0 +1,314 @@
/* xmp-schemas.h - standard schemas defined in the XMP specifications
*
* 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 "xmp-model.h"
#include "xmp-schemas.h"
static XMPProperty dc_properties[] =
{
{ "contributor", XMP_TYPE_TEXT, TRUE },
{ "coverage", XMP_TYPE_TEXT, TRUE },
{ "creator", XMP_TYPE_TEXT_SEQ, TRUE },
{ "date", XMP_TYPE_DATE, TRUE },
{ "description", XMP_TYPE_LANG_ALT, TRUE },
{ "format", XMP_TYPE_MIME_TYPE, XMP_AUTO_UPDATE },
{ "identifier", XMP_TYPE_TEXT, TRUE }, /*xmp:Identifier*/
{ "language", XMP_TYPE_LOCALE_BAG, FALSE },
{ "publisher", XMP_TYPE_TEXT_BAG, TRUE },
{ "relation", XMP_TYPE_TEXT_BAG, TRUE },
{ "rights", XMP_TYPE_LANG_ALT, TRUE },
{ "source", XMP_TYPE_TEXT, TRUE },
{ "subject", XMP_TYPE_TEXT_BAG, TRUE },
{ "title", XMP_TYPE_LANG_ALT, TRUE },
{ "type", XMP_TYPE_TEXT_BAG, TRUE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty xmp_properties[] =
{
{ "Advisory", XMP_TYPE_XPATH_BAG, TRUE },
{ "BaseURL", XMP_TYPE_URI, FALSE },
{ "CreateDate", XMP_TYPE_DATE, TRUE },
{ "CreatorTool", XMP_TYPE_TEXT, FALSE },
{ "Identifier", XMP_TYPE_TEXT_BAG, TRUE },
{ "MetadataDate", XMP_TYPE_DATE, XMP_AUTO_UPDATE },
{ "ModifyDate", XMP_TYPE_DATE, XMP_AUTO_UPDATE },
{ "NickName", XMP_TYPE_TEXT, TRUE },
{ "Thumbnails", XMP_TYPE_THUMBNAIL_ALT, TRUE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty xmprights_properties[] =
{
{ "Certificate", XMP_TYPE_URI, TRUE },
{ "Marked", XMP_TYPE_BOOLEAN, TRUE },
{ "Owner", XMP_TYPE_TEXT_BAG, TRUE },
{ "UsageTerms", XMP_TYPE_LANG_ALT, TRUE },
{ "WebStatement", XMP_TYPE_URI, TRUE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty xmpmm_properties[] =
{
{ "DerivedFrom", XMP_TYPE_RESOURCE_REF, FALSE },
{ "DocumentID", XMP_TYPE_URI, FALSE },
{ "History", XMP_TYPE_RESOURCE_EVENT_SEQ, FALSE },
{ "ManagedFrom", XMP_TYPE_RESOURCE_REF, FALSE },
{ "Manager", XMP_TYPE_TEXT, FALSE },
{ "ManageTo", XMP_TYPE_URI, FALSE },
{ "ManageUI", XMP_TYPE_URI, FALSE },
{ "ManagerVariant", XMP_TYPE_TEXT, FALSE },
{ "RenditionClass", XMP_TYPE_TEXT, FALSE },
{ "RenditionParams", XMP_TYPE_TEXT, FALSE },
{ "VersionID", XMP_TYPE_TEXT, FALSE },
{ "Versions", XMP_TYPE_TEXT_SEQ, FALSE },
{ "LastURL", XMP_TYPE_URI, FALSE }, /*deprecated*/
{ "RenditionOf", XMP_TYPE_RESOURCE_REF, FALSE }, /*deprecated*/
{ "SaveID", XMP_TYPE_INTEGER, FALSE }, /*deprecated*/
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty xmpbj_properties[] =
{
{ "JobRef", XMP_TYPE_JOB_BAG, TRUE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty xmptpg_properties[] =
{
{ "MaxPageSize", XMP_TYPE_DIMENSIONS, FALSE },
{ "NPages", XMP_TYPE_INTEGER, FALSE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty pdf_properties[] =
{
{ "Keywords", XMP_TYPE_TEXT, TRUE },
{ "PDFVersion", XMP_TYPE_TEXT, FALSE },
{ "Producer", XMP_TYPE_TEXT, FALSE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty photoshop_properties[] =
{
{ "AuthorsPosition", XMP_TYPE_TEXT, TRUE },
{ "CaptionWriter", XMP_TYPE_TEXT, TRUE },
{ "Category", XMP_TYPE_TEXT, TRUE },/* 3 ascii chars */
{ "City", XMP_TYPE_TEXT, TRUE },
{ "Country", XMP_TYPE_TEXT, TRUE },
{ "Credit", XMP_TYPE_TEXT, TRUE },
{ "DateCreated", XMP_TYPE_DATE, TRUE },
{ "Headline", XMP_TYPE_TEXT, TRUE },
{ "Instructions", XMP_TYPE_TEXT, TRUE },
{ "Source", XMP_TYPE_TEXT, TRUE },
{ "State", XMP_TYPE_TEXT, TRUE },
{ "SupplementalCategories",XMP_TYPE_TEXT, TRUE },
{ "TransmissionReference",XMP_TYPE_TEXT, TRUE },
{ "Urgency", XMP_TYPE_INTEGER, TRUE },/* range: 1-8 */
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty tiff_properties[] =
{
{ "ImageWidth", XMP_TYPE_INTEGER, XMP_AUTO_UPDATE },
{ "ImageLength", XMP_TYPE_INTEGER, XMP_AUTO_UPDATE },
{ "BitsPerSample", XMP_TYPE_INTEGER_SEQ, FALSE },
{ "Compression", XMP_TYPE_INTEGER, FALSE },/* 1 or 6 */
{ "PhotometricInterpretation",XMP_TYPE_INTEGER, FALSE },/* 2 or 6 */
{ "Orientation", XMP_TYPE_INTEGER, FALSE },/* 1-8 */
{ "SamplesPerPixel", XMP_TYPE_INTEGER, FALSE },
{ "PlanarConfiguration",XMP_TYPE_INTEGER, FALSE },/* 1 or 2 */
{ "YCbCrSubSampling",XMP_TYPE_INTEGER_SEQ, FALSE },/* 2,1 or 2,2 */
{ "YCbCrPositioning",XMP_TYPE_INTEGER, FALSE },/* 1 or 2 */
{ "XResolution", XMP_TYPE_RATIONAL, XMP_AUTO_UPDATE },
{ "YResolution", XMP_TYPE_RATIONAL, XMP_AUTO_UPDATE },
{ "ResolutionUnit", XMP_TYPE_INTEGER, XMP_AUTO_UPDATE },/*2or3*/
{ "TransferFunction",XMP_TYPE_INTEGER_SEQ, FALSE },/* 3 * 256 ints */
{ "WhitePoint", XMP_TYPE_RATIONAL_SEQ, FALSE },
{ "PrimaryChromaticities",XMP_TYPE_RATIONAL_SEQ, FALSE },
{ "YCbCrCoefficients",XMP_TYPE_RATIONAL_SEQ, FALSE },
{ "ReferenceBlackWhite",XMP_TYPE_RATIONAL_SEQ, FALSE },
{ "DateTime", XMP_TYPE_DATE, FALSE },/*xmp:ModifyDate*/
{ "ImageDescription",XMP_TYPE_LANG_ALT, TRUE },/*dc:description*/
{ "Make", XMP_TYPE_TEXT, FALSE },
{ "Model", XMP_TYPE_TEXT, FALSE },
{ "Software", XMP_TYPE_TEXT, FALSE },/*xmp:CreatorTool*/
{ "Artist", XMP_TYPE_TEXT, TRUE },/*dc:creator*/
{ "Copyright", XMP_TYPE_TEXT, TRUE },/*dc:rights*/
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty exif_properties[] =
{
{ "ExifVersion", XMP_TYPE_TEXT, XMP_AUTO_UPDATE },/*"0210*/
{ "FlashpixVersion", XMP_TYPE_TEXT, FALSE },/* "0100" */
{ "ColorSpace", XMP_TYPE_INTEGER, FALSE },/* 1 or -32768 */
{ "ComponentsConfiguration",XMP_TYPE_INTEGER_SEQ, FALSE },/* 4 ints */
{ "CompressedBitsPerPixel",XMP_TYPE_RATIONAL, FALSE },
{ "PixelXDimension", XMP_TYPE_INTEGER, XMP_AUTO_UPDATE },
{ "PixelYDimension", XMP_TYPE_INTEGER, XMP_AUTO_UPDATE },
{ "MakerNote", XMP_TYPE_TEXT, FALSE },/* base64 enc.? */
{ "UserComment", XMP_TYPE_TEXT, TRUE },
{ "RelatedSoundFile",XMP_TYPE_TEXT, FALSE },/* DOS 8.3 fname */
{ "DateTimeOriginal",XMP_TYPE_DATE, FALSE },
{ "DateTimeDigitized",XMP_TYPE_DATE, FALSE },
{ "ExposureTime", XMP_TYPE_RATIONAL, FALSE },
{ "FNumber", XMP_TYPE_RATIONAL, FALSE },
{ "ExposureProgram", XMP_TYPE_INTEGER, FALSE },/* 0-8 */
{ "SpectralSensitivity",XMP_TYPE_TEXT, FALSE },/* ? */
{ "ISOSpeedRatings", XMP_TYPE_INTEGER_SEQ, FALSE },
{ "OECF", XMP_TYPE_OECF_SFR, FALSE },
{ "ShutterSpeedValue",XMP_TYPE_RATIONAL, FALSE },
{ "ApertureValue", XMP_TYPE_RATIONAL, FALSE },
{ "BrightnessValue", XMP_TYPE_RATIONAL, FALSE },
{ "ExposureBiasValue",XMP_TYPE_RATIONAL, FALSE },
{ "MaxApertureValue",XMP_TYPE_RATIONAL, FALSE },
{ "SubjectDistance", XMP_TYPE_RATIONAL, FALSE },/* in meters */
{ "MeteringMode", XMP_TYPE_INTEGER, FALSE },/* 0-6 or 255 */
{ "LightSource", XMP_TYPE_INTEGER, FALSE },/* 0-3,17-22,255*/
{ "Flash", XMP_TYPE_FLASH, FALSE },
{ "FocalLength", XMP_TYPE_RATIONAL, FALSE },
{ "SubjectArea", XMP_TYPE_INTEGER_SEQ, FALSE },
{ "FlashEnergy", XMP_TYPE_RATIONAL, FALSE },
{ "SpatialFrequencyResponse",XMP_TYPE_OECF_SFR, FALSE },
{ "FocalPlaneXResolution",XMP_TYPE_RATIONAL, FALSE },
{ "FocalPlaneYResolution",XMP_TYPE_RATIONAL, FALSE },
{ "FocalPlaneResolutionUnit",XMP_TYPE_INTEGER, FALSE },/* unit: 2 or 3 */
{ "SubjectLocation", XMP_TYPE_INTEGER_SEQ, FALSE },/* 2 ints: X, Y */
{ "ExposureIndex", XMP_TYPE_RATIONAL, FALSE },
{ "SensingMethod", XMP_TYPE_INTEGER, FALSE },/* 1-8 */
{ "FileSource", XMP_TYPE_INTEGER, FALSE },/* 3 */
{ "SceneType", XMP_TYPE_INTEGER, FALSE },/* 1 */
{ "CFAPattern", XMP_TYPE_CFA_PATTERN, FALSE },
{ "CustomRendered", XMP_TYPE_INTEGER, FALSE },/* 0-1 */
{ "ExposureMode", XMP_TYPE_INTEGER, FALSE },/* 0-2 */
{ "WhiteBalance", XMP_TYPE_INTEGER, FALSE },/* 0-1 */
{ "DigitalZoomRatio",XMP_TYPE_RATIONAL, FALSE },
{ "FocalLengthIn35mmFilm",XMP_TYPE_INTEGER, FALSE },/* in mm */
{ "SceneCaptureType",XMP_TYPE_INTEGER, FALSE },/* 0-3 */
{ "GainControl", XMP_TYPE_INTEGER, FALSE },/* 0-4 */
{ "Contrast", XMP_TYPE_INTEGER, FALSE },/* 0-2 */
{ "Saturation", XMP_TYPE_INTEGER, FALSE },/* 0-2 */
{ "Sharpness", XMP_TYPE_INTEGER, FALSE },/* 0-2 */
{ "DeviceSettingDescription",XMP_TYPE_DEVICE_SETTINGS, FALSE },
{ "SubjectDistanceRange",XMP_TYPE_INTEGER, FALSE },/* 0-3 */
{ "ImageUniqueID", XMP_TYPE_TEXT, FALSE },/* 32 chars */
{ "GPSVersionID", XMP_TYPE_TEXT, FALSE },/* "2.0.0.0" */
{ "GPSLatitude", XMP_TYPE_GPS_COORDINATE, FALSE },
{ "GPSLongitude", XMP_TYPE_GPS_COORDINATE, FALSE },
{ "GPSAltitudeRef", XMP_TYPE_INTEGER, FALSE },/* 0-1 */
{ "GPSAltitude", XMP_TYPE_RATIONAL, FALSE },/* in meters */
{ "GPSTimeStamp", XMP_TYPE_DATE, FALSE },
{ "GPSSatellites", XMP_TYPE_TEXT, FALSE },/* ? */
{ "GPSStatus", XMP_TYPE_TEXT, FALSE },/* "A" or "V" */
{ "GPSMeasureMode", XMP_TYPE_INTEGER, FALSE },/* 2-3 */
{ "GPSDOP", XMP_TYPE_RATIONAL, FALSE },
{ "GPSSpeedRef", XMP_TYPE_TEXT, FALSE },/* "K","M","N" */
{ "GPSSpeed", XMP_TYPE_RATIONAL, FALSE },
{ "GPSTrackRef", XMP_TYPE_TEXT, FALSE },/* "T" or "M"" */
{ "GPSTrack", XMP_TYPE_RATIONAL, FALSE },
{ "GPSImgDirectionRef",XMP_TYPE_TEXT, FALSE },/* "T" or "M"" */
{ "GPSImgDirection", XMP_TYPE_RATIONAL, FALSE },
{ "GPSMapDatum", XMP_TYPE_TEXT, FALSE },
{ "GPSDestLatitude", XMP_TYPE_GPS_COORDINATE, FALSE },
{ "GPSDestLongitude",XMP_TYPE_GPS_COORDINATE, FALSE },
{ "GPSDestBearingRef",XMP_TYPE_TEXT, FALSE },/* "T" or "M"" */
{ "GPSDestBearing", XMP_TYPE_RATIONAL, FALSE },
{ "GPSDestDistanceRef",XMP_TYPE_TEXT, FALSE },/* "K","M","N" */
{ "GPSDestDistance", XMP_TYPE_RATIONAL, FALSE },
{ "GPSProcessingMethod",XMP_TYPE_TEXT, FALSE },
{ "GPSAreaInformation",XMP_TYPE_TEXT, FALSE },
{ "GPSDifferential", XMP_TYPE_INTEGER, FALSE },/* 0-1 */
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty xmpplus_properties[] =
{
{ "CreditLineReq", XMP_TYPE_BOOLEAN, TRUE },
{ "ReuseAllowed", XMP_TYPE_BOOLEAN, TRUE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty iptccore_properties[] =
{
/* FIXME */
{ "CreatorContactInfo",XMP_TYPE_GENERIC_STRUCTURE,TRUE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPProperty cc_properties[] =
{
{ "license", XMP_TYPE_URI, TRUE },
{ NULL, XMP_TYPE_UNKNOWN, FALSE }
};
static XMPSchema xmp_schemas_array[] =
{
/* XMP schemas defined as of January 2004 */
{ XMP_SCHEMA_DUBLIN_CORE, XMP_PREFIX_DUBLIN_CORE,
"Dublin Core", dc_properties },
{ XMP_SCHEMA_XMP_BASIC, XMP_PREFIX_XMP_BASIC,
"XMP Basic", xmp_properties },
{ XMP_SCHEMA_XMP_RIGHTS, XMP_PREFIX_XMP_RIGHTS,
"XMP Rights Management", xmprights_properties },
{ XMP_SCHEMA_XMP_MM, XMP_PREFIX_XMP_MM,
"XMP Media Management", xmpmm_properties },
{ XMP_SCHEMA_XMP_BJ, XMP_PREFIX_XMP_BJ,
"XMP Basic Job Ticket", xmpbj_properties },
{ XMP_SCHEMA_XMP_TPG, XMP_PREFIX_XMP_TPG,
"XMP Paged-Text", xmptpg_properties },
{ XMP_SCHEMA_PDF, XMP_PREFIX_PDF,
"Adobe PDF", pdf_properties },
{ XMP_SCHEMA_PHOTOSHOP, XMP_PREFIX_PHOTOSHOP,
"Photoshop", photoshop_properties },
{ XMP_SCHEMA_TIFF, XMP_PREFIX_TIFF,
"EXIF (TIFF Properties)", tiff_properties },
{ XMP_SCHEMA_EXIF, XMP_PREFIX_EXIF,
"EXIF (EXIF-specific Properties)", exif_properties },
/* Additional schemas published in March 2005 */
{ XMP_SCHEMA_XMP_PLUS, XMP_PREFIX_XMP_PLUS,
"XMP Photographic Licensing Universal System", xmpplus_properties },
{ XMP_SCHEMA_IPTC_CORE, XMP_PREFIX_IPTC_CORE,
"IPTC Core", iptccore_properties },
/* other useful namespaces */
{ "http://web.resource.org/cc/", "cc",
"Creative Commons", cc_properties },
{ "http://ns.adobe.com/iX/1.0/", "iX",
NULL, NULL },
/* XMP sub-types (not top-level schemas) */
{ "http://ns.adobe.com/xmp/Identifier/qual/1.0/", "xmpidq",
NULL, NULL },
{ "http://ns.adobe.com/xap/1.0/g/img/", "xapGImg",
NULL, NULL },
{ "http://ns.adobe.com/xap/1.0/sType/Dimensions#", "stDim",
NULL, NULL },
{ "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#", "stEvt",
NULL, NULL },
{ "http://ns.adobe.com/xap/1.0/sType/ResourceRef#", "stRef",
NULL, NULL },
{ "http://ns.adobe.com/xap/1.0/sType/Version#", "stVer",
NULL, NULL },
{ "http://ns.adobe.com/xap/1.0/sType/Job#", "stJob",
NULL, NULL },
{ NULL, NULL, NULL }
};
XMPSchema * const xmp_schemas = xmp_schemas_array;

View File

@ -0,0 +1,109 @@
/* xmp-schemas.h - standard schemas defined in the XMP specifications
*
* 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_SCHEMAS_H
#define XMP_SCHEMAS_H
G_BEGIN_DECLS
/* 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, /* ALT_THUMBS */
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_GENERIC_STRUCTURE, /* STRUCTURE */
XMP_TYPE_UNKNOWN
} XMPType;
/* 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;
/* URIs of standard XMP schemas (as of January 2004) */
#define XMP_SCHEMA_DUBLIN_CORE "http://purl.org/dc/elements/1.1/"
#define XMP_SCHEMA_XMP_BASIC "http://ns.adobe.com/xap/1.0/"
#define XMP_SCHEMA_XMP_RIGHTS "http://ns.adobe.com/xap/1.0/rights/"
#define XMP_SCHEMA_XMP_MM "http://ns.adobe.com/xap/1.0/mm/"
#define XMP_SCHEMA_XMP_BJ "http://ns.adobe.com/xap/1.0/bj/"
#define XMP_SCHEMA_XMP_TPG "http://ns.adobe.com/xap/1.0/t/pg/"
#define XMP_SCHEMA_PDF "http://ns.adobe.com/pdf/1.3/"
#define XMP_SCHEMA_PHOTOSHOP "http://ns.adobe.com/photoshop/1.0/"
#define XMP_SCHEMA_TIFF "http://ns.adobe.com/tiff/1.0/"
#define XMP_SCHEMA_EXIF "http://ns.adobe.com/exif/1.0/"
/* Additional schemas published in March 2005 */
#define XMP_SCHEMA_XMP_PLUS "http://ns.adobe.com/xap/1.0/PLUS/"
#define XMP_SCHEMA_IPTC_CORE "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"
/* recommended prefixes for the schemas listed above */
#define XMP_PREFIX_DUBLIN_CORE "dc"
#define XMP_PREFIX_XMP_BASIC "xmp"
#define XMP_PREFIX_XMP_RIGHTS "xmpRights"
#define XMP_PREFIX_XMP_MM "xmpMM"
#define XMP_PREFIX_XMP_BJ "xmpBJ"
#define XMP_PREFIX_XMP_TPG "xmpTPg"
#define XMP_PREFIX_PDF "pdf"
#define XMP_PREFIX_PHOTOSHOP "photoshop"
#define XMP_PREFIX_TIFF "tiff"
#define XMP_PREFIX_EXIF "exif"
#define XMP_PREFIX_XMP_PLUS "xmpPLUS"
#define XMP_PREFIX_IPTC_CORE "Iptc4xmpCore"
/* List of known XMP schemas and their properties */
XMPSchema * const xmp_schemas;
G_END_DECLS
#endif /* XMP_SCHEMAS_H */

View File

@ -12,7 +12,7 @@ start_schema (XMPParseContext *context,
gpointer user_data,
GError **error)
{
printf ("Schema %s = \"%s\"\n", ns_prefix, ns_uri);
g_print ("Schema %s = \"%s\"\n", ns_prefix, ns_uri);
return (gpointer) ns_prefix;
}
@ -22,7 +22,7 @@ end_schema (XMPParseContext *context,
gpointer user_data,
GError **error)
{
/* printf ("End of %s\n", user_ns_prefix); */
/* g_print ("End of %s\n", user_ns_prefix); */
}
static void
@ -40,47 +40,47 @@ set_property (XMPParseContext *context,
switch (type)
{
case XMP_PTYPE_TEXT:
printf ("\t%s:%s = \"%s\"\n", ns_prefix, name,
value[0]);
g_print ("\t%s:%s = \"%s\"\n", ns_prefix, name,
value[0]);
break;
case XMP_PTYPE_RESOURCE:
printf ("\t%s:%s @ = \"%s\"\n", ns_prefix, name,
value[0]);
g_print ("\t%s:%s @ = \"%s\"\n", ns_prefix, name,
value[0]);
break;
case XMP_PTYPE_ORDERED_LIST:
case XMP_PTYPE_UNORDERED_LIST:
printf ("\t%s:%s [] =", ns_prefix, name);
g_print ("\t%s:%s [] =", ns_prefix, name);
for (i = 0; value[i] != NULL; i++)
if (i == 0)
printf (" \"%s\"", value[i]);
g_print (" \"%s\"", value[i]);
else
printf (", \"%s\"", value[i]);
printf ("\n");
g_print (", \"%s\"", value[i]);
g_print ("\n");
break;
case XMP_PTYPE_ALT_THUMBS:
for (i = 0; value[i] != NULL; i += 2)
printf ("\t%s:%s [size = %d] = \"...\"\n", ns_prefix, name,
*(int*)(value[i])); /* FIXME: show part of image */
g_print ("\t%s:%s [size = %d] = \"...\"\n", ns_prefix, name,
*(int*)(value[i])); /* FIXME: show part of image */
break;
case XMP_PTYPE_ALT_LANG:
for (i = 0; value[i] != NULL; i += 2)
printf ("\t%s:%s [lang:%s] = \"%s\"\n", ns_prefix, name,
value[i], value[i + 1]);
g_print ("\t%s:%s [lang:%s] = \"%s\"\n", ns_prefix, name,
value[i], value[i + 1]);
break;
case XMP_PTYPE_STRUCTURE:
printf ("\tLocal schema %s = \"%s\"\n", value[0], value[1]);
g_print ("\tLocal schema %s = \"%s\"\n", value[0], value[1]);
for (i = 2; value[i] != NULL; i += 2)
printf ("\t%s:%s [%s] = \"%s\"\n", ns_prefix, name,
value[i], value[i + 1]);
g_print ("\t%s:%s [%s] = \"%s\"\n", ns_prefix, name,
value[i], value[i + 1]);
break;
default:
printf ("\t%s:%s = ?\n", ns_prefix, name);
g_print ("\t%s:%s = ?\n", ns_prefix, name);
break;
}
}
@ -92,8 +92,8 @@ print_error (XMPParseContext *context,
{
gchar *filename = user_data;
fprintf (stderr, "While parsing XMP metadata in %s:\n%s\n",
filename, error->message);
g_printerr ("While parsing XMP metadata in %s:\n%s\n",
filename, error->message);
}
static XMPParser xmp_parser = {
@ -111,7 +111,7 @@ scan_file (const gchar *filename)
GError *error;
XMPParseContext *context;
printf ("\nFile: %s\n", filename);
g_print ("\nFile: %s\n", filename);
error = NULL;
if (!g_file_get_contents (filename,
&contents,
@ -157,7 +157,7 @@ main (int argc,
}
else
{
fprintf (stderr, "Usage:\n"
g_print ("Usage:\n"
"\txmpdump file [file [...]]\n\n"
"The file(s) given on the command line will be scanned "
"for XMP metadata\n");