]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/property.c
(no commit message)
[imagemagick] / MagickCore / property.c
index afe15293be5284aafa635b1f6f057c80d79f6a60..507806d5d68cbcc2b0c2c30d95e258129f5320ca 100644 (file)
@@ -17,7 +17,7 @@
 %                                 March 2000                                  %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
 %  dedicated to making software imaging solutions freely available.           %
 %                                                                             %
 %  You may not use this file except in compliance with the License.  You may  %
   Include declarations.
 */
 #include "MagickCore/studio.h"
+#include "MagickCore/artifact.h"
 #include "MagickCore/attribute.h"
 #include "MagickCore/cache.h"
 #include "MagickCore/color.h"
+#include "MagickCore/color-private.h"
+#include "MagickCore/colorspace-private.h"
 #include "MagickCore/compare.h"
 #include "MagickCore/constitute.h"
 #include "MagickCore/draw.h"
@@ -56,8 +59,8 @@
 #include "MagickCore/geometry.h"
 #include "MagickCore/histogram.h"
 #include "MagickCore/image.h"
-#include "MagickCore/image.h"
 #include "MagickCore/layer.h"
+#include "MagickCore/locale-private.h"
 #include "MagickCore/list.h"
 #include "MagickCore/magick.h"
 #include "MagickCore/memory_.h"
 #include "MagickCore/quantum.h"
 #include "MagickCore/resource_.h"
 #include "MagickCore/splay-tree.h"
-#include "MagickCore/signature-private.h"
+#include "MagickCore/signature.h"
 #include "MagickCore/statistic.h"
 #include "MagickCore/string_.h"
 #include "MagickCore/string-private.h"
 #include "MagickCore/token.h"
+#include "MagickCore/token-private.h"
 #include "MagickCore/utility.h"
+#include "MagickCore/utility-private.h"
 #include "MagickCore/version.h"
 #include "MagickCore/xml-tree.h"
+#include "MagickCore/xml-tree-private.h"
 \f
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -89,7 +95,7 @@
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  CloneImageProperties() clones one or more image properties.
+%  CloneImageProperties() clones all the image properties to another image.
 %
 %  The format of the CloneImageProperties method is:
 %
@@ -108,11 +114,11 @@ MagickExport MagickBooleanType CloneImageProperties(Image *image,
 {
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
+  if( IfMagickTrue(image->debug) )
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   assert(clone_image != (const Image *) NULL);
   assert(clone_image->signature == MagickSignature);
-  if (clone_image->debug != MagickFalse)
+  if( IfMagickTrue(clone_image->debug) )
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
       clone_image->filename);
   (void) CopyMagickString(image->filename,clone_image->filename,MaxTextExtent);
@@ -134,14 +140,12 @@ MagickExport MagickBooleanType CloneImageProperties(Image *image,
   image->directory=(char *) NULL;
   (void) CloneString(&image->geometry,clone_image->geometry);
   image->offset=clone_image->offset;
-  image->x_resolution=clone_image->x_resolution;
-  image->y_resolution=clone_image->y_resolution;
+  image->resolution.x=clone_image->resolution.x;
+  image->resolution.y=clone_image->resolution.y;
   image->page=clone_image->page;
   image->tile_offset=clone_image->tile_offset;
   image->extract_info=clone_image->extract_info;
-  image->bias=clone_image->bias;
   image->filter=clone_image->filter;
-  image->blur=clone_image->blur;
   image->fuzz=clone_image->fuzz;
   image->interlace=clone_image->interlace;
   image->interpolate=clone_image->interpolate;
@@ -183,12 +187,14 @@ MagickExport MagickBooleanType CloneImageProperties(Image *image,
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  DefineImageProperty() associates a key/value pair with an image property.
+%  DefineImageProperty() associates an assignment string of the form
+%  "key=value" with per-image artifact. It is equivelent to
+%  SetImageProperity().
 %
 %  The format of the DefineImageProperty method is:
 %
 %      MagickBooleanType DefineImageProperty(Image *image,
-%        const char *property)
+%        const char *property,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -196,9 +202,11 @@ MagickExport MagickBooleanType CloneImageProperties(Image *image,
 %
 %    o property: the image property.
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
 MagickExport MagickBooleanType DefineImageProperty(Image *image,
-  const char *property)
+  const char *property,ExceptionInfo *exception)
 {
   char
     key[MaxTextExtent],
@@ -217,7 +225,7 @@ MagickExport MagickBooleanType DefineImageProperty(Image *image,
   if (*p == '=')
     (void) CopyMagickString(value,p+1,MaxTextExtent);
   *p='\0';
-  return(SetImageProperty(image,key,value));
+  return(SetImageProperty(image,key,value,exception));
 }
 \f
 /*
@@ -249,7 +257,7 @@ MagickExport MagickBooleanType DeleteImageProperty(Image *image,
 {
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
+  if( IfMagickTrue(image->debug) )
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
       image->filename);
   if (image->properties == (void *) NULL)
@@ -268,8 +276,8 @@ MagickExport MagickBooleanType DeleteImageProperty(Image *image,
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  DestroyImageProperties() releases memory associated with image property
-%  values.
+%  DestroyImageProperties() destroys all properities and associated memory
+%  attached to the given image.
 %
 %  The format of the DestroyDefines method is:
 %
@@ -284,7 +292,7 @@ MagickExport void DestroyImageProperties(Image *image)
 {
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
+  if( IfMagickTrue(image->debug) )
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
       image->filename);
   if (image->properties != (void *) NULL)
@@ -327,6 +335,12 @@ MagickExport MagickBooleanType FormatImageProperty(Image *image,
   char
     value[MaxTextExtent];
 
+  ExceptionInfo
+    *exception;
+
+  MagickBooleanType
+    status;
+
   ssize_t
     n;
 
@@ -337,7 +351,10 @@ MagickExport MagickBooleanType FormatImageProperty(Image *image,
   n=FormatLocaleStringList(value,MaxTextExtent,format,operands);
   (void) n;
   va_end(operands);
-  return(SetImageProperty(image,property,value));
+  exception=AcquireExceptionInfo();
+  status=SetImageProperty(image,property,value,exception);
+  exception=DestroyExceptionInfo(exception);
+  return(status);
 }
 \f
 /*
@@ -353,9 +370,13 @@ MagickExport MagickBooleanType FormatImageProperty(Image *image,
 %
 %  GetImageProperty() gets a value associated with an image property.
 %
+%  The returned string is a constant string in the tree and should NOT be
+%  freed by the caller.
+%
 %  The format of the GetImageProperty method is:
 %
-%      const char *GetImageProperty(const Image *image,const char *key)
+%      const char *GetImageProperty(const Image *image,const char *key,
+%        ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -363,6 +384,8 @@ MagickExport MagickBooleanType FormatImageProperty(Image *image,
 %
 %    o key: the key.
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
 
 static char
@@ -371,7 +394,8 @@ static char
   *TraceSVGClippath(const unsigned char *,size_t,const size_t,
     const size_t);
 
-static MagickBooleanType GetIPTCProperty(const Image *image,const char *key)
+static MagickBooleanType GetIPTCProperty(const Image *image,const char *key,
+  ExceptionInfo *exception)
 {
   char
     *attribute,
@@ -431,7 +455,8 @@ static MagickBooleanType GetIPTCProperty(const Image *image,const char *key)
       return(MagickFalse);
     }
   attribute[strlen(attribute)-1]='\0';
-  (void) SetImageProperty((Image *) image,key,(const char *) attribute);
+  (void) SetImageProperty((Image *) image,key,(const char *) attribute,
+    exception);
   attribute=DestroyString(attribute);
   return(MagickTrue);
 }
@@ -443,6 +468,13 @@ static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
   return(y);
 }
 
+static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
+{
+  if (x < y)
+    return(x);
+  return(y);
+}
+
 static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
 {
   int
@@ -513,7 +545,8 @@ static inline unsigned short ReadPropertyMSBShort(const unsigned char **p,
   return((unsigned short) (value & 0xffff));
 }
 
-static MagickBooleanType Get8BIMProperty(const Image *image,const char *key)
+static MagickBooleanType Get8BIMProperty(const Image *image,const char *key,
+  ExceptionInfo *exception)
 {
   char
     *attribute,
@@ -567,7 +600,7 @@ static MagickBooleanType Get8BIMProperty(const Image *image,const char *key)
   status=MagickFalse;
   length=GetStringInfoLength(profile);
   info=GetStringInfoDatum(profile);
-  while ((length > 0) && (status == MagickFalse))
+  while ((length > 0) && IfMagickFalse(status))
   {
     if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
       continue;
@@ -577,7 +610,7 @@ static MagickBooleanType Get8BIMProperty(const Image *image,const char *key)
       continue;
     if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
       continue;
-    id=(ssize_t) ReadPropertyMSBShort(&info,&length);
+    id=(ssize_t) ((int) ReadPropertyMSBShort(&info,&length));
     if (id < (ssize_t) start)
       continue;
     if (id > (ssize_t) stop)
@@ -588,7 +621,7 @@ static MagickBooleanType Get8BIMProperty(const Image *image,const char *key)
     if ((count != 0) && ((size_t) count <= length))
       {
         resource=(char *) NULL;
-        if (~(1UL*count) >= (MaxTextExtent-1))
+        if (~((size_t) count) >= (MaxTextExtent-1))
           resource=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
             sizeof(*resource));
         if (resource != (char *) NULL)
@@ -608,7 +641,7 @@ static MagickBooleanType Get8BIMProperty(const Image *image,const char *key)
             No name match, scroll forward and try next.
           */
           info+=count;
-          length-=count;
+          length-=MagickMin(count,(ssize_t) length);
           continue;
         }
     if ((*name == '#') && (sub_number != 1))
@@ -618,14 +651,14 @@ static MagickBooleanType Get8BIMProperty(const Image *image,const char *key)
         */
         sub_number--;
         info+=count;
-        length-=count;
+        length-=MagickMin(count,(ssize_t) length);
         continue;
       }
     /*
       We have the resource of interest.
     */
     attribute=(char *) NULL;
-    if (~(1UL*count) >= (MaxTextExtent-1))
+    if (~((size_t) count) >= (MaxTextExtent-1))
       attribute=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
         sizeof(*attribute));
     if (attribute != (char *) NULL)
@@ -633,10 +666,10 @@ static MagickBooleanType Get8BIMProperty(const Image *image,const char *key)
         (void) CopyMagickMemory(attribute,(char *) info,(size_t) count);
         attribute[count]='\0';
         info+=count;
-        length-=count;
+        length-=MagickMin(count,(ssize_t) length);
         if ((id <= 1999) || (id >= 2999))
           (void) SetImageProperty((Image *) image,key,(const char *)
-            attribute);
+            attribute,exception);
         else
           {
             char
@@ -648,7 +681,8 @@ static MagickBooleanType Get8BIMProperty(const Image *image,const char *key)
             else
               path=TracePSClippath((unsigned char *) attribute,(size_t) count,
                 image->columns,image->rows);
-            (void) SetImageProperty((Image *) image,key,(const char *) path);
+            (void) SetImageProperty((Image *) image,key,(const char *) path,
+              exception);
             path=DestroyString(path);
           }
         attribute=DestroyString(attribute);
