[mapserver-commits] r12630 - trunk/mapserver
svn at osgeo.org
svn at osgeo.org
Thu Oct 6 14:33:38 EDT 2011
Author: pramsey
Date: 2011-10-06 11:33:38 -0700 (Thu, 06 Oct 2011)
New Revision: 12630
Added:
trunk/mapserver/mapxmp.c
Modified:
trunk/mapserver/HISTORY.TXT
trunk/mapserver/Makefile.in
trunk/mapserver/README.CONFIGURE
trunk/mapserver/configure.in
trunk/mapserver/maperror.c
trunk/mapserver/mapgdal.c
trunk/mapserver/mapserver.h
Log:
Support for XMP metadata in output images (#3932) RFC 76
Modified: trunk/mapserver/HISTORY.TXT
===================================================================
--- trunk/mapserver/HISTORY.TXT 2011-10-06 18:06:34 UTC (rev 12629)
+++ trunk/mapserver/HISTORY.TXT 2011-10-06 18:33:38 UTC (rev 12630)
@@ -15,6 +15,7 @@
Current Version (SVN trunk, 6.1-dev, future 6.2):
-------------------------------------------------
+- Added XMP support for metadata embedding, RFC 76, (#3932)
- Added GetLegendGraphic Cascading support (#3923)
- Rewrite postgres TIME queries to take advantage of indexes (#3374)
Modified: trunk/mapserver/Makefile.in
===================================================================
--- trunk/mapserver/Makefile.in 2011-10-06 18:06:34 UTC (rev 12629)
+++ trunk/mapserver/Makefile.in 2011-10-06 18:33:38 UTC (rev 12630)
@@ -177,8 +177,11 @@
EXSLT_INC=@EXSLT_INC@
EXSLT_LIB=@EXSLT_LIB@
+# Optional Exempi
+EXEMPI=@EXEMPI_ENABLED@
+EXEMPI_INC=@EXEMPI_INC@
+EXEMPI_LIB=@EXEMPI_LIB@
-
# Optional FastCGI
FASTCGI=@FASTCGI_ENABLED@
FASTCGI_INC=@FASTCGI_INC@
@@ -247,7 +250,7 @@
$(AGG_INC) $(OGL_INC) $(FTGL_INC) $(PROJ_INC) $(EGIS_INC) \
$(SDE_INC) $(GDAL_INC) $(POSTGIS_INC) $(MYSQL_INC) \
$(CURL_INC) $(ORACLESPATIAL_INC) $(GEOS_INC) $(ICONV_INC) \
- $(FASTCGI_INC) $(ZLIB_INC) $(XML2_INC) $(FRIBIDI_INC) $(CAIRO_INC)
+ $(FASTCGI_INC) $(EXEMPI_INC) $(ZLIB_INC) $(XML2_INC) $(FRIBIDI_INC) $(CAIRO_INC)
FLAGS = @DEBUG_FLAGS@ $(DEFINES) $(INCLUDES)
@@ -258,7 +261,7 @@
SUP_LIBS = $(FT_LIB) $(GD_LIB) $(AGG_LIB) $(OGL_LIB) $(FTGL_LIB) $(PROJ_LIBS) \
$(JPEG_LIB) $(PNG_LIB) $(GIF_LIB) $(SDE_LIB) $(GDAL_LIB) $(POSTGIS_LIB) \
$(MYSQL_LIB) $(CURL_LIB) $(ORACLESPATIAL_LIB) $(GEOS_LIB) \
- $(THREAD_LIB) $(ICONV_LIB) $(FASTCGI_LIB) $(XSLT_LIB) $(EXSLT_LIB) \
+ $(THREAD_LIB) $(ICONV_LIB) $(FASTCGI_LIB) $(EXEMPI_LIB) $(XSLT_LIB) $(EXSLT_LIB) \
$(ZLIB_LIB) $(XML2_LIB) $(FRIBIDI_LIB) $(XTRALIBS) $(CAIRO_LIB)
# STATIC_LIBS is full filename with path of libs that will be statically linked
@@ -281,7 +284,7 @@
maprasterquery.o mapobject.o mapgeos.o classobject.o layerobject.o mapio.o mappool.o \
mapregex.o mappluginlayer.o mapogcsos.o mappostgresql.o mapcrypto.o mapowscommon.o \
maplibxml2.o mapdebug.o mapchart.o maptclutf.o mapxml.o mapkml.o mapkmlrenderer.o \
- mapogroutput.o mapwcs20.o mapogcfiltercommon.o mapunion.o mapcluster.o
+ mapogroutput.o mapwcs20.o mapogcfiltercommon.o mapunion.o mapcluster.o mapxmp.o
EXE_LIST = shp2img legend mapserv shptree shptreevis \
shptreetst scalebar sortshp mapscriptvars tile4ms \
Modified: trunk/mapserver/README.CONFIGURE
===================================================================
--- trunk/mapserver/README.CONFIGURE 2011-10-06 18:06:34 UTC (rev 12629)
+++ trunk/mapserver/README.CONFIGURE 2011-10-06 18:33:38 UTC (rev 12630)
@@ -303,10 +303,23 @@
- GEOS support is optional, and is not included by default.
MapServer requires GEOS version 2.2.3 or more recent.
-- The GEOS library can be found at http://geos.refractions.net/
+- The GEOS library can be found at http://trac.osgeo.org/geos/
+XMP Support:
+------------
+::
+
+ --with-exempi=PATH
+
+- XMP support is optional, and is not incuded by default.
+
+- XMP support allows metadata (title, attribute, license, etc) to be directly embedded in the images generated by MapServer.
+
+- The exempi library can be found at http://libopenraw.freedesktop.org/wiki/Exempi
+
+
OGR Support:
------------
Modified: trunk/mapserver/configure.in
===================================================================
--- trunk/mapserver/configure.in 2011-10-06 18:06:34 UTC (rev 12629)
+++ trunk/mapserver/configure.in 2011-10-06 18:33:38 UTC (rev 12630)
@@ -1942,6 +1942,44 @@
fi
dnl ---------------------------------------------------------------------
+dnl Exempi support
+dnl ---------------------------------------------------------------------
+
+AC_ARG_WITH(exempi,
+[ --with-exempi=DIR Specify path to exempi.],,)
+
+if test "$with_exempi" != "no" ; then
+
+ if test "$with_exempi" = "yes" ; then
+ AC_CHECK_HEADER([xmp.h],,[AC_MSG_ERROR([cannot find xmp.h])])
+ EXEMPI_INC=""
+ EXEMPI_LIB="-lexempi"
+ else
+ xmp_dir=$with_exempi
+ xmp_include=$xmp_dir/include
+ xmp_lib=$xmp_dir/lib
+ xmp_found=
+ for xmp_subdir in / /exempi /exempi-2.0/exempi; do
+ AC_CHECK_HEADER([$xmp_include$xmp_subdir/xmp.h],xmp_found=$xmp_subdir,,)
+ done
+ if test "x$xmp_found" = "x" ; then
+ AC_MSG_ERROR([cannot find xmp.h in $xmp_include])
+ else
+ EXEMPI_INC="-I$xmp_include$xmp_subdir"
+ fi
+ EXEMPI_LIB="-L$xmp_lib -lexempi"
+ fi
+
+ AC_CHECK_LIB(exempi,xmp_init,EXEMPI_ENABLED=-DUSE_EXEMPI,,$EXEMPI_LIB)
+
+fi
+
+AC_SUBST(EXEMPI_ENABLED,$EXEMPI_ENABLED)
+AC_SUBST(EXEMPI_INC, $EXEMPI_INC)
+AC_SUBST(EXEMPI_LIB, $EXEMPI_LIB)
+ALL_ENABLED="$EXEMPI_ENABLED $ALL_ENABLED"
+
+dnl ---------------------------------------------------------------------
dnl Look for libxml2 if SOS Server requested
dnl ---------------------------------------------------------------------
@@ -2866,6 +2904,7 @@
AC_MSG_RESULT([ FriBidi support: ${FRIBIDI_ENABLED}])
AC_MSG_RESULT([ Curl support: ${CURL_ENABLED}])
AC_MSG_RESULT([ FastCGI support: ${FASTCGI_ENABLED}])
+AC_MSG_RESULT([ Exempi support: ${EXEMPI_ENABLED}])
AC_MSG_RESULT([ Threading support: ${THREAD_FLAG}])
AC_MSG_RESULT([ GEOS support: ${GEOS_ENABLED}])
AC_MSG_RESULT([ XML Mapfile support: ${XMLMAPFILE_ENABLED}])
Modified: trunk/mapserver/maperror.c
===================================================================
--- trunk/mapserver/maperror.c 2011-10-06 18:06:34 UTC (rev 12629)
+++ trunk/mapserver/maperror.c 2011-10-06 18:33:38 UTC (rev 12630)
@@ -538,6 +538,9 @@
#ifdef USE_ICONV
strcat(version, " SUPPORTS=ICONV");
#endif
+#ifdef USE_EXEMPI
+ strcat(version, " SUPPORTS=XMP");
+#endif
#ifdef USE_FRIBIDI
strcat(version, " SUPPORTS=FRIBIDI");
#endif
Modified: trunk/mapserver/mapgdal.c
===================================================================
--- trunk/mapserver/mapgdal.c 2011-10-06 18:06:34 UTC (rev 12629)
+++ trunk/mapserver/mapgdal.c 2011-10-06 18:33:38 UTC (rev 12630)
@@ -162,10 +162,16 @@
outputFormatObj *format = image->format;
rasterBufferObj rb;
GDALDataType eDataType = GDT_Byte;
+ int bUseXmp = MS_FALSE;
msGDALInitialize();
memset(&rb,0,sizeof(rasterBufferObj));
+
+#ifdef USE_EXEMPI
+ bUseXmp = msXmpPresent(map);
+#endif
+
/* -------------------------------------------------------------------- */
/* Identify the proposed output driver. */
/* -------------------------------------------------------------------- */
@@ -193,7 +199,7 @@
pszExtension = "img.tmp";
#ifdef GDAL_DCAP_VIRTUALIO
- if( GDALGetMetadataItem( hOutputDriver, GDAL_DCAP_VIRTUALIO, NULL )
+ if( bUseXmp == MS_FALSE && GDALGetMetadataItem( hOutputDriver, GDAL_DCAP_VIRTUALIO, NULL )
!= NULL )
{
CleanVSIDir( "/vsimem/msout" );
@@ -527,10 +533,27 @@
GDALClose( hMemDS );
GDALClose( hOutputDS );
-
msReleaseLock( TLOCK_GDAL );
+
/* -------------------------------------------------------------------- */
+/* Are we writing license info into the image? */
+/* If so, add it to the temp file on disk now. */
+/* -------------------------------------------------------------------- */
+#ifdef USE_EXEMPI
+ if ( bUseXmp == MS_TRUE )
+ {
+ if( msXmpWrite(map, filename) == MS_FAILURE )
+ {
+ /* Something bad happened. */
+ msSetError( MS_MISCERR, "XMP write to %s failed.\n%s",
+ "msSaveImageGDAL()", filename);
+ return MS_FAILURE;
+ }
+ }
+#endif
+
+/* -------------------------------------------------------------------- */
/* Is this supposed to be a temporary file? If so, stream to */
/* stdout and delete the file. */
/* -------------------------------------------------------------------- */
Modified: trunk/mapserver/mapserver.h
===================================================================
--- trunk/mapserver/mapserver.h 2011-10-06 18:06:34 UTC (rev 12629)
+++ trunk/mapserver/mapserver.h 2011-10-06 18:33:38 UTC (rev 12630)
@@ -2479,8 +2479,12 @@
MS_DLL_EXPORT void msHexEncode(const unsigned char *in, char *out, int numbytes);
MS_DLL_EXPORT int msHexDecode(const char *in, unsigned char *out, int numchars);
+/* ==================================================================== */
+/* prototypes for functions in mapxmp.c */
+/* ==================================================================== */
+MS_DLL_EXPORT int msXmpPresent(mapObj *map);
+MS_DLL_EXPORT int msXmpWrite(mapObj *map, const char *filename);
-
/* ==================================================================== */
/* prototypes for functions in mapgeomtransform.c */
/* ==================================================================== */
Added: trunk/mapserver/mapxmp.c
===================================================================
--- trunk/mapserver/mapxmp.c (rev 0)
+++ trunk/mapserver/mapxmp.c 2011-10-06 18:33:38 UTC (rev 12630)
@@ -0,0 +1,304 @@
+/******************************************************************************
+ * $Id: mapchart.c 11880 2011-07-07 19:51:37Z sdlime $
+ *
+ * Project: MapServer
+ * Purpose: XMP embedded image metadata (MS-RFC-7X)
+ * Author: Paul Ramsey <pramsey at opengeo.org>
+ *
+ ******************************************************************************
+ * Copyright (c) 1996-2007 Regents of the University of Minnesota.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies of this Software or works derived from this Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#include "mapserver.h"
+
+
+#ifdef USE_EXEMPI
+
+/* To pull parts out of hash keys */
+#include <regex.h>
+
+/* To write the XMP XML into the files */
+#include <xmp.h>
+#include <xmpconsts.h>
+
+/**
+* Get standard Exempi namespace URI for a given namespace string
+*/
+static const char*
+msXmpUri(char *ns_name)
+{
+ /* Creative Commons */
+ if( strcmp(ns_name, "cc") == 0 )
+ return NS_CC;
+ /* Dublin Core */
+ else if( strcmp(ns_name, "dc") == 0 )
+ return NS_DC;
+ /* XMP Meta */
+ else if( strcmp(ns_name, "meta") == 0 )
+ return NS_XMP_META;
+ /* XMP Rights */
+ else if( strcmp(ns_name, "rights") == 0 )
+ return NS_XAP_RIGHTS;
+ /* EXIF */
+ else if( strcmp(ns_name, "exif") == 0 )
+ return NS_EXIF;
+ /* TIFF */
+ else if( strcmp(ns_name, "tiff") == 0 )
+ return NS_TIFF;
+ /* Photoshop Camera Raw Schema */
+ else if( strcmp(ns_name, "crs") == 0 )
+ return NS_CAMERA_RAW_SETTINGS;
+ /* Photoshop */
+ else if( strcmp(ns_name, "photoshop") == 0 )
+ return NS_PHOTOSHOP;
+ else
+ return NULL;
+}
+
+#endif
+
+/**
+* Is there any XMP metadata in the map file for us to worry about?
+*/
+int
+msXmpPresent( mapObj *map )
+{
+#ifdef USE_EXEMPI
+
+ /* Read the WEB.METADATA */
+ hashTableObj hash_metadata = map->web.metadata;
+ const char *key = NULL;
+ int rv = MS_FALSE;
+
+ /* Check all the keys for "xmp_" start pattern */
+ key = msFirstKeyFromHashTable(&hash_metadata);
+ do
+ {
+ /* Found one! Break out and return true */
+ if ( strcasestr(key, "xmp_") == key )
+ {
+ rv = MS_TRUE;
+ break;
+ }
+ }
+ while( (key = msNextKeyFromHashTable(&hash_metadata, key)) );
+
+ return rv;
+
+#else
+ return MS_FALSE;
+#endif
+}
+
+
+/**
+* Is there any XMP metadata in the map file for us to worry about?
+*/
+int
+msXmpWrite( mapObj *map, const char *filename )
+{
+#ifdef USE_EXEMPI
+
+ /* Should hold our keys */
+ hashTableObj hash_metadata = map->web.metadata;
+ /* Temporary place for custom name spaces */
+ hashTableObj hash_ns;
+ /* We use regex to strip out the namespace and XMP key value from the metadata key */
+ regex_t xmp_regex;
+ const char *xmp_ns_str = "^xmp_(.+)_namespace$";
+ const char *xmp_tag_str = "^xmp_(.+)_(.+)$";
+ const char *key = NULL;
+ static int regflags = REG_ICASE | REG_EXTENDED;
+
+ /* XMP object and file pointers */
+ XmpPtr xmp;
+ XmpFilePtr f;
+
+ /* Force the hash table to empty */
+ hash_ns.numitems = 0;
+
+ /* Prepare XMP library */
+ xmp_init();
+ f = xmp_files_open_new(filename, XMP_OPEN_FORUPDATE);
+ if ( ! f )
+ {
+ msSetError( MS_MISCERR,
+ "Unable to open temporary file '%s' to write XMP info",
+ "msXmpWrite()", filename );
+ return MS_FAILURE;
+ }
+
+ /* Create a new XMP structure if the file doesn't already have one */
+ xmp = xmp_files_get_new_xmp(f);
+ if ( xmp == NULL )
+ xmp = xmp_new_empty();
+
+ /* Check we can write to the file */
+ if ( ! xmp_files_can_put_xmp(f, xmp) )
+ {
+ msSetError( MS_MISCERR,
+ "Unable to write XMP information to '%s'",
+ "msXmpWrite()", filename );
+ return MS_FAILURE;
+ }
+
+ /* Compile our "xmp_*_namespace" regex */
+ if ( regcomp(&xmp_regex, xmp_ns_str, regflags) )
+ {
+ msSetError( MS_MISCERR,
+ "Unable compile regex '%s'",
+ "msXmpWrite()", xmp_ns_str );
+ return MS_FAILURE;
+ }
+
+ /* Check all the keys for "xmp_*_namespace" pattern */
+ initHashTable(&hash_ns);
+ key = msFirstKeyFromHashTable(&hash_metadata);
+ do
+ {
+ /* Our regex has one match slot */
+ regmatch_t matches[2];
+
+ /* Found a custom namespace entry! Store it for later. */
+ if ( 0 == regexec(&xmp_regex, key, 2, matches, 0) )
+ {
+ size_t ns_size = 0;
+ char *ns_name = NULL;
+ const char *ns_uri;
+
+ /* Copy in the namespace name */
+ ns_size = matches[1].rm_eo - matches[1].rm_so;
+ ns_name = msSmallMalloc(ns_size + 1);
+ memcpy(ns_name, key + matches[1].rm_so, ns_size);
+ ns_name[ns_size] = 0; /* null terminate */
+
+ /* Copy in the namespace uri */
+ ns_uri = msLookupHashTable(&hash_metadata, key);
+ msInsertHashTable(&hash_ns, ns_name, ns_uri);
+ xmp_register_namespace(ns_uri, ns_name, NULL);
+ msFree(ns_name);
+ }
+ }
+ while( (key = msNextKeyFromHashTable(&hash_metadata, key)) );
+ /* Clean up regex */
+ regfree(&xmp_regex);
+
+
+ /* Compile our "xmp_*_*" regex */
+ if ( regcomp(&xmp_regex, xmp_tag_str, regflags) )
+ {
+ msFreeHashItems(&hash_ns);
+ msSetError( MS_MISCERR,
+ "Unable compile regex '%s'",
+ "msXmpWrite()", xmp_tag_str );
+ return MS_FAILURE;
+ }
+
+ /* Check all the keys for "xmp_*_*" pattern */
+ key = msFirstKeyFromHashTable(&hash_metadata);
+ do
+ {
+ /* Our regex has two match slots */
+ regmatch_t matches[3];
+
+ /* Found a namespace entry! Write it into XMP. */
+ if ( 0 == regexec(&xmp_regex, key, 3, matches, 0) )
+ {
+ /* Get the namespace and tag name */
+ size_t ns_name_size = matches[1].rm_eo - matches[1].rm_so;
+ size_t ns_tag_size = matches[2].rm_eo - matches[2].rm_so;
+ char *ns_name = msSmallMalloc(ns_name_size + 1);
+ char *ns_tag = msSmallMalloc(ns_tag_size + 1);
+ const char *ns_uri = NULL;
+ memcpy(ns_name, key + matches[1].rm_so, ns_name_size);
+ memcpy(ns_tag, key + matches[2].rm_so, ns_tag_size);
+ ns_name[ns_name_size] = 0; /* null terminate */
+ ns_tag[ns_tag_size] = 0; /* null terminate */
+
+ if ( strcmp(ns_tag,"namespace") == 0 )
+ {
+ msFree(ns_name);
+ msFree(ns_tag);
+ continue;
+ }
+
+ /* If this is a default name space?... */
+ if ( (ns_uri = msXmpUri(ns_name)) )
+ {
+ xmp_register_namespace(ns_uri, ns_name, NULL);
+ xmp_set_property(xmp, ns_uri, ns_tag, msLookupHashTable(&hash_metadata, key), 0);
+ }
+ /* Or maybe it's a custom one?... */
+ else if ( (ns_uri = msLookupHashTable(&hash_ns, ns_name)) )
+ {
+ xmp_set_property(xmp, ns_uri, ns_tag, msLookupHashTable(&hash_metadata, key), 0);
+ }
+ /* Or perhaps we're screwed. */
+ else
+ {
+ msFreeHashItems(&hash_ns);
+ msFree(ns_name);
+ msFree(ns_tag);
+ msSetError( MS_MISCERR,
+ "Unable to identify XMP namespace '%s' in metadata key '%s'",
+ "msXmpWrite()", ns_name, key );
+ return MS_FAILURE;
+ }
+ msFree(ns_name);
+ msFree(ns_tag);
+ }
+ }
+ while( (key = msNextKeyFromHashTable(&hash_metadata, key)) );
+
+ /* Clean up regex */
+ regfree(&xmp_regex);
+
+ /* Write out the XMP */
+ if ( !xmp_files_put_xmp(f, xmp) )
+ {
+ msFreeHashItems(&hash_ns);
+ msSetError( MS_MISCERR,
+ "Unable to execute '%s' on pointer %p",
+ "msXmpWrite()", "xmp_files_put_xmp", f );
+ return MS_FAILURE;
+ }
+
+ /* Write out the file and flush */
+ if ( !xmp_files_close(f, XMP_CLOSE_SAFEUPDATE) )
+ {
+ msFreeHashItems(&hash_ns);
+ msSetError( MS_MISCERR,
+ "Unable to execute '%s' on pointer %p",
+ "msXmpWrite()", "xmp_files_close", f );
+ return MS_FAILURE;
+ }
+
+ msFreeHashItems(&hash_ns);
+ xmp_free(xmp);
+ xmp_terminate();
+
+ return MS_SUCCESS;
+
+#else
+ return MS_FAILURE;
+#endif
+}
+
More information about the mapserver-commits
mailing list