@@ -694,7 +728,7 @@ static inline size_t ReadPropertyLong(const EndianType endian,
 }
 
 static MagickBooleanType GetEXIFProperty(const Image *image,
-  const char *property)
+  const char *property,ExceptionInfo *exception)
 {
 #define MaxDirectoryStack  16
 #define EXIF_DELIMITER  "\n"
@@ -715,7 +749,7 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
 #define TAG_GPS_OFFSET  0x8825
 #define TAG_INTEROP_OFFSET  0xa005
 
-#define EXIFMultipleValues(size, format, arg) \
+#define EXIFMultipleValues(size,format,arg) \
 { \
    ssize_t \
      component; \
@@ -741,7 +775,7 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
    value=AcquireString(buffer); \
 }
 
-#define EXIFMultipleFractions(size, format, arg1, arg2) \
+#define EXIFMultipleFractions(size,format,arg1,arg2) \
 { \
    ssize_t \
      component; \
@@ -757,7 +791,7 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
    for (component=0; component < components; component++) \
    { \
      length+=FormatLocaleString(buffer+length,MaxTextExtent-length, \
-       format", ",arg1, arg2); \
+       format", ",(arg1),(arg2)); \
      if (length >= (MaxTextExtent-1)) \
        length=MaxTextExtent-1; \
      p1+=size; \
@@ -773,7 +807,9 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
       *directory;
 
     size_t
-      entry,
+      entry;
+
+    ssize_t
       offset;
   } DirectoryInfo;
 
@@ -1059,7 +1095,7 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
       { 0x1001c, "exif:GPSAreaInformation" },
       { 0x1001d, "exif:GPSDateStamp" },
       { 0x1001e, "exif:GPSDifferential" },
-      {  0x0000, NULL}
+      { 0x00000, (const char *) NULL }
     };
 
   const StringInfo
@@ -1085,14 +1121,17 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
     entry,
     length,
     number_entries,
-    tag_offset,
     tag;
 
+  SplayTreeInfo
+    *exif_resources;
+
   ssize_t
     all,
     id,
     level,
     offset,
+    tag_offset,
     tag_value;
 
   static int
@@ -1208,7 +1247,7 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
   }
   if (length < 16)
     return(MagickFalse);
-  id=(ssize_t) ReadPropertyShort(LSBEndian,exif);
+  id=(ssize_t) ((int) ReadPropertyShort(LSBEndian,exif));
   endian=LSBEndian;
   if (id == 0x4949)
     endian=LSBEndian;
@@ -1223,7 +1262,7 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
     This the offset to the first IFD.
   */
   offset=(ssize_t) ((int) ReadPropertyLong(endian,exif+4));
-  if ((size_t) offset >= length)
+  if ((offset < 0) || (size_t) offset >= length)
     return(MagickFalse);
   /*
     Set the pointer to the first IFD and follow it were it leads.
@@ -1233,6 +1272,8 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
   level=0;
   entry=0;
   tag_offset=0;
+  exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
+    (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
   do
   {
     /*
@@ -1248,7 +1289,7 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
     /*
       Determine how many entries there are in the current IFD.
     */
-    number_entries=ReadPropertyShort(endian,directory);
+    number_entries=(size_t) ((int) ReadPropertyShort(endian,directory));
     for ( ; entry < number_entries; entry++)
     {
       register unsigned char
@@ -1256,19 +1297,24 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
         *q;
 
       size_t
-        format,
-        number_bytes;
+        format;
 
       ssize_t
+        number_bytes,
         components;
 
-      q=(unsigned char *) (directory+2+(12*entry));
-      tag_value=(ssize_t) (ReadPropertyShort(endian,q)+tag_offset);
-      format=(size_t) ReadPropertyShort(endian,q+2);
+      q=(unsigned char *) (directory+(12*entry)+2);
+      if (GetValueFromSplayTree(exif_resources,q) == q)
+        break;
+      (void) AddValueToSplayTree(exif_resources,q,q);
+      tag_value=(ssize_t) ((int) ReadPropertyShort(endian,q)+tag_offset);
+      format=(size_t) ((int) ReadPropertyShort(endian,q+2));
       if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes)))
         break;
       components=(ssize_t) ((int) ReadPropertyLong(endian,q+4));
       number_bytes=(size_t) components*tag_bytes[format];
+      if (number_bytes < components)
+        break;  /* prevent overflow */
       if (number_bytes <= 4)
         p=q+8;
       else
@@ -1280,6 +1326,8 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
             The directory entry contains an offset.
           */
           offset=(ssize_t) ((int) ReadPropertyLong(endian,q+8));
+          if ((ssize_t) (offset+number_bytes) < offset)
+            continue;  /* prevent overflow */
           if ((size_t) (offset+number_bytes) > length)
             continue;
           p=(unsigned char *) (exif+offset);
@@ -1290,13 +1338,14 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
             buffer[MaxTextExtent],
             *value;
 
+          value=(char *) NULL;
+          *buffer='\0';
           switch (format)
           {
             case EXIF_FMT_BYTE:
             case EXIF_FMT_UNDEFINED:
             {
-              EXIFMultipleValues(1,"%.20g",(double)
-                (*(unsigned char *) p1));
+              EXIFMultipleValues(1,"%.20g",(double) (*(unsigned char *) p1));
               break;
             }
             case EXIF_FMT_SBYTE:
@@ -1317,27 +1366,27 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
             case EXIF_FMT_ULONG:
             {
               EXIFMultipleValues(4,"%.20g",(double)
-                ReadPropertyLong(endian,p1));
+                ((int) ReadPropertyLong(endian,p1)));
               break;
             }
             case EXIF_FMT_SLONG:
             {
               EXIFMultipleValues(4,"%.20g",(double)
-                ReadPropertyLong(endian,p1));
+                ((int) ReadPropertyLong(endian,p1)));
               break;
             }
             case EXIF_FMT_URATIONAL:
             {
               EXIFMultipleFractions(8,"%.20g/%.20g",(double)
-                ReadPropertyLong(endian,p1),(double)
-                ReadPropertyLong(endian,p1+4));
+                ((int) ReadPropertyLong(endian,p1)),(double)
+                ((int) ReadPropertyLong(endian,p1+4)));
               break;
             }
             case EXIF_FMT_SRATIONAL:
             {
               EXIFMultipleFractions(8,"%.20g/%.20g",(double)
-                ReadPropertyLong(endian,p1),(double)
-                ReadPropertyLong(endian,p1+4));
+                ((int) ReadPropertyLong(endian,p1)),(double)
+                ((int) ReadPropertyLong(endian,p1+4)));
               break;
             }
             case EXIF_FMT_SINGLE:
@@ -1354,7 +1403,7 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
             case EXIF_FMT_STRING:
             {
               value=(char *) NULL;
-              if (~(1UL*number_bytes) >= 1)
+              if (~((size_t) number_bytes) >= 1)
                 value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
                   sizeof(*value));
               if (value != (char *) NULL)
@@ -1376,12 +1425,14 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
           if (value != (char *) NULL)
             {
               char
-                key[MaxTextExtent];
+                *key;
 
               register const char
                 *p;
 
-              (void) CopyMagickString(key,property,MaxTextExtent);
+              key=AcquireString(property);
+              if (level == 2)
+                (void) SubstituteString(&key,"exif:","exif:thumbnail:");
               switch (all)
               {
                 case 1:
@@ -1403,8 +1454,7 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
                         break;
                       }
                   }
-                  (void) FormatLocaleString(key,MaxTextExtent,"%s",
-                    description);
+                  (void) FormatLocaleString(key,MaxTextExtent,"%s",description);
                   break;
                 }
                 case 2:
@@ -1426,25 +1476,26 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
                 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
                   image->properties,key);
               if (p == (const char *) NULL)
-                (void) SetImageProperty((Image *) image,key,value);
+                (void) SetImageProperty((Image *) image,key,value,exception);
               value=DestroyString(value);
+              key=DestroyString(key);
               status=MagickTrue;
             }
         }
         if ((tag_value == TAG_EXIF_OFFSET) ||
-            (tag_value == TAG_INTEROP_OFFSET) ||
-            (tag_value == TAG_GPS_OFFSET))
+            (tag_value == TAG_INTEROP_OFFSET) || (tag_value == TAG_GPS_OFFSET))
           {
-            size_t
+            ssize_t
               offset;
 
-            offset=(size_t) ReadPropertyLong(endian,p);
-            if ((offset < length) && (level < (MaxDirectoryStack-2)))
+            offset=(ssize_t) ((int) ReadPropertyLong(endian,p));
+            if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
               {
-                size_t
+                ssize_t
                   tag_offset1;
 
-                tag_offset1=(tag_value == TAG_GPS_OFFSET) ? 0x10000UL : 0UL;
+                tag_offset1=(ssize_t) ((tag_value == TAG_GPS_OFFSET) ? 0x10000 :
+                  0);
                 directory_stack[level].directory=directory;
                 entry++;
                 directory_stack[level].entry=entry;
@@ -1456,9 +1507,9 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
                 level++;
                 if ((directory+2+(12*number_entries)) > (exif+length))
                   break;
-                offset=(size_t) ReadPropertyLong(endian,directory+2+(12*
-                  number_entries));
-                if ((offset != 0) && (offset < length) &&
+                offset=(ssize_t) ((int) ReadPropertyLong(endian,directory+2+(12*
+                  number_entries)));
+                if ((offset != 0) && ((size_t) offset < length) &&
                     (level < (MaxDirectoryStack-2)))
                   {
                     directory_stack[level].directory=exif+offset;
@@ -1471,11 +1522,11 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
           }
     }
   } while (level > 0);
+  exif_resources=DestroySplayTree(exif_resources);
   return(status);
 }
 
-static MagickBooleanType GetXMPProperty(const Image *image,
-  const char *property)
+static MagickBooleanType GetXMPProperty(const Image *image,const char *property)
 {
   char
     *xmp_profile;
@@ -1552,8 +1603,7 @@ static MagickBooleanType GetXMPProperty(const Image *image,
 }
 
 static char *TracePSClippath(const unsigned char *blob,size_t length,
-  const size_t magick_unused(columns),
-  const size_t magick_unused(rows))
+  const size_t magick_unused(columns),const size_t magick_unused(rows))
 {
   char
     *path,
@@ -1612,7 +1662,7 @@ static char *TracePSClippath(const unsigned char *blob,size_t length,
   in_subpath=MagickFalse;
   while (length > 0)
   {
-    selector=(ssize_t) ReadPropertyMSBShort(&blob,&length);
+    selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
     switch (selector)
     {
       case 0:
@@ -1621,15 +1671,15 @@ static char *TracePSClippath(const unsigned char *blob,size_t length,
         if (knot_count != 0)
           {
             blob+=24;
-            length-=24;
+            length-=MagickMin(24,(ssize_t) length);
             break;
           }
         /*
           Expected subpath length record.
         */
-        knot_count=(ssize_t) ReadPropertyMSBShort(&blob,&length);
+        knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
         blob+=22;
-        length-=22;
+        length-=MagickMin(22,(ssize_t) length);
         break;
       }
       case 1:
@@ -1643,7 +1693,7 @@ static char *TracePSClippath(const unsigned char *blob,size_t length,
               Unexpected subpath knot
             */
             blob+=24;
-            length-=24;
+            length-=MagickMin(24,(ssize_t) length);
             break;
           }
         /*
@@ -1655,8 +1705,8 @@ static char *TracePSClippath(const unsigned char *blob,size_t length,
             xx,
             yy;
 
-          yy=ReadPropertyMSBLong(&blob,&length);
-          xx=ReadPropertyMSBLong(&blob,&length);
+          yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
+          xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
           x=(ssize_t) xx;
           if (xx > 2147483647)
             x=(ssize_t) xx-4294967295U-1;
@@ -1666,7 +1716,7 @@ static char *TracePSClippath(const unsigned char *blob,size_t length,
           point[i].x=(double) x/4096/4096;
           point[i].y=1.0-(double) y/4096/4096;
         }
-        if (in_subpath == MagickFalse)
+        if( IfMagickFalse(in_subpath) )
           {
             (void) FormatLocaleString(message,MaxTextExtent,"  %g %g m\n",
               point[1].x,point[1].y);
@@ -1744,7 +1794,7 @@ static char *TracePSClippath(const unsigned char *blob,size_t length,
       default:
       {
         blob+=24;
-        length-=24;
+        length-=MagickMin(24,(ssize_t) length);
         break;
       }
     }
@@ -1809,7 +1859,7 @@ static char *TraceSVGClippath(const unsigned char *blob,size_t length,
   in_subpath=MagickFalse;
   while (length != 0)
   {
-    selector=(ssize_t) ReadPropertyMSBShort(&blob,&length);
+    selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
     switch (selector)
     {
       case 0:
@@ -1818,15 +1868,15 @@ static char *TraceSVGClippath(const unsigned char *blob,size_t length,
         if (knot_count != 0)
           {
             blob+=24;
-            length-=24;
+            length-=MagickMin(24,(ssize_t) length);
             break;
           }
         /*
           Expected subpath length record.
         */
-        knot_count=(ssize_t) ReadPropertyMSBShort(&blob,&length);
+        knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
         blob+=22;
-        length-=22;
+        length-=MagickMin(22,(ssize_t) length);
         break;
       }
       case 1:
@@ -1840,7 +1890,7 @@ static char *TraceSVGClippath(const unsigned char *blob,size_t length,
               Unexpected subpath knot.
             */
             blob+=24;
-            length-=24;
+            length-=MagickMin(24,(ssize_t) length);
             break;
           }
         /*
@@ -1852,8 +1902,8 @@ static char *TraceSVGClippath(const unsigned char *blob,size_t length,
             xx,
             yy;
 
-          yy=ReadPropertyMSBLong(&blob,&length);
-          xx=ReadPropertyMSBLong(&blob,&length);
+          yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
+          xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
           x=(ssize_t) xx;
           if (xx > 2147483647)
             x=(ssize_t) xx-4294967295U-1;
@@ -1863,7 +1913,7 @@ static char *TraceSVGClippath(const unsigned char *blob,size_t length,
           point[i].x=(double) x*columns/4096/4096;
           point[i].y=(double) y*rows/4096/4096;
         }
-        if (in_subpath == MagickFalse)
+        if( IfMagickFalse(in_subpath) )
           {
             (void) FormatLocaleString(message,MaxTextExtent,"M %g,%g\n",
               point[1].x,point[1].y);
@@ -1915,7 +1965,7 @@ static char *TraceSVGClippath(const unsigned char *blob,size_t length,
       default:
       {
         blob+=24;
-        length-=24;
+        length-=MagickMin(24,(ssize_t) length);
         break;
       }
     }
@@ -1934,15 +1984,12 @@ static char *TraceSVGClippath(const unsigned char *blob,size_t length,
 }
 
 MagickExport const char *GetImageProperty(const Image *image,
-  const char *property)
+  const char *property,ExceptionInfo *exception)
 {
-  ExceptionInfo
-    *exception;
-
   FxInfo
     *fx_info;
 
-  MagickRealType
+  double
     alpha;
 
   MagickStatusType
@@ -1953,7 +2000,7 @@ MagickExport const char *GetImageProperty(const Image *image,
 
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
+  if( IfMagickTrue(image->debug) )
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   p=(const char *) NULL;
   if (image->properties != (void *) NULL)
@@ -1965,7 +2012,7 @@ MagickExport const char *GetImageProperty(const Image *image,
             image->properties);
           return(p);
         }
-      if (LocaleNCompare("fx:",property,3) != 0)
+      if (LocaleNCompare("fx:",property,3) != 0) /* NOT %[fx:..] !!!! */
         {
           p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
             image->properties,property);
@@ -1976,14 +2023,13 @@ MagickExport const char *GetImageProperty(const Image *image,
   if ((property == (const char *) NULL) ||
       (strchr(property,':') == (char *) NULL))
     return(p);
-  exception=(&((Image *) image)->exception);
   switch (*property)
   {
     case '8':
     {
       if (LocaleNCompare("8bim:",property,5) == 0)
         {
-          if ((Get8BIMProperty(image,property) != MagickFalse) &&
+          if( IfMagickTrue(Get8BIMProperty(image,property,exception)) &&
               (image->properties != (void *) NULL))
             {
               p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
@@ -1998,7 +2044,7 @@ MagickExport const char *GetImageProperty(const Image *image,
     {
       if (LocaleNCompare("exif:",property,5) == 0)
         {
-          if ((GetEXIFProperty(image,property) != MagickFalse) &&
+          if( IfMagickTrue(GetEXIFProperty(image,property,exception)) &&
               (image->properties != (void *) NULL))
             {
               p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
@@ -2013,18 +2059,18 @@ MagickExport const char *GetImageProperty(const Image *image,
     {
       if (LocaleNCompare("fx:",property,3) == 0)
         {
-          fx_info=AcquireFxInfo(image,property+3);
+          fx_info=AcquireFxInfo(image,property+3,exception);
           status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0,
             &alpha,exception);
           fx_info=DestroyFxInfo(fx_info);
-          if (status != MagickFalse)
+          if( IfMagickTrue(status) )
             {
               char
                 value[MaxTextExtent];
 
               (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
                 GetMagickPrecision(),(double) alpha);
-              (void) SetImageProperty((Image *) image,property,value);
+              (void) SetImageProperty((Image *) image,property,value,exception);
             }
           if (image->properties != (void *) NULL)
             {
@@ -2040,7 +2086,7 @@ MagickExport const char *GetImageProperty(const Image *image,
     {
       if (LocaleNCompare("iptc:",property,5) == 0)
         {
-          if ((GetIPTCProperty(image,property) != MagickFalse) &&
+          if( IfMagickTrue(GetIPTCProperty(image,property,exception)) &&
               (image->properties != (void *) NULL))
             {
               p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
@@ -2059,35 +2105,35 @@ MagickExport const char *GetImageProperty(const Image *image,
             pixel;
 
           GetPixelInfo(image,&pixel);
-          fx_info=AcquireFxInfo(image,property+6);
+          fx_info=AcquireFxInfo(image,property+6,exception);
           status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
             &alpha,exception);
-          pixel.red=(MagickRealType) QuantumRange*alpha;
+          pixel.red=(double) QuantumRange*alpha;
           status|=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0,
             &alpha,exception);
-          pixel.green=(MagickRealType) QuantumRange*alpha;
+          pixel.green=(double) QuantumRange*alpha;
           status|=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0,
             &alpha,exception);
-          pixel.blue=(MagickRealType) QuantumRange*alpha;
+          pixel.blue=(double) QuantumRange*alpha;
           if (image->colorspace == CMYKColorspace)
             {
               status|=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0,
                 &alpha,exception);
-              pixel.black=(MagickRealType) QuantumRange*alpha;
+              pixel.black=(double) QuantumRange*alpha;
             }
           status|=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0,
             &alpha,exception);
-          pixel.alpha=(MagickRealType) QuantumRange*(1.0-alpha);
+          pixel.alpha=(double) QuantumRange*(1.0-alpha);
           fx_info=DestroyFxInfo(fx_info);
-          if (status != MagickFalse)
+          if( IfMagickTrue(status) )
             {
               char
                 name[MaxTextExtent];
 
-              (void) QueryMagickColorname(image,&pixel,SVGCompliance,name,
+              (void) QueryColorname(image,&pixel,SVGCompliance,name,
                 exception);
-              (void) SetImageProperty((Image *) image,property,name);
-              return(GetImageProperty(image,property));
+              (void) SetImageProperty((Image *) image,property,name,exception);
+              return(GetImageProperty(image,property,exception));
             }
         }
       break;
@@ -2097,7 +2143,7 @@ MagickExport const char *GetImageProperty(const Image *image,
     {
       if (LocaleNCompare("xmp:",property,4) == 0)
         {
-          if ((GetXMPProperty(image,property) != MagickFalse) &&
+          if( IfMagickTrue(GetXMPProperty(image,property)) &&
               (image->properties != (void *) NULL))
             {
               p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
@@ -2124,12 +2170,22 @@ MagickExport const char *GetImageProperty(const Image *image,
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  GetMagickProperty() gets a value associated with an image property.
+%  GetMagickProperty() gets attributes or calculated values that is associated
+%  with a fixed known property name, or single letter property.
+%
+%  This does not return, special profile or property expressions. Nor does it
+%  return free-form property strings, unless referenced by a single letter
+%  property name.
+%
+%  The returned string is stored as the image artifact 'get-property' (not as
+%  another property), and as such should not be freed. Later calls however
+%  will overwrite this value so if needed for a longer period a copy should be
+%  made.  This artifact can be deleted when no longer required.
 %
 %  The format of the GetMagickProperty method is:
 %
-%      const char *GetMagickProperty(const ImageInfo *image_info,
-%        Image *image,const char *key)
+%      const char *GetMagickProperty(const ImageInfo *image_info,Image *image,
+%        const char *property,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -2139,59 +2195,357 @@ MagickExport const char *GetImageProperty(const Image *image,
 %
 %    o key: the key.
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
+static const char *GetMagickPropertyLetter(const ImageInfo *image_info,
+  Image *image,const char letter,ExceptionInfo *exception)
+{
+  char
+    value[MaxTextExtent];
+
+  const char
+    *string;
+
+  if (image != (Image *) NULL && IfMagickTrue(image->debug))
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+  *value='\0';  /* formatted string */
+  string=(char *) NULL;  /* constant string reference */
+  switch (letter)
+  {
+    case 'b':  /* image size read in - in bytes */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+        ((MagickOffsetType) image->extent));
+      if (image->extent != (MagickSizeType) ((size_t) image->extent))
+        (void) FormatMagickSize(image->extent,MagickFalse,value);
+      ConcatenateMagickString(value,"B",MaxTextExtent);
+      break;
+    }
+    case 'c':  /* image comment property - empty string by default */
+    {
+      string=GetImageProperty(image,"comment",exception);
+      if (string == (const char *) NULL)
+        string="";
+      break;
+    }
+    case 'd':  /* Directory component of filename */
+    {
+      GetPathComponent(image->magick_filename,HeadPath,value);
+      break;
+    }
+    case 'e': /* Filename extension (suffix) of image file */
+    {
+      GetPathComponent(image->magick_filename,ExtensionPath,value);
+      break;
+    }
+    case 'f': /* Filename without directory component */
+    {
+      GetPathComponent(image->magick_filename,TailPath,value);
+      break;
+    }
+    case 'g': /* Image geometry, canvas and offset  %Wx%H+%X+%Y */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
+        (double) image->page.width,(double) image->page.height,
+        (double) image->page.x,(double) image->page.y);
+      break;
+    }
+    case 'h': /* Image height (current) */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+        (image->rows != 0 ? image->rows : image->magick_rows));
+      break;
+    }
+    case 'i': /* Filename last used for image (read or write) */
+    {
+      string=image->filename;
+      break;
+    }
+    case 'k': /* Number of unique colors  */
+    {
+      /*
+        FUTURE: ensure this does not generate the formatted comment!
+      */
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+        GetNumberColors(image,(FILE *) NULL,exception));
+      break;
+    }
+    case 'l': /* Image label property - empty string by default */
+    {
+      string=GetImageProperty(image,"label",exception);
+      if ( string == (const char *)NULL)
+        string="";
+      break;
+    }
+    case 'm': /* Image format (file magick) */
+    {
+      string=image->magick;
+      break;
+    }
+    case 'n': /* Number of images in the list.  */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+        GetImageListLength(image));
+      break;
+    }
+    case 'o': /* Output Filename - for delegate use only */
+    {
+      string=image_info->filename;
+      break;
+    }
+    case 'p': /* Image index in current image list -- As 'n' OBSOLETE */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+        GetImageIndexInList(image));
+      break;
+    }
+    case 'q': /* Quantum depth of image in memory */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+        MAGICKCORE_QUANTUM_DEPTH);
+      break;
+    }
+    case 'r': /* Image storage class and colorspace.  */
+    {
+      ColorspaceType
+        colorspace;
+
+      colorspace=image->colorspace;
+      if (IfMagickTrue(IsImageGray(image,exception)))
+        colorspace=GRAYColorspace;
+      (void) FormatLocaleString(value,MaxTextExtent,"%s %s %s",
+        CommandOptionToMnemonic(MagickClassOptions,(ssize_t) image->storage_class),
+        CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) colorspace),
+        image->alpha_trait == BlendPixelTrait ? "Matte" : "");
+      break;
+    }
+    case 's': /* Image scene number */
+    {
+      if (image_info->number_scenes != 0)
+        (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+          image_info->scene);
+      else
+        (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+          image->scene);
+      break;
+    }
+    case 't': /* Base filename without directory or extention */
+    {
+      GetPathComponent(image->magick_filename,BasePath,value);
+      break;
+    }
+    case 'u': /* Unique filename */
+    {
+      string=image_info->unique;
+      break;
+    }
+    case 'w': /* Image width (current) */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+        (image->columns != 0 ? image->columns : image->magick_columns));
+      break;
+    }
+    case 'x': /* Image horizontal resolution (with units) */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%g %s",
+        image->resolution.x,CommandOptionToMnemonic(
+        MagickResolutionOptions,(ssize_t)image->units));
+      break;
+    }
+    case 'y': /* Image vertical resolution (with units) */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%g %s",
+        image->resolution.y,CommandOptionToMnemonic(MagickResolutionOptions,
+        (ssize_t) image->units));
+      break;
+    }
+    case 'z': /* Image depth as read in */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+        image->depth);
+      break;
+    }
+    case 'A': /* Image alpha channel  */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%s",
+        CommandOptionToMnemonic(MagickBooleanOptions,(ssize_t) image->alpha_trait));
+      break;
+    }
+    case 'C': /* Image compression method.  */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%s",
+        CommandOptionToMnemonic(MagickCompressOptions,(ssize_t)
+          image->compression));
+      break;
+    }
+    case 'D': /* Image dispose method.  */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%s",
+        CommandOptionToMnemonic(MagickDisposeOptions,(ssize_t) image->dispose));
+      break;
+    }
+    case 'G': /* Image size as geometry = "%wx%h" */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g",
+        (double)image->magick_columns,(double) image->magick_rows);
+      break;
+    }
+    case 'H': /* layer canvas height */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+        image->page.height);
+      break;
+    }
+    case 'M': /* Magick filename - filename given incl. coder & read mods */
+    {
+      string=image->magick_filename;
+      break;
+    }
+    case 'O': /* layer canvas offset with sign = "+%X+%Y" */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%+ld%+ld",(long)
+        image->page.x,(long) image->page.y);
+      break;
+    }
+    case 'P': /* layer canvas page size = "%Wx%H" */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g",
+        (double) image->page.width,(double) image->page.height);
+      break;
+    }
+    case 'Q': /* image compression quality */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+        image->quality);
+      break;
+    }
+    case 'S': /* Image scenes  ???? */
+    {
+      if (image_info->number_scenes == 0)
+        string="2147483647";
+      else
+        (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+          image_info->scene+image_info->number_scenes);
+      break;
+    }
+    case 'T': /* image time delay for animations */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+        image->delay);
+      break;
+    }
+    case 'W': /* layer canvas width */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+        image->page.width);
+      break;
+    }
+    case 'X': /* layer canvas X offset */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double)
+        image->page.x);
+      break;
+    }
+    case 'Y': /* layer canvas Y offset */
+    {
+      (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double)
+        image->page.y);
+      break;
+    }
+    case 'Z': /* Zero filename ??? */
+    {
+      string=image_info->zero;
+      break;
+    }
+    case '@': /* Trim bounding box, without Trimming! */
+    {
+      RectangleInfo
+        page;
+
+      page=GetImageBoundingBox(image,exception);
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
+        (double) page.width,(double) page.height,(double) page.x,(double)
+        page.y);
+      break;
+    }
+    case '#': /* Image signature */
+    {
+      (void) SignatureImage(image,exception);
+      string=GetImageProperty(image,"signature",exception);
+      break;
+    }
+    case '%': /* percent escaped */
+    {
+      string="%";
+      break;
+    }
+  }
+  if (*value != '\0')
+    string=value;
+  if (string != (char *) NULL)
+    {
+      (void) SetImageArtifact(image,"get-property",string);
+      return(GetImageArtifact(image,"get-property"));
+    }
+  return((char *)NULL);
+}
+
 MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
-  Image *image,const char *property)
+  Image *image,const char *property,ExceptionInfo *exception)
 {
   char
-    value[MaxTextExtent],
-    filename[MaxTextExtent];
+    value[MaxTextExtent];
 
-  *value='\0';
+  const char
+    *string;
+
+  assert(property[0] != '\0');
+  if (property[1] == '\0')  /* single letter property request */
+    return(GetMagickPropertyLetter(image_info,image,*property,exception));
+  if ((image != (Image *) NULL) && IfMagickTrue(image->debug))
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+  *value='\0';  /* formated string */
+  string=(char *) NULL;  /* constant string reference */
   switch (*property)
   {
     case 'b':
     {
-      if (LocaleNCompare("base",property,4) == 0)
+      if ((LocaleCompare("base",property) == 0) ||
+          (LocaleCompare("basename",property) == 0) )
         {
-          GetPathComponent(image->magick_filename,BasePath,filename);
-          (void) CopyMagickString(value,filename,MaxTextExtent);
+          GetPathComponent(image->magick_filename,BasePath,value);
           break;
         }
       break;
     }
     case 'c':
     {
-      if (LocaleNCompare("channels",property,8) == 0)
+      if (LocaleCompare("channels",property) == 0)
         {
-          /*
-            Image channels.
-          */
+          /* FUTURE: return actual image channels */
           (void) FormatLocaleString(value,MaxTextExtent,"%s",
             CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
             image->colorspace));
           LocaleLower(value);
-          if (image->matte != MagickFalse)
+          if( image->alpha_trait == BlendPixelTrait )
             (void) ConcatenateMagickString(value,"a",MaxTextExtent);
           break;
         }
-      if (LocaleNCompare("colorspace",property,10) == 0)
+      if (LocaleCompare("colorspace",property) == 0)
         {
           ColorspaceType
             colorspace;
 
-          /*
-            Image storage class and colorspace.
-          */
+          /* FUTURE: return actual colorspace - no 'gray' stuff */
           colorspace=image->colorspace;
-          if (IsImageGray(image,&image->exception) != MagickFalse)
+          if( IfMagickTrue(IsImageGray(image,exception)) )
             colorspace=GRAYColorspace;
-          (void) FormatLocaleString(value,MaxTextExtent,"%s",
-            CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
-            colorspace));
+          string=CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
+            colorspace);
           break;
         }
-      if (LocaleNCompare("copyright",property,9) == 0)
+      if (LocaleCompare("copyright",property) == 0)
         {
           (void) CopyMagickString(value,GetMagickCopyright(),MaxTextExtent);
           break;
@@ -2200,43 +2554,48 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     }
     case 'd':
     {
-      if (LocaleNCompare("depth",property,5) == 0)
+      if (LocaleCompare("depth",property) == 0)
         {
           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
             image->depth);
           break;
         }
-      if (LocaleNCompare("directory",property,9) == 0)
+      if (LocaleCompare("directory",property) == 0)
         {
-          GetPathComponent(image->magick_filename,HeadPath,filename);
-          (void) CopyMagickString(value,filename,MaxTextExtent);
+          GetPathComponent(image->magick_filename,HeadPath,value);
           break;
         }
       break;
     }
     case 'e':
     {
-      if (LocaleNCompare("extension",property,9) == 0)
+      if (LocaleCompare("extension",property) == 0)
         {
-          GetPathComponent(image->magick_filename,ExtensionPath,filename);
-          (void) CopyMagickString(value,filename,MaxTextExtent);
+          GetPathComponent(image->magick_filename,ExtensionPath,value);
           break;
         }
       break;
     }
     case 'g':
     {
-      if (LocaleNCompare("group",property,5) == 0)
+      if (LocaleCompare("gamma",property) == 0)
+        {
+          (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
+            GetMagickPrecision(),image->gamma);
+          break;
+        }
+      if ( (image_info != (ImageInfo *) NULL) &&
+           (LocaleCompare("group",property) == 0) )
         {
-          (void) FormatLocaleString(value,MaxTextExtent,"0x%lx",
-            (unsigned long) image_info->group);
+          (void) FormatLocaleString(value,MaxTextExtent,"0x%lx",(unsigned long)
+            image_info->group);
           break;
         }
       break;
     }
     case 'h':
     {
-      if (LocaleNCompare("height",property,6) == 0)
+      if (LocaleCompare("height",property) == 0)
         {
           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
             image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
@@ -2246,22 +2605,22 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     }
     case 'i':
     {
-      if (LocaleNCompare("input",property,5) == 0)
+      if (LocaleCompare("input",property) == 0)
         {
-          (void) CopyMagickString(value,image->filename,MaxTextExtent);
+          string=image->filename;
           break;
         }
       break;
     }
     case 'k':
     {
-      if (LocaleNCompare("kurtosis",property,8) == 0)
+      if (LocaleCompare("kurtosis",property) == 0)
         {
           double
             kurtosis,
             skewness;
 
-          (void) GetImageKurtosis(image,&kurtosis,&skewness,&image->exception);
+          (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
             GetMagickPrecision(),kurtosis);
           break;
@@ -2270,69 +2629,66 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     }
     case 'm':
     {
-      if (LocaleNCompare("magick",property,6) == 0)
+      if (LocaleCompare("magick",property) == 0)
         {
-          (void) CopyMagickString(value,image->magick,MaxTextExtent);
+          string=image->magick;
           break;
         }
-      if (LocaleNCompare("max",property,3) == 0)
+      if (LocaleCompare("max",property) == 0)
         {
           double
             maximum,
             minimum;
 
-          (void) GetImageRange(image,&minimum,&maximum,&image->exception);
+          (void) GetImageRange(image,&minimum,&maximum,exception);
           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
             GetMagickPrecision(),maximum);
           break;
         }
-      if (LocaleNCompare("mean",property,4) == 0)
+      if (LocaleCompare("mean",property) == 0)
         {
           double
             mean,
             standard_deviation;
 
-          (void) GetImageMean(image,&mean,&standard_deviation,
-             &image->exception);
+          (void) GetImageMean(image,&mean,&standard_deviation,exception);
           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
             GetMagickPrecision(),mean);
           break;
         }
-      if (LocaleNCompare("min",property,3) == 0)
+      if (LocaleCompare("min",property) == 0)
         {
           double
             maximum,
             minimum;
 
-          (void) GetImageRange(image,&minimum,&maximum,&image->exception);
+          (void) GetImageRange(image,&minimum,&maximum,exception);
           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
             GetMagickPrecision(),minimum);
           break;
         }
       break;
     }
-    case 'n':
-    {
-      if (LocaleNCompare("name",property,4) == 0)
-        {
-          (void) CopyMagickString(value,filename,MaxTextExtent);
-          break;
-        }
-     break;
-    }
     case 'o':
     {
-      if (LocaleNCompare("opaque",property,6) == 0)
+      if (LocaleCompare("opaque",property) == 0)
         {
           MagickBooleanType
             opaque;
 
-          opaque=IsImageOpaque(image,&image->exception);
-          (void) CopyMagickString(value,opaque == MagickFalse ? "false" :
-            "true",MaxTextExtent);
+          opaque=IsImageOpaque(image,exception);
+          (void) CopyMagickString(value,IfMagickTrue(opaque) ? "true" : "false",
+            MaxTextExtent);
+          break;
+        }
+      if (LocaleCompare("orientation",property) == 0)
+        {
+          string=CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t)
+            image->orientation);
           break;
         }
-      if (LocaleNCompare("output",property,6) == 0)
+      if ((image_info != (ImageInfo *) NULL) &&
+          (LocaleCompare("output",property) == 0))
         {
           (void) CopyMagickString(value,image_info->filename,MaxTextExtent);
           break;
@@ -2341,88 +2697,116 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     }
     case 'p':
     {
-      if (LocaleNCompare("page",property,4) == 0)
+      if (LocaleCompare("page",property) == 0)
         {
           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
-              GetImageIndexInList(image)+1);
+            GetImageIndexInList(image)+1);
+          break;
+        }
+      break;
+    }
+    case 'r':
+    {
+      /* This matches %[fx:resolution.x] */
+      if (LocaleCompare("resolution.x",property) == 0)
+        {
+          (void) FormatLocaleString(value,MaxTextExtent,"%g",
+            image->resolution.x);
+          break;
+        }
+      /* This matches %[fx:resolution.y] */
+      if (LocaleCompare("resolution.y",property) == 0)
+        {
+          (void) FormatLocaleString(value,MaxTextExtent,"%g",
+            image->resolution.y);
           break;
         }
       break;
     }
     case 's':
     {
-      if (LocaleNCompare("size",property,4) == 0)
+      if (LocaleCompare("scene",property) == 0)
         {
-          char
-            format[MaxTextExtent];
-
-          (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format);
-          (void) FormatLocaleString(value,MaxTextExtent,"%sB",format);
+          if ((image_info != (ImageInfo *) NULL) &&
+              (image_info->number_scenes != 0))
+            (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+              image_info->scene);
+          else
+            (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+              image->scene);
           break;
         }
-      if (LocaleNCompare("scenes",property,6) == 0)
+      if (LocaleCompare("scenes",property) == 0)
         {
           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
             GetImageListLength(image));
           break;
         }
-      if (LocaleNCompare("scene",property,5) == 0)
+      if (LocaleCompare("size",property) == 0)
         {
-          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
-            image->scene);
-          if (image_info->number_scenes != 0)
-            (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
-              image_info->scene);
+          char
+            format[MaxTextExtent];
+
+          (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format);
+          (void) FormatLocaleString(value,MaxTextExtent,"%sB",format);
           break;
         }
-      if (LocaleNCompare("skewness",property,8) == 0)
+      if (LocaleCompare("skewness",property) == 0)
         {
           double
             kurtosis,
             skewness;
 
-          (void) GetImageKurtosis(image,&kurtosis,&skewness,&image->exception);
+          (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
             GetMagickPrecision(),skewness);
           break;
         }
-      if (LocaleNCompare("standard-deviation",property,18) == 0)
+      if (LocaleCompare("standard-deviation",property) == 0)
         {
           double
             mean,
             standard_deviation;
 
-          (void) GetImageMean(image,&mean,&standard_deviation,
-            &image->exception);
+          (void) GetImageMean(image,&mean,&standard_deviation,exception);
           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
             GetMagickPrecision(),standard_deviation);
           break;
         }
        break;
     }
+    case 't':
+    {
+      if (LocaleCompare("type",property) == 0)
+        {
+          string=CommandOptionToMnemonic(MagickTypeOptions,(ssize_t)
+            GetImageType(image,exception));
+          break;
+        }
+       break;
+    }
     case 'u':
     {
-      if (LocaleNCompare("unique",property,6) == 0)
+      if ((image_info != (ImageInfo *) NULL) &&
+          (LocaleCompare("unique",property) == 0))
         {
-          (void) CopyMagickString(filename,image_info->unique,MaxTextExtent);
-          (void) CopyMagickString(value,filename,MaxTextExtent);
+          string=image_info->unique;
           break;
         }
       break;
     }
     case 'v':
     {
-      if (LocaleNCompare("version",property,7) == 0)
+      if (LocaleCompare("version",property) == 0)
         {
-          (void) CopyMagickString(value,GetMagickVersion((size_t *) NULL),
-            MaxTextExtent);
+          string=GetMagickVersion((size_t *) NULL);
           break;
         }
       break;
     }
     case 'w':
     {
-      if (LocaleNCompare("width",property,5) == 0)
+      if (LocaleCompare("width",property) == 0)
         {
           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
             (image->magick_columns != 0 ? image->magick_columns : 256));
@@ -2430,46 +2814,47 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
         }
       break;
     }
-    case 'x':
+    case 'x': /* FUTURE: Obsolete X resolution */
     {
-      if (LocaleNCompare("xresolution",property,11) == 0)
+      if ((LocaleCompare("xresolution",property) == 0) ||
+          (LocaleCompare("x-resolution",property) == 0) )
         {
-          (void) FormatLocaleString(value,MaxTextExtent,"%g",
-            image->x_resolution);
+          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
+            image->resolution.x);
           break;
         }
       break;
     }
-    case 'y':
+    case 'y': /* FUTURE: Obsolete Y resolution */
     {
-      if (LocaleNCompare("yresolution",property,11) == 0)
+      if ((LocaleCompare("yresolution",property) == 0) ||
+          (LocaleCompare("y-resolution",property) == 0) )
         {
-          (void) FormatLocaleString(value,MaxTextExtent,"%g",
-            image->y_resolution);
+          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
+            image->resolution.y);
           break;
         }
       break;
     }
     case 'z':
     {
-      if (LocaleNCompare("zero",property,4) == 0)
+      if ((image_info != (ImageInfo *) NULL) &&
+          (LocaleCompare("zero",property) == 0))
         {
-          (void) CopyMagickString(filename,image_info->zero,MaxTextExtent);
-          (void) CopyMagickString(value,filename,MaxTextExtent);
+          string=image_info->zero;
           break;
         }
       break;
     }
   }
   if (*value != '\0')
-   {
-     if (image->properties == (void *) NULL)
-       image->properties=NewSplayTree(CompareSplayTreeString,
-         RelinquishMagickMemory,RelinquishMagickMemory);
-     (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
-       ConstantString(property),ConstantString(value));
-   }
-  return(GetImageProperty(image,property));
+    string=value;
+  if (string != (char *)NULL)
+    {
+      (void) SetImageArtifact(image,"get-property", string);
+      return(GetImageArtifact(image,"get-property"));
+    }
+  return((char *)NULL);
 }
 \f
 /*
@@ -2483,7 +2868,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  GetNextImageProperty() gets the next image property value.
+%  GetNextImageProperty() gets the next free-form string property name.
 %
 %  The format of the GetNextImageProperty method is:
 %
@@ -2498,7 +2883,7 @@ MagickExport char *GetNextImageProperty(const Image *image)
 {
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
+  if( IfMagickTrue(image->debug) )
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
       image->filename);
   if (image->properties == (void *) NULL)
@@ -2520,10 +2905,33 @@ MagickExport char *GetNextImageProperty(const Image *image)
 %  InterpretImageProperties() replaces any embedded formatting characters with
 %  the appropriate image property and returns the interpreted text.
 %
+%  This searches for and replaces
+%     \n \r \%          replaced by newline, return, and percent resp.
+%     &lt; &gt; &amp;   replaced by '<', '>', '&' resp.
+%     %%                replaced by percent
+%
+%     %x %[x]       where 'x' is a single letter properity, case sensitive).
+%     %[type:name]  where 'type' a is special and known prefix.
+%     %[name]       where 'name' is a specifically known attribute, calculated
+%                   value, or a per-image property string name, or a per-image
+%                   'artifact' (as generated from a global option).
+%                   It may contain ':' as long as the prefix is not special.
+%
+%  Single letter % substitutions will only happen if the character before the
+%  percent is NOT a number. But braced substitutions will always be performed.
+%  This prevents the typical usage of percent in a interpreted geometry
+%  argument from being substituted when the percent is a geometry flag.
+%
+%  If 'glob-expresions' ('*' or '?' characters) is used for 'name' it may be
+%  used as a search pattern to print multiple lines of "name=value\n" pairs of
+%  the associacted set of properities.
+%
+%  The returned string must be freed using DestoryString() by the caller.
+%
 %  The format of the InterpretImageProperties method is:
 %
-%      char *InterpretImageProperties(const ImageInfo *image_info,Image *image,
-%        const char *embed_text)
+%      char *InterpretImageProperties(const ImageInfo *image_info,
+%        Image *image,const char *embed_text,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -2534,17 +2942,14 @@ MagickExport char *GetNextImageProperty(const Image *image)
 %    o embed_text: the address of a character string containing the embedded
 %      formatting characters.
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
 MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
-  Image *image,const char *embed_text)
+  Image *image,const char *embed_text,ExceptionInfo *exception)
 {
   char
-    filename[MaxTextExtent],
-    *interpret_text,
-    *text;
-
-  const char
-    *value;
+    *interpret_text;
 
   register char
     *q;
@@ -2552,30 +2957,43 @@ MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
   register const char
     *p;
 
-  register ssize_t
-    i;
-
   size_t
     extent,
     length;
 
+  MagickBooleanType
+    number;
+
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
+  if( IfMagickTrue(image->debug) )
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  if ((embed_text == (const char *) NULL) || (*embed_text == '\0'))
+
+  if ((embed_text == (const char *) NULL))
     return((char *) NULL);
-  text=(char *) embed_text;
-  if ((*text == '@') && ((*(text+1) == '-') ||
-      (IsPathAccessible(text+1) != MagickFalse)))
-    return(FileToString(embed_text+1,~0,&image->exception));
+  p=embed_text;
+
+  if (*p == '\0')
+    return(ConstantString(""));
+
+  /* handle a '@' replace string from file */
+  if (*p == '@') {
+     p++;
+     if (*p != '-' && IfMagickFalse(IsPathAccessible(p)) ) {
+       (void) ThrowMagickException(exception,GetMagickModule(),
+           OptionError,"UnableToAccessPath","%s",p);
+       return((char *) NULL);
+     }
+     return(FileToString(p,~0,exception));
+  }
+
   /*
     Translate any embedded format characters.
   */
-  interpret_text=AcquireString(text);
-  extent=MaxTextExtent;
-  p=text;
-  for (q=interpret_text; *p != '\0'; p++)
+  interpret_text=AcquireString(embed_text); /* new string with extra space */
+  extent=MaxTextExtent;                     /* how many extra space */
+  number=MagickFalse;                       /* is last char a number? */
+  for (q=interpret_text; *p!='\0'; number=IsMagickTrue(isdigit(*p)),p++)
   {
     *q='\0';
     if ((size_t) (q-interpret_text+MaxTextExtent) >= extent)
@@ -2584,503 +3002,258 @@ MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
         interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
           MaxTextExtent+1,sizeof(*interpret_text));
         if (interpret_text == (char *) NULL)
-          break;
+          return((char *) NULL);
         q=interpret_text+strlen(interpret_text);
       }
     /*
-      Process formatting characters in text.
+      Look for the various escapes, (and handle other specials)
     */
-    if ((*p == '\\') && (*(p+1) == 'r'))
-      {
-        *q++='\r';
-        p++;
-        continue;
-      }
-    if ((*p == '\\') && (*(p+1) == 'n'))
-      {
-        *q++='\n';
-        p++;
+    switch (*p) {
+      case '\\':
+        switch (*(p+1)) {
+          case '\0':
+            continue;
+          case 'r':       /* convert to RETURN */
+            *q++='\r';
+            p++;
+            continue;
+          case 'n':       /* convert to NEWLINE */
+            *q++='\n';
+            p++;
+            continue;
+          case '\n':      /* EOL removal UNIX,MacOSX */
+            p++;
+            continue;
+          case '\r':      /* EOL removal DOS,Windows */
+            p++;
+            if (*p == '\n') /* return-newline EOL */
+              p++;
+            continue;
+          default:
+            p++;
+            *q++=(*p);
+            continue;
+        }
+        continue; /* never reached! */
+      case '&':
+        if (LocaleNCompare("&lt;",p,4) == 0)
+          *q++='<', p+=3;
+        else if (LocaleNCompare("&gt;",p,4) == 0)
+          *q++='>', p+=3;
+        else if (LocaleNCompare("&amp;",p,5) == 0)
+          *q++='&', p+=4;
+        else
+          *q++=(*p);
         continue;
-      }
-    if (*p == '\\')
-      {
-        p++;
-        *q++=(*p);
+      case '%':
+        break;      /* continue to next set of handlers */
+      default:
+        *q++=(*p);  /* any thing else is 'as normal' */
         continue;
-      }
-    if (*p != '%')
-      {
-        *q++=(*p);
+    }
+    p++; /* advance beyond the percent */
+
+    /*
+      Doubled Percent - or percent at end of string
+    */
+    if ( *p == '\0' )
+       p--;
+    if ( *p == '%' ) {
+        *q++='%';
         continue;
       }
-    p++;
-    switch (*p)
-    {
-      case 'b':
-      {
-        char
-          format[MaxTextExtent];
 
-        /*
-          File size.
-        */
-        (void) FormatLocaleString(format,MaxTextExtent,"%.20g",(double)
-          ((MagickOffsetType) image->extent));
-        if (image->extent != (MagickSizeType) ((size_t) image->extent))
-          (void) FormatMagickSize(image->extent,MagickFalse,format);
-        q+=ConcatenateMagickString(q,format,extent);
-        q+=ConcatenateMagickString(q,"B",extent);
-        break;
+    /*
+      Single letter escapes  %c
+    */
+    if ( *p != '[' ) {
+      const char
+        *value;
+
+      /* But only if not preceeded by a number! */
+      if ( IfMagickTrue(number) ) {
+        *q++='%'; /* do NOT substitute the percent */
+        p--;      /* back up one */
+        continue;
       }
-      case 'c':
-      {
-        /*
-          Image comment.
-        */
-        value=GetImageProperty(image,"comment");
-        if (value == (const char *) NULL)
-          break;
+      value=GetMagickPropertyLetter(image_info,image,*p, exception);
+      if (value != (char *) NULL) {
         length=strlen(value);
         if ((size_t) (q-interpret_text+length+1) >= extent)
           {
             extent+=length;
-            interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
-              MaxTextExtent,sizeof(*interpret_text));
+            interpret_text=(char *) ResizeQuantumMemory(interpret_text,
+              extent+MaxTextExtent,sizeof(*interpret_text));
             if (interpret_text == (char *) NULL)
-              break;
+              return((char *)NULL);
             q=interpret_text+strlen(interpret_text);
           }
         (void) CopyMagickString(q,value,extent);
         q+=length;
-        break;
-      }
-      case 'd':
-      case 'e':
-      case 'f':
-      case 't':
-      {
-        /*
-          Label segment is the base of the filename.
-        */
-        if (*image->magick_filename == '\0')
-          break;
-        switch (*p)
-        {
-          case 'd':
-          {
-            /*
-              Directory.
-            */
-            GetPathComponent(image->magick_filename,HeadPath,filename);
-            q+=CopyMagickString(q,filename,extent);
-            break;
-          }
-          case 'e':
-          {
-            /*
-              Filename extension.
-            */
-            GetPathComponent(image->magick_filename,ExtensionPath,filename);
-            q+=CopyMagickString(q,filename,extent);
-            break;
-          }
-          case 'f':
-          {
-            /*
-              Filename.
-            */
-            GetPathComponent(image->magick_filename,TailPath,filename);
-            q+=CopyMagickString(q,filename,extent);
-            break;
-          }
-          case 't':
-          {
-            /*
-              Base filename.
-            */
-            GetPathComponent(image->magick_filename,BasePath,filename);
-            q+=CopyMagickString(q,filename,extent);
-            break;
-          }
-        }
-        break;
-      }
-      case 'g':
-      {
-        /*
-          Image geometry.
-        */
-        q+=FormatLocaleString(q,extent,"%.20gx%.20g%+.20g%+.20g",(double)
-          image->page.width,(double) image->page.height,(double) image->page.x,
-          (double) image->page.y);
-        break;
-      }
-      case 'h':
-      {
-        /*
-          Image height.
-        */
-        q+=FormatLocaleString(q,extent,"%.20g",(double) (image->rows != 0 ?
-          image->rows : image->magick_rows));
-        break;
-      }
-      case 'i':
-      {
-        /*
-          Image filename.
-        */
-        q+=CopyMagickString(q,image->filename,extent);
-        break;
-      }
-      case 'k':
-      {
-        /*
-          Number of unique colors.
-        */
-        q+=FormatLocaleString(q,extent,"%.20g",(double) GetNumberColors(image,
-          (FILE *) NULL,&image->exception));
-        break;
-      }
-      case 'l':
-      {
-        /*
-          Image label.
-        */
-        value=GetImageProperty(image,"label");
-        if (value == (const char *) NULL)
-          break;
-        length=strlen(value);
-        if ((size_t) (q-interpret_text+length+1) >= extent)
-          {
-            extent+=length;
-            interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
-              MaxTextExtent,sizeof(*interpret_text));
-            if (interpret_text == (char *) NULL)
-              break;
-            q=interpret_text+strlen(interpret_text);
-          }
-        q+=CopyMagickString(q,value,extent);
-        break;
-      }
-      case 'm':
-      {
-        /*
-          Image format.
-        */
-        q+=CopyMagickString(q,image->magick,extent);
-        break;
-      }
-      case 'M':
-      {
-        /*
-          Image magick filename.
-        */
-        q+=CopyMagickString(q,image->magick_filename,extent);
-        break;
-      }
-      case 'n':
-      {
-        /*
-          Number of images in the list.
-        */
-        q+=FormatLocaleString(q,extent,"%.20g",(double)
-             GetImageListLength(image));
-        break;
-      }
-      case 'o':
-      {
-        /*
-          Image output filename.
-        */
-        q+=CopyMagickString(q,image_info->filename,extent);
-        break;
-      }
-      case 'p':
-      {
-        /*
-          Image index in list.
-        */
-        q+=FormatLocaleString(q,extent,"%.20g",(double)
-            GetImageIndexInList(image));
-        break;
-      }
-      case 'q':
-      {
-        /*
-          Image depth.
-        */
-        q+=FormatLocaleString(q,extent,"%.20g",(double)
-          MAGICKCORE_QUANTUM_DEPTH);
-        break;
+        continue;
       }
-      case 'r':
-      {
-        ColorspaceType
-          colorspace;
+      (void) ThrowMagickException(exception,GetMagickModule(),
+          OptionWarning,"UnknownImageProperty","\"%%%c\"",*p);
+      continue;
+    }
 
-        /*
-          Image storage class and colorspace.
-        */
-        colorspace=image->colorspace;
-        if (IsImageGray(image,&image->exception) != MagickFalse)
-          colorspace=GRAYColorspace;
-        q+=FormatLocaleString(q,extent,"%s%s%s",CommandOptionToMnemonic(
-          MagickClassOptions,(ssize_t) image->storage_class),
-          CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) colorspace),
-          image->matte != MagickFalse ? "Matte" : "");
-        break;
-      }
-      case 's':
-      {
-        /*
-          Image scene number.
-        */
-        if (image_info->number_scenes == 0)
-          q+=FormatLocaleString(q,extent,"%.20g",(double) image->scene);
-        else
-          q+=FormatLocaleString(q,extent,"%.20g",(double) image_info->scene);
-        break;
-      }
-      case 'u':
-      {
-        /*
-          Unique filename.
-        */
-        (void) CopyMagickString(filename,image_info->unique,extent);
-        q+=CopyMagickString(q,filename,extent);
-        break;
-      }
-      case 'w':
-      {
-        /*
-          Image width.
-        */
-        q+=FormatLocaleString(q,extent,"%.20g",(double) (image->columns != 0 ?
-          image->columns : image->magick_columns));
-        break;
-      }
-      case 'x':
-      {
-        /*
-          Image horizontal resolution.
-        */
-        q+=FormatLocaleString(q,extent,"%g %s",image->x_resolution,
-          CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
-            image->units));
-        break;
-      }
-      case 'y':
-      {
-        /*
-          Image vertical resolution.
-        */
-        q+=FormatLocaleString(q,extent,"%g %s",image->y_resolution,
-          CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
-          image->units));
-        break;
-      }
-      case 'z':
-      {
-        /*
-          Image depth.
-        */
-        q+=FormatLocaleString(q,extent,"%.20g",(double) image->depth);
-        break;
-      }
-      case 'A':
-      {
-        /*
-          Image alpha channel.
-        */
-        q+=FormatLocaleString(q,extent,"%s",CommandOptionToMnemonic(
-          MagickBooleanOptions,(ssize_t) image->matte));
-        break;
-      }
-      case 'C':
-      {
-        /*
-          Image compression method.
-        */
-        q+=FormatLocaleString(q,extent,"%s",CommandOptionToMnemonic(
-          MagickCompressOptions,(ssize_t) image->compression));
-        break;
-      }
-      case 'D':
-      {
-        /*
-          Image dispose method.
-        */
-        q+=FormatLocaleString(q,extent,"%s",CommandOptionToMnemonic(
-          MagickDisposeOptions,(ssize_t) image->dispose));
-        break;
-      }
-      case 'G':
-      {
-        q+=FormatLocaleString(q,extent,"%.20gx%.20g",(double)
-          image->magick_columns,(double) image->magick_rows);
-        break;
-      }
-      case 'H':
-      {
-        q+=FormatLocaleString(q,extent,"%.20g",(double) image->page.height);
-        break;
-      }
-      case 'O':
-      {
-        q+=FormatLocaleString(q,extent,"%+ld%+ld",(long) image->page.x,(long)
-          image->page.y);
-        break;
-      }
-      case 'P':
-      {
-        q+=FormatLocaleString(q,extent,"%.20gx%.20g",(double) image->page.width,
-          (double) image->page.height);
-        break;
-      }
-      case 'Q':
-      {
-        q+=FormatLocaleString(q,extent,"%.20g",(double) image->quality);
-        break;
-      }
-      case 'S':
-      {
-        /*
-          Image scenes.
-        */
-        if (image_info->number_scenes == 0)
-          q+=CopyMagickString(q,"2147483647",extent);
-        else
-          q+=FormatLocaleString(q,extent,"%.20g",(double) (image_info->scene+
-            image_info->number_scenes));
-        break;
-      }
-      case 'T':
-      {
-        q+=FormatLocaleString(q,extent,"%.20g",(double) image->delay);
-        break;
-      }
-      case 'W':
-      {
-        q+=FormatLocaleString(q,extent,"%.20g",(double) image->page.width);
-        break;
-      }
-      case 'X':
-      {
-        q+=FormatLocaleString(q,extent,"%+.20g",(double) image->page.x);
+    /*
+      Braced Percent Escape  %[...]
+    */
+    {
+      char
+        pattern[MaxTextExtent];
+
+      const char
+        *key,
+        *value;
+
+      register ssize_t
+        len;
+
+      ssize_t
+        depth;
+
+      /* get the string framed by the %[...] */
+      p++;  /* advance p to just inside the opening brace */
+      depth=1;
+      if ( *p == ']' ) {
+        (void) ThrowMagickException(exception,GetMagickModule(),
+            OptionWarning,"UnknownImageProperty","\"%%[]\"");
         break;
       }
-      case 'Y':
+      for (len=0; len<(MaxTextExtent-1L) && (*p != '\0');)
       {
-        q+=FormatLocaleString(q,extent,"%+.20g",(double) image->page.y);
-        break;
+        /* skip escaped braces within braced pattern */
+        if ( (*p == '\\') && (*(p+1) != '\0') ) {
+          pattern[len++]=(*p++);
+          pattern[len++]=(*p++);
+          continue;
+        }
+        if (*p == '[')
+          depth++;
+        if (*p == ']')
+          depth--;
+        if (depth <= 0)
+          break;
+        pattern[len++]=(*p++);
       }
-      case 'Z':
-      {
-        /*
-          Unique filename.
-        */
-        (void) CopyMagickString(filename,image_info->zero,extent);
-        q+=CopyMagickString(q,filename,extent);
-        break;
+      pattern[len]='\0';
+      /* Check for unmatched final ']' for "%[...]" */
+      if ( depth != 0 ) {
+        if (len >= 64) {  /* truncate string for error message */
+          pattern[61] = '.';
+          pattern[62] = '.';
+          pattern[63] = '.';
+          pattern[64] = '\0';
+        }
+        (void) ThrowMagickException(exception,GetMagickModule(),
+            OptionError,"UnbalancedBraces","\"%%[%s\"",pattern);
+        interpret_text=DestroyString(interpret_text);
+        return((char *)NULL);
       }
-      case '[':
-      {
-        char
-          pattern[MaxTextExtent];
-
-        const char
-          *key,
-          *value;
 
-        ssize_t
-          depth;
-
-        /*
-          Image value.
-        */
-        if (strchr(p,']') == (char *) NULL)
-          break;
-        depth=1;
-        p++;
-        for (i=0; (i < (MaxTextExtent-1L)) && (*p != '\0'); i++)
-        {
-          if (*p == '[')
-            depth++;
-          if (*p == ']')
-            depth--;
-          if (depth <= 0)
-            break;
-          pattern[i]=(*p++);
+      /*
+        Special Property Prefixes
+        such as: %[exif:...] %[fx:...] %[pixel:...]
+        Otherwise a free-form property string
+      */
+      value=GetImageProperty(image,pattern,exception);
+      if (value != (const char *) NULL)
+        {
+          length=strlen(value);
+          if ((size_t) (q-interpret_text+length+1) >= extent)
+            {
+              extent+=length;
+              interpret_text=(char *) ResizeQuantumMemory(interpret_text,
+                extent+MaxTextExtent,sizeof(*interpret_text));
+              if (interpret_text == (char *) NULL)
+                return((char *)NULL);
+              q=interpret_text+strlen(interpret_text);
+            }
+          (void) CopyMagickString(q,value,extent);
+          q+=length;
+          continue;
         }
-        pattern[i]='\0';
-        value=GetImageProperty(image,pattern);
-        if (value != (const char *) NULL)
+      /*
+        Handle property 'glob' patterns
+        Such as:  %[*]   %[user:array_??]  %[filename:e*]
+      */
+      if( IfMagickTrue(IsGlob(pattern)) )
+        {
+          ResetImagePropertyIterator(image);
+          key=GetNextImageProperty(image);
+          while (key != (const char *) NULL)
           {
-            length=strlen(value);
-            if ((size_t) (q-interpret_text+length+1) >= extent)
-              {
-                extent+=length;
-                interpret_text=(char *) ResizeQuantumMemory(interpret_text,
-                  extent+MaxTextExtent,sizeof(*interpret_text));
-                if (interpret_text == (char *) NULL)
-                  break;
-                q=interpret_text+strlen(interpret_text);
-              }
-            (void) CopyMagickString(q,value,extent);
-            q+=length;
-            break;
-          }
-        else
-          if (IsGlob(pattern) != MagickFalse)
-            {
-              /*
-                Iterate over image properties.
-              */
-              ResetImagePropertyIterator(image);
-              key=GetNextImageProperty(image);
-              while (key != (const char *) NULL)
+            if( IfMagickTrue(GlobExpression(key,pattern,MagickTrue)) )
               {
-                if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
+                value=GetImageProperty(image,key,exception);
+                if (value != (const char *) NULL)
                   {
-                    value=GetImageProperty(image,key);
-                    if (value != (const char *) NULL)
+                    length=strlen(key)+strlen(value)+2;
+                    if ((size_t) (q-interpret_text+length+1) >= extent)
                       {
-                        length=strlen(key)+strlen(value)+2;
-                        if ((size_t) (q-interpret_text+length+1) >= extent)
-                          {
-                            extent+=length;
-                            interpret_text=(char *) ResizeQuantumMemory(
-                              interpret_text,extent+MaxTextExtent,
-                              sizeof(*interpret_text));
-                            if (interpret_text == (char *) NULL)
-                              break;
-                            q=interpret_text+strlen(interpret_text);
-                          }
-                        q+=FormatLocaleString(q,extent,"%s=%s\n",key,value);
+                        extent+=length;
+                        interpret_text=(char *) ResizeQuantumMemory(
+                          interpret_text,extent+MaxTextExtent,
+                          sizeof(*interpret_text));
+                        if (interpret_text == (char *) NULL)
+                          return((char *)NULL);
+                        q=interpret_text+strlen(interpret_text);
                       }
+                    q+=FormatLocaleString(q,extent,"%s=%s\n",key,value);
                   }
-                key=GetNextImageProperty(image);
-              }
-            }
-        value=GetMagickProperty(image_info,image,pattern);
-        if (value != (const char *) NULL)
-          {
-            length=strlen(value);
-            if ((size_t) (q-interpret_text+length+1) >= extent)
-              {
-                extent+=length;
-                interpret_text=(char *) ResizeQuantumMemory(interpret_text,
-                  extent+MaxTextExtent,sizeof(*interpret_text));
-                if (interpret_text == (char *) NULL)
-                  break;
-                q=interpret_text+strlen(interpret_text);
               }
-            (void) CopyMagickString(q,value,extent);
-            q+=length;
-            break;
+            key=GetNextImageProperty(image);
           }
-        if (image_info == (ImageInfo *) NULL)
-          break;
+          continue;
+        }
+      /*
+        Look for a known property or image attribute
+        Such as  %[basename]  %[denisty]  %[delay]
+        Also does wrapped single letters:  %[b] %[G] %[g]
+      */
+      value=GetMagickProperty(image_info,image,pattern,exception);
+      if (value != (const char *) NULL)
+        {
+          length=strlen(value);
+          if ((size_t) (q-interpret_text+length+1) >= extent)
+            {
+              extent+=length;
+              interpret_text=(char *) ResizeQuantumMemory(interpret_text,
+                extent+MaxTextExtent,sizeof(*interpret_text));
+              if (interpret_text == (char *) NULL)
+                return((char *)NULL);
+              q=interpret_text+strlen(interpret_text);
+            }
+          (void) CopyMagickString(q,value,extent);
+          q+=length;
+          continue;
+        }
+      /*
+        Look for a per-image Artifact (user option, post-interpreted)
+      */
+      value=GetImageArtifact(image,pattern);
+      if (value != (char *) NULL)
+        {
+          length=strlen(value);
+          if ((size_t) (q-interpret_text+length+1) >= extent)
+            {
+              extent+=length;
+              interpret_text=(char *) ResizeQuantumMemory(interpret_text,
+                extent+MaxTextExtent,sizeof(*interpret_text));
+              if (interpret_text == (char *) NULL)
+                return((char *)NULL);
+              q=interpret_text+strlen(interpret_text);
+            }
+          (void) CopyMagickString(q,value,extent);
+          q+=length;
+          continue;
+        }
+      /*
+        Look for user option of this name (should never match in CLI usage)
+      */
+      if (image_info != (ImageInfo *) NULL) {
         value=GetImageOption(image_info,pattern);
         if (value != (char *) NULL)
           {
@@ -3091,59 +3264,30 @@ MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
                 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
                   extent+MaxTextExtent,sizeof(*interpret_text));
                 if (interpret_text == (char *) NULL)
-                  break;
+                  return((char *)NULL);
                 q=interpret_text+strlen(interpret_text);
               }
             (void) CopyMagickString(q,value,extent);
             q+=length;
-            break;
+            continue;
           }
-        break;
+        }
+      /*
+        Failed to find any match anywhere!
+      */
+      if (len >= 64) {  /* truncate string for error message */
+        pattern[61] = '.';
+        pattern[62] = '.';
+        pattern[63] = '.';
+        pattern[64] = '\0';
       }
-      case '@':
-      {
-        RectangleInfo
-          page;
+      (void) ThrowMagickException(exception,GetMagickModule(),
+          OptionWarning,"UnknownImageProperty","\"%%[%s]\"",pattern);
+      /* continue */
+    } /* Braced Percent Escape */
 
-        /*
-          Image bounding box.
-        */
-        page=GetImageBoundingBox(image,&image->exception);
-        q+=FormatLocaleString(q,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
-          (double) page.width,(double) page.height,(double) page.x,(double)
-          page.y);
-        break;
-      }
-      case '#':
-      {
-        /*
-          Image signature.
-        */
-        (void) SignatureImage(image);
-        value=GetImageProperty(image,"signature");
-        if (value == (const char *) NULL)
-          break;
-        q+=CopyMagickString(q,value,extent);
-        break;
-      }
-      case '%':
-      {
-        *q++=(*p);
-        break;
-      }
-      default:
-      {
-        *q++='%';
-        *q++=(*p);
-        break;
-      }
-    }
-  }
+  } /* for each char in 'embed_text' */
   *q='\0';
-  if (text != (const char *) embed_text)
-    text=DestroyString(text);
-  (void) SubstituteString(&interpret_text,"&lt;","<");
-  (void) SubstituteString(&interpret_text,"&gt;",">");
   return(interpret_text);
 }
 \f
@@ -3161,6 +3305,9 @@ MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
 %  RemoveImageProperty() removes a property from the image and returns its
 %  value.
 %
+%  In this case the ConstantString() value returned should be freed by the
+%  caller when finished.
+%
 %  The format of the RemoveImageProperty method is:
 %
 %      char *RemoveImageProperty(Image *image,const char *property)
@@ -3180,7 +3327,7 @@ MagickExport char *RemoveImageProperty(Image *image,
 
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
+  if( IfMagickTrue(image->debug) )
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
       image->filename);
   if (image->properties == (void *) NULL)
@@ -3218,7 +3365,7 @@ MagickExport void ResetImagePropertyIterator(const Image *image)
 {
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
+  if( IfMagickTrue(image->debug) )
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
       image->filename);
   if (image->properties == (void *) NULL)
@@ -3237,12 +3384,16 @@ MagickExport void ResetImagePropertyIterator(const Image *image)
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  SetImageProperty() associates an value with an image property.
+%  SetImageProperty() saves the given string value either to specific known
+%  attribute or to a freeform property string.
+%
+%  Attempting to set a property that is normally calculated will produce
+%  an exception.
 %
 %  The format of the SetImageProperty method is:
 %
 %      MagickBooleanType SetImageProperty(Image *image,const char *property,
-%        const char *value)
+%        const char *value,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -3252,13 +3403,12 @@ MagickExport void ResetImagePropertyIterator(const Image *image)
 %
 %    o values: the image property values.
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
 MagickExport MagickBooleanType SetImageProperty(Image *image,
-  const char *property,const char *value)
+  const char *property,const char *value,ExceptionInfo *exception)
 {
-  ExceptionInfo
-    *exception;
-
   MagickBooleanType
     status;
 
@@ -3267,39 +3417,60 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
 
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
-    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
-      image->filename);
+  if( IfMagickTrue(image->debug) )
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+
+  /* Create splay-tree */
   if (image->properties == (void *) NULL)
     image->properties=NewSplayTree(CompareSplayTreeString,
       RelinquishMagickMemory,RelinquishMagickMemory);
-  if ((value == (const char *) NULL) || (*value == '\0'))
+
+  /* Delete property if NULL --  empty string values are valid! */
+  if ((value == (const char *) NULL))
     return(DeleteImageProperty(image,property));
   status=MagickTrue;
-  exception=(&image->exception);
+
+  /* Do not 'set' single letter properties - read only shorthand */
+  if (strlen(property) <= 1)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),
+          OptionError,"SetReadOnlyProperty","'%s'",property);
+      return(MagickFalse);
+    }
+
+  /* FUTURE: These should produce a 'illegal settings' error
+     + binary chars in property key
+     + single letter property keys (read only)
+     + known special prefixes (read only, they don't get saved!)
+  */
+
   switch (*property)
   {
     case 'B':
     case 'b':
     {
-      if (LocaleCompare(property,"background") == 0)
-        {
-          (void) QueryColorDatabase(value,&image->background_color,exception);
-          break;
-        }
-      if (LocaleCompare(property,"bias") == 0)
+      if (LocaleCompare("background",property) == 0)
         {
-          image->bias=SiPrefixToDouble(value,QuantumRange);
+          (void) QueryColorCompliance(value,AllCompliance,
+            &image->background_color,exception);
           break;
         }
       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
         ConstantString(property),ConstantString(value));
+      /* FUTURE: error if status is bad? */
       break;
     }
     case 'C':
     case 'c':
     {
-      if (LocaleCompare(property,"colorspace") == 0)
+      if (LocaleCompare("channels",property) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","'%s'",property);
+          status=MagickFalse;
+          break;
+        }
+      if (LocaleCompare("colorspace",property) == 0)
         {
           ssize_t
             colorspace;
@@ -3308,10 +3479,30 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
             value);
           if (colorspace < 0)
             break;
-          (void) SetImageColorspace(image,(ColorspaceType) colorspace);
+          image->colorspace=(ColorspaceType) colorspace;
+          image->rendering_intent=UndefinedIntent;
+          image->gamma=1.000f;
+          ResetMagickMemory(&image->chromaticity,0,sizeof(image->chromaticity));
+          if (IssRGBColorspace(image->colorspace) != MagickFalse)
+            {
+              image->rendering_intent=PerceptualIntent;
+              image->gamma=1.000f/2.200f;
+              image->chromaticity.red_primary.x=0.6400f;
+              image->chromaticity.red_primary.y=0.3300f;
+              image->chromaticity.red_primary.z=0.0300f;
+              image->chromaticity.green_primary.x=0.3000f;
+              image->chromaticity.green_primary.y=0.6000f;
+              image->chromaticity.green_primary.z=0.1000f;
+              image->chromaticity.blue_primary.x=0.1500f;
+              image->chromaticity.blue_primary.y=0.0600f;
+              image->chromaticity.blue_primary.z=0.7900f;
+              image->chromaticity.white_point.x=0.3127f;
+              image->chromaticity.white_point.y=0.3290f;
+              image->chromaticity.white_point.z=0.3583f;
+            }
           break;
         }
-      if (LocaleCompare(property,"compose") == 0)
+      if (LocaleCompare("compose",property) == 0)
         {
           ssize_t
             compose;
@@ -3322,7 +3513,7 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           image->compose=(CompositeOperator) compose;
           break;
         }
-      if (LocaleCompare(property,"compress") == 0)
+      if (LocaleCompare("compress",property) == 0)
         {
           ssize_t
             compression;
@@ -3334,6 +3525,13 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           image->compression=(CompressionType) compression;
           break;
         }
+      if (LocaleCompare("copyright",property) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","'%s'",property);
+          status=MagickFalse;
+          break;
+        }
       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
         ConstantString(property),ConstantString(value));
       break;
@@ -3341,7 +3539,7 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
     case 'D':
     case 'd':
     {
-      if (LocaleCompare(property,"delay") == 0)
+      if (LocaleCompare("delay",property) == 0)
         {
           GeometryInfo
             geometry_info;
@@ -3365,23 +3563,23 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
             image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
           break;
         }
-      if (LocaleCompare(property,"density") == 0)
+      if (LocaleCompare("density",property) == 0)
         {
           GeometryInfo
             geometry_info;
 
           flags=ParseGeometry(value,&geometry_info);
-          image->x_resolution=geometry_info.rho;
-          image->y_resolution=geometry_info.sigma;
+          image->resolution.x=geometry_info.rho;
+          image->resolution.y=geometry_info.sigma;
           if ((flags & SigmaValue) == 0)
-            image->y_resolution=image->x_resolution;
+            image->resolution.y=image->resolution.x;
         }
-      if (LocaleCompare(property,"depth") == 0)
+      if (LocaleCompare("depth",property) == 0)
         {
           image->depth=StringToUnsignedLong(value);
           break;
         }
-      if (LocaleCompare(property,"dispose") == 0)
+      if (LocaleCompare("dispose",property) == 0)
         {
           ssize_t
             dispose;
@@ -3399,7 +3597,12 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
     case 'G':
     case 'g':
     {
-      if (LocaleCompare(property,"gravity") == 0)
+      if (LocaleCompare("gamma",property) == 0)
+        {
+          image->gamma=StringToDouble(value,(char **) NULL);
+          break;
+        }
+      if (LocaleCompare("gravity",property) == 0)
         {
           ssize_t
             gravity;
@@ -3414,10 +3617,19 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
         ConstantString(property),ConstantString(value));
       break;
     }
+    case 'H':
+    case 'h':
+      if (LocaleCompare("height",property) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","'%s'",property);
+          status=MagickFalse;
+          break;
+        }
     case 'I':
     case 'i':
     {
-      if (LocaleCompare(property,"intent") == 0)
+      if (LocaleCompare("intent",property) == 0)
         {
           ssize_t
             rendering_intent;
@@ -3429,7 +3641,7 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           image->rendering_intent=(RenderingIntent) rendering_intent;
           break;
         }
-      if (LocaleCompare(property,"interpolate") == 0)
+      if (LocaleCompare("interpolate",property) == 0)
         {
           ssize_t
             interpolate;
@@ -3438,17 +3650,26 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
             value);
           if (interpolate < 0)
             break;
-          image->interpolate=(InterpolatePixelMethod) interpolate;
+          image->interpolate=(PixelInterpolateMethod) interpolate;
           break;
         }
       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
         ConstantString(property),ConstantString(value));
       break;
     }
+    case 'K':
+    case 'k':
+      if (LocaleCompare("kurtosis",property) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","'%s'",property);
+          status=MagickFalse;
+          break;
+        }
     case 'L':
     case 'l':
     {
-      if (LocaleCompare(property,"loop") == 0)
+      if (LocaleCompare("loop",property) == 0)
         {
           image->iterations=StringToUnsignedLong(value);
           break;
@@ -3457,10 +3678,32 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
         ConstantString(property),ConstantString(value));
       break;
     }
+    case 'M':
+    case 'm':
+      if ( (LocaleCompare("magick",property) == 0) ||
+           (LocaleCompare("max",property) == 0) ||
+           (LocaleCompare("mean",property) == 0) ||
+           (LocaleCompare("min",property) == 0) ||
+           (LocaleCompare("min",property) == 0) )
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+             "SetReadOnlyProperty","'%s'",property);
+          status=MagickFalse;
+          break;
+        }
+    case 'O':
+    case 'o':
+      if (LocaleCompare("opaque",property) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","'%s'",property);
+          status=MagickFalse;
+          break;
+        }
     case 'P':
     case 'p':
     {
-      if (LocaleCompare(property,"page") == 0)
+      if (LocaleCompare("page",property) == 0)
         {
           char
             *geometry;
@@ -3470,7 +3713,7 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           geometry=DestroyString(geometry);
           break;
         }
-      if (LocaleCompare(property,"profile") == 0)
+      if (LocaleCompare("profile",property) == 0)
         {
           ImageInfo
             *image_info;
@@ -3483,7 +3726,7 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           (void) SetImageInfo(image_info,1,exception);
           profile=FileToStringInfo(image_info->filename,~0UL,exception);
           if (profile != (StringInfo *) NULL)
-            status=SetImageProfile(image,image_info->magick,profile);
+            status=SetImageProfile(image,image_info->magick,profile,exception);
           image_info=DestroyImageInfo(image_info);
           break;
         }
@@ -3494,7 +3737,7 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
     case 'R':
     case 'r':
     {
-      if (LocaleCompare(property,"rendering-intent") == 0)
+      if (LocaleCompare("rendering-intent",property) == 0)
         {
           ssize_t
             rendering_intent;
@@ -3502,18 +3745,31 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
             value);
           if (rendering_intent < 0)
-            break;
-          image->rendering_intent=(RenderingIntent) rendering_intent;
+            status=MagickFalse;
+          else
+            image->rendering_intent=(RenderingIntent) rendering_intent;
           break;
         }
       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
         ConstantString(property),ConstantString(value));
       break;
     }
+    case 'S':
+    case 's':
+      if ( (LocaleCompare("size",property) == 0) ||
+           (LocaleCompare("skewness",property) == 0) ||
+           (LocaleCompare("scenes",property) == 0) ||
+           (LocaleCompare("standard-deviation",property) == 0) )
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","'%s'",property);
+          status=MagickFalse;
+          break;
+        }
     case 'T':
     case 't':
     {
-      if (LocaleCompare(property,"tile-offset") == 0)
+      if (LocaleCompare("tile-offset",property) == 0)
         {
           char
             *geometry;
@@ -3530,7 +3786,7 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
     case 'U':
     case 'u':
     {
-      if (LocaleCompare(property,"units") == 0)
+      if (LocaleCompare("units",property) == 0)
         {
           ssize_t
             units;
@@ -3545,6 +3801,24 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
         ConstantString(property),ConstantString(value));
       break;
     }
+    case 'V':
+    case 'v':
+      if (LocaleCompare("version",property) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","'%s'",property);
+          status=MagickFalse;
+          break;
+        }
+    case 'W':
+    case 'w':
+      if (LocaleCompare("width",property) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","'%s'",property);
+          status=MagickFalse;
+          break;
+        }
     default:
     {
       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,