]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/property.c
(no commit message)
[imagemagick] / MagickCore / property.c
index dc083e51790df7373f493bec1d1b76b6a4a48195..9868e822caf2b5b2c1b0e4197b4dace34c2b62bd 100644 (file)
 %                         MagickCore Property Methods                         %
 %                                                                             %
 %                              Software Design                                %
-%                                John Cristy                                  %
+%                                   Cristy                                    %
 %                                 March 2000                                  %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2014 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  %
@@ -36,7 +36,8 @@
 %
 %
 */
-\f
+
+
 /*
   Include declarations.
 */
@@ -44,6 +45,7 @@
 #include "MagickCore/artifact.h"
 #include "MagickCore/attribute.h"
 #include "MagickCore/cache.h"
+#include "MagickCore/cache-private.h"
 #include "MagickCore/color.h"
 #include "MagickCore/color-private.h"
 #include "MagickCore/colorspace-private.h"
 #include "MagickCore/version.h"
 #include "MagickCore/xml-tree.h"
 #include "MagickCore/xml-tree-private.h"
+#if defined(MAGICKCORE_LCMS_DELEGATE)
+#if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
+#include <lcms/lcms2.h>
+#elif defined(MAGICKCORE_HAVE_LCMS2_H)
+#include "lcms2.h"
+#elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
+#include <lcms/lcms.h>
+#else
+#include "lcms.h"
+#endif
+#endif
+\f
+/*
+  Define declarations.
+*/
+#if defined(MAGICKCORE_LCMS_DELEGATE)
+#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
+#define cmsUInt32Number  DWORD
+#endif
+#endif
 \f
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -147,6 +169,7 @@ MagickExport MagickBooleanType CloneImageProperties(Image *image,
   image->extract_info=clone_image->extract_info;
   image->filter=clone_image->filter;
   image->fuzz=clone_image->fuzz;
+  image->intensity=clone_image->intensity;
   image->interlace=clone_image->interlace;
   image->interpolate=clone_image->interpolate;
   image->endian=clone_image->endian;
@@ -188,13 +211,13 @@ MagickExport MagickBooleanType CloneImageProperties(Image *image,
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
 %  DefineImageProperty() associates an assignment string of the form
-%  "key=value" with per-image artifact. It is equivelent to
-%  SetImageProperity().
+%  "key=value" with an artifact or options. It is equivelent to
+%  SetImageProperty()
 %
 %  The format of the DefineImageProperty method is:
 %
-%      MagickBooleanType DefineImageProperty(Image *image,
-%        const char *property,ExceptionInfo *exception)
+%      MagickBooleanType DefineImageProperty(Image *image,const char *property,
+%        ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -276,7 +299,7 @@ MagickExport MagickBooleanType DeleteImageProperty(Image *image,
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  DestroyImageProperties() destroys all properities and associated memory
+%  DestroyImageProperties() destroys all properties and associated memory
 %  attached to the given image.
 %
 %  The format of the DestroyDefines method is:
@@ -370,8 +393,12 @@ 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.
+%  This includes,  profile prefixes, such as "exif:", "iptc:" and "8bim:"
+%  It does not handle non-prifile prefixes, such as "fx:", "option:", or
+%  "artifact:".
+%
+%  The returned string is stored as a properity of the same name for faster
+%  lookup later. It should NOT be freed by the caller.
 %
 %  The format of the GetImageProperty method is:
 %
@@ -533,7 +560,7 @@ static inline unsigned short ReadPropertyMSBShort(const unsigned char **p,
     value;
 
   if (*length < 2)
-    return((unsigned short) ~0U);
+    return((unsigned short) ~0);
   for (i=0; i < 2; i++)
   {
     c=(int) (*(*p)++);
@@ -700,13 +727,13 @@ static inline unsigned short ReadPropertyShort(const EndianType endian,
   unsigned short
     value;
 
-  if (endian == MSBEndian)
+  if (endian == LSBEndian)
     {
-      value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
-        ((unsigned char *) buffer)[1]);
+      value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
       return((unsigned short) (value & 0xffff));
     }
-  value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
+  value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
+    ((unsigned char *) buffer)[1]);
   return((unsigned short) (value & 0xffff));
 }
 
@@ -716,14 +743,14 @@ static inline size_t ReadPropertyLong(const EndianType endian,
   size_t
     value;
 
-  if (endian == MSBEndian)
+  if (endian == LSBEndian)
     {
-      value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
-        (buffer[2] << 8) | buffer[3]);
+      value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
+        (buffer[1] << 8 ) | (buffer[0]));
       return((size_t) (value & 0xffffffff));
     }
-  value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
-    (buffer[1] << 8 ) | (buffer[0]));
+  value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
+    (buffer[2] << 8) | buffer[3]);
   return((size_t) (value & 0xffffffff));
 }
 
@@ -1095,7 +1122,7 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
       { 0x1001c, "exif:GPSAreaInformation" },
       { 0x1001d, "exif:GPSDateStamp" },
       { 0x1001e, "exif:GPSDifferential" },
-      {  0x0000, NULL}
+      { 0x00000, (const char *) NULL }
     };
 
   const StringInfo
@@ -1425,12 +1452,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:
@@ -1476,6 +1505,7 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
               if (p == (const char *) NULL)
                 (void) SetImageProperty((Image *) image,key,value,exception);
               value=DestroyString(value);
+              key=DestroyString(key);
               status=MagickTrue;
             }
         }
@@ -1523,6 +1553,63 @@ static MagickBooleanType GetEXIFProperty(const Image *image,
   return(status);
 }
 
+static MagickBooleanType GetICCProperty(const Image *image,const char *property,
+  ExceptionInfo *exception)
+{
+  const StringInfo
+    *profile;
+
+  magick_unreferenced(property);
+
+  profile=GetImageProfile(image,"icc");
+  if (profile == (StringInfo *) NULL)
+    profile=GetImageProfile(image,"icm");
+  if (profile == (StringInfo *) NULL)
+    return(MagickFalse);
+  if (GetStringInfoLength(profile) < 128)
+    return(MagickFalse);  /* minimum ICC profile length */
+#if defined(MAGICKCORE_LCMS_DELEGATE)
+  {
+    cmsHPROFILE
+      icc_profile;
+
+    icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
+      (cmsUInt32Number) GetStringInfoLength(profile));
+    if (icc_profile != (cmsHPROFILE *) NULL)
+      {
+#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
+        const char
+          *name;
+
+        name=cmsTakeProductName(icc_profile);
+        if (name != (const char *) NULL)
+          (void) SetImageProperty((Image *) image,"icc:name",name,exception);
+#else
+        char
+          info[MaxTextExtent];
+
+        (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,
+          "en","US",info,MaxTextExtent);
+        (void) SetImageProperty((Image *) image,"icc:description",info,
+          exception);
+        (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoManufacturer,
+          "en","US",info,MaxTextExtent);
+        (void) SetImageProperty((Image *) image,"icc:manufacturer",info,
+          exception);
+        (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoModel,"en",
+          "US",info,MaxTextExtent);
+        (void) SetImageProperty((Image *) image,"icc:model",info,exception);
+        (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoCopyright,
+          "en","US",info,MaxTextExtent);
+        (void) SetImageProperty((Image *) image,"icc:copyright",info,exception);
+#endif
+        (void) cmsCloseProfile(icc_profile);
+      }
+  }
+#endif
+  return(MagickTrue);
+}
+
 static MagickBooleanType GetXMPProperty(const Image *image,const char *property)
 {
   char
@@ -1910,9 +1997,9 @@ 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( IfMagickFalse(in_subpath) )
+        if (in_subpath == MagickFalse)
           {
-            (void) FormatLocaleString(message,MaxTextExtent,"M %g,%g\n",
+            (void) FormatLocaleString(message,MaxTextExtent,"M %g %g\n",
               point[1].x,point[1].y);
             for (i=0; i < 3; i++)
             {
@@ -1922,14 +2009,28 @@ static char *TraceSVGClippath(const unsigned char *blob,size_t length,
           }
         else
           {
+            /*
+              Handle special cases when Bezier curves are used to describe
+              corners and straight lines.
+            */
             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
                 (point[0].x == point[1].x) && (point[0].y == point[1].y))
-              (void) FormatLocaleString(message,MaxTextExtent,"L %g,%g\n",
-                point[1].x,point[1].y);
-            else
               (void) FormatLocaleString(message,MaxTextExtent,
-                "C %g,%g %g,%g %g,%g\n",last[2].x,last[2].y,
-                point[0].x,point[0].y,point[1].x,point[1].y);
+                "L %g %g\n",point[1].x,point[1].y);
+            else
+              if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
+                (void) FormatLocaleString(message,MaxTextExtent,
+                  "V %g %g %g %g\n",point[0].x,point[0].y,
+                  point[1].x,point[1].y);
+              else
+                if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
+                  (void) FormatLocaleString(message,MaxTextExtent,
+                    "Y %g %g %g %g\n",last[2].x,last[2].y,
+                    point[1].x,point[1].y);
+                else
+                  (void) FormatLocaleString(message,MaxTextExtent,
+                    "C %g %g %g %g %g %g\n",last[2].x,
+                    last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
             for (i=0; i < 3; i++)
               last[i]=point[i];
           }
@@ -1941,17 +2042,29 @@ static char *TraceSVGClippath(const unsigned char *blob,size_t length,
         */
         if (knot_count == 0)
           {
+           /*
+              Same special handling as above except we compare to the
+              first point in the path and close the path.
+            */
             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
                 (first[0].x == first[1].x) && (first[0].y == first[1].y))
               (void) FormatLocaleString(message,MaxTextExtent,
-                "L %g,%g Z\n",first[1].x,first[1].y);
+                "L %g %g Z\n",first[1].x,first[1].y);
             else
-              {
+              if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
                 (void) FormatLocaleString(message,MaxTextExtent,
-                  "C %g,%g %g,%g %g,%g Z\n",last[2].x,
-                  last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
-                (void) ConcatenateString(&path,message);
-              }
+                  "V %g %g %g %g Z\n",first[0].x,first[0].y,
+                  first[1].x,first[1].y);
+              else
+                if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
+                  (void) FormatLocaleString(message,MaxTextExtent,
+                    "Y %g %g %g %g Z\n",last[2].x,last[2].y,
+                    first[1].x,first[1].y);
+                else
+                  (void) FormatLocaleString(message,MaxTextExtent,
+                    "C %g %g %g %g %g %g Z\n",last[2].x,
+                    last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
+            (void) ConcatenateString(&path,message);
             in_subpath=MagickFalse;
           }
         break;
@@ -1983,21 +2096,12 @@ static char *TraceSVGClippath(const unsigned char *blob,size_t length,
 MagickExport const char *GetImageProperty(const Image *image,
   const char *property,ExceptionInfo *exception)
 {
-  FxInfo
-    *fx_info;
-
-  double
-    alpha;
-
-  MagickStatusType
-    status;
-
   register const char
     *p;
 
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
-  if( IfMagickTrue(image->debug) )
+  if (IfMagickTrue(image->debug))
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   p=(const char *) NULL;
   if (image->properties != (void *) NULL)
@@ -2009,13 +2113,10 @@ MagickExport const char *GetImageProperty(const Image *image,
             image->properties);
           return(p);
         }
-      if (LocaleNCompare("fx:",property,3) != 0) /* NOT %[fx:..] !!!! */
-        {
-          p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
-            image->properties,property);
-          if (p != (const char *) NULL)
-            return(p);
-        }
+        p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
+          image->properties,property);
+        if (p != (const char *) NULL)
+          return(p);
     }
   if ((property == (const char *) NULL) ||
       (strchr(property,':') == (char *) NULL))
@@ -2026,13 +2127,8 @@ MagickExport const char *GetImageProperty(const Image *image,
     {
       if (LocaleNCompare("8bim:",property,5) == 0)
         {
-          if( IfMagickTrue(Get8BIMProperty(image,property,exception)) &&
-              (image->properties != (void *) NULL))
-            {
-              p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
-                image->properties,property);
-              return(p);
-            }
+          (void) Get8BIMProperty(image,property,exception);
+          break;
         }
       break;
     }
@@ -2041,97 +2137,24 @@ MagickExport const char *GetImageProperty(const Image *image,
     {
       if (LocaleNCompare("exif:",property,5) == 0)
         {
-          if( IfMagickTrue(GetEXIFProperty(image,property,exception)) &&
-              (image->properties != (void *) NULL))
-            {
-              p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
-                image->properties,property);
-              return(p);
-            }
-        }
-      break;
-    }
-    case 'F':
-    case 'f':
-    {
-      if (LocaleNCompare("fx:",property,3) == 0)
-        {
-          fx_info=AcquireFxInfo(image,property+3,exception);
-          status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0,
-            &alpha,exception);
-          fx_info=DestroyFxInfo(fx_info);
-          if( IfMagickTrue(status) )
-            {
-              char
-                value[MaxTextExtent];
-
-              (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
-                GetMagickPrecision(),(double) alpha);
-              (void) SetImageProperty((Image *) image,property,value,exception);
-            }
-          if (image->properties != (void *) NULL)
-            {
-              p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
-                image->properties,property);
-              return(p);
-            }
+          (void) GetEXIFProperty(image,property,exception);
+          break;
         }
       break;
     }
     case 'I':
     case 'i':
     {
-      if (LocaleNCompare("iptc:",property,5) == 0)
+      if ((LocaleNCompare("icc:",property,4) == 0) ||
+          (LocaleNCompare("icm:",property,4) == 0))
         {
-          if( IfMagickTrue(GetIPTCProperty(image,property,exception)) &&
-              (image->properties != (void *) NULL))
-            {
-              p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
-                image->properties,property);
-              return(p);
-            }
+          (void) GetICCProperty(image,property,exception);
+          break;
         }
-      break;
-    }
-    case 'P':
-    case 'p':
-    {
-      if (LocaleNCompare("pixel:",property,6) == 0)
+      if (LocaleNCompare("iptc:",property,5) == 0)
         {
-          PixelInfo
-            pixel;
-
-          GetPixelInfo(image,&pixel);
-          fx_info=AcquireFxInfo(image,property+6,exception);
-          status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
-            &alpha,exception);
-          pixel.red=(double) QuantumRange*alpha;
-          status|=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0,
-            &alpha,exception);
-          pixel.green=(double) QuantumRange*alpha;
-          status|=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0,
-            &alpha,exception);
-          pixel.blue=(double) QuantumRange*alpha;
-          if (image->colorspace == CMYKColorspace)
-            {
-              status|=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0,
-                &alpha,exception);
-              pixel.black=(double) QuantumRange*alpha;
-            }
-          status|=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0,
-            &alpha,exception);
-          pixel.alpha=(double) QuantumRange*(1.0-alpha);
-          fx_info=DestroyFxInfo(fx_info);
-          if( IfMagickTrue(status) )
-            {
-              char
-                name[MaxTextExtent];
-
-              (void) QueryColorname(image,&pixel,SVGCompliance,name,
-                exception);
-              (void) SetImageProperty((Image *) image,property,name,exception);
-              return(GetImageProperty(image,property,exception));
-            }
+          (void) GetIPTCProperty(image,property,exception);
+          break;
         }
       break;
     }
@@ -2140,20 +2163,21 @@ MagickExport const char *GetImageProperty(const Image *image,
     {
       if (LocaleNCompare("xmp:",property,4) == 0)
         {
-          if( IfMagickTrue(GetXMPProperty(image,property)) &&
-              (image->properties != (void *) NULL))
-            {
-              p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
-                image->properties,property);
-              return(p);
-            }
+          (void) GetXMPProperty(image,property);
+          break;
         }
       break;
     }
     default:
       break;
   }
-  return(p);
+  if (image->properties != (void *) NULL)
+    {
+      p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
+        image->properties,property);
+      return(p);
+    }
+  return((const char *) NULL);
 }
 \f
 /*
@@ -2168,50 +2192,72 @@ MagickExport const char *GetImageProperty(const Image *image,
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
 %  GetMagickProperty() gets attributes or calculated values that is associated
-%  with a fixed known property name, or single letter property.
+%  with a fixed known property name, or single letter property. It may be
+%  called if no image is defined (IMv7), in which case only global image_info
+%  values are available.
 %
-%  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.
+%  This routine only handles specifically known properties.  It does not
+%  handle special prefixed properties, profiles, or expressions. Nor does
+%  it return any free-form property strings.
 %
-%  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 returned string is stored in a structure somewhere, and should not be
+%  directly freed.  If the string was generated (common) the string will be
+%  stored as as either as artifact or option 'get-property'.  These may be
+%  deleted (cleaned up) when no longer required, but neither artifact or
+%  option is guranteed to exist.
 %
 %  The format of the GetMagickProperty method is:
 %
-%      const char *GetMagickProperty(const ImageInfo *image_info,Image *image,
+%      const char *GetMagickProperty(ImageInfo *image_info,Image *image,
 %        const char *property,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
-%    o image_info: the image info.
+%    o image_info: the image info (optional)
 %
-%    o image: the image.
+%    o image: the image (optional)
 %
 %    o key: the key.
 %
 %    o exception: return any errors or warnings in this structure.
 %
 */
-static const char *GetMagickPropertyLetter(const ImageInfo *image_info,
+#define WarnNoImageReturn(format,arg) \
+  if (image == (Image *) NULL ) { \
+    (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
+        "NoImageForProperty",format,arg); \
+    return((const char *)NULL); \
+  }
+#define WarnNoImageInfoReturn(format,arg) \
+  if (image_info == (ImageInfo *) NULL ) { \
+    (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \
+        "NoImageInfoForProperty",format,arg); \
+    return((const char *)NULL); \
+  }
+
+static const char *GetMagickPropertyLetter(ImageInfo *image_info,
   Image *image,const char letter,ExceptionInfo *exception)
 {
   char
-    value[MaxTextExtent];
+    value[MaxTextExtent];  /* formated string to store as a returned artifact */
 
   const char
-    *string;
+    *string;     /* return a string already stored somewher */
 
   if (image != (Image *) NULL && IfMagickTrue(image->debug))
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  *value='\0';  /* formatted string */
+  else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
+
+  *value='\0';           /* formatted string */
   string=(char *) NULL;  /* constant string reference */
+
+  /* Get properities that are directly defined by images */
   switch (letter)
   {
     case 'b':  /* image size read in - in bytes */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
         ((MagickOffsetType) image->extent));
       if (image->extent != (MagickSizeType) ((size_t) image->extent))
@@ -2221,28 +2267,36 @@ static const char *GetMagickPropertyLetter(const ImageInfo *image_info,
     }
     case 'c':  /* image comment property - empty string by default */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       string=GetImageProperty(image,"comment",exception);
-      if (string == (const char *) NULL)
+      if ( string == (const char *) NULL )
         string="";
       break;
     }
     case 'd':  /* Directory component of filename */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       GetPathComponent(image->magick_filename,HeadPath,value);
+      if (*value == '\0') string="";
       break;
     }
     case 'e': /* Filename extension (suffix) of image file */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       GetPathComponent(image->magick_filename,ExtensionPath,value);
+      if (*value == '\0') string="";
       break;
     }
     case 'f': /* Filename without directory component */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       GetPathComponent(image->magick_filename,TailPath,value);
+      if (*value == '\0') string="";
       break;
     }
     case 'g': /* Image geometry, canvas and offset  %Wx%H+%X+%Y */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
         (double) image->page.width,(double) image->page.height,
         (double) image->page.x,(double) image->page.y);
@@ -2250,12 +2304,14 @@ static const char *GetMagickPropertyLetter(const ImageInfo *image_info,
     }
     case 'h': /* Image height (current) */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (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) */
+    case 'i': /* Filename last used for an image (read or write) */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       string=image->filename;
       break;
     }
@@ -2264,231 +2320,291 @@ static const char *GetMagickPropertyLetter(const ImageInfo *image_info,
       /*
         FUTURE: ensure this does not generate the formatted comment!
       */
+      WarnNoImageReturn("\"%%%c\"",letter);
       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
         GetNumberColors(image,(FILE *) NULL,exception));
       break;
     }
     case 'l': /* Image label property - empty string by default */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       string=GetImageProperty(image,"label",exception);
-      if ( string == (const char *)NULL)
+      if ( string == (const char *) NULL)
         string="";
       break;
     }
     case 'm': /* Image format (file magick) */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       string=image->magick;
       break;
     }
     case 'n': /* Number of images in the list.  */
     {
-      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
-        GetImageListLength(image));
+      if ( image != (Image *) NULL )
+        (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+          GetImageListLength(image));
+      else
+        string="0";    /* no images or scenes */
       break;
     }
     case 'o': /* Output Filename - for delegate use only */
-    {
+      WarnNoImageInfoReturn("\"%%%c\"",letter);
       string=image_info->filename;
       break;
-    }
-    case 'p': /* Image index in current image list -- As 'n' OBSOLETE */
+    case 'p': /* Image index in current image list */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
         GetImageIndexInList(image));
       break;
     }
     case 'q': /* Quantum depth of image in memory */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
         MAGICKCORE_QUANTUM_DEPTH);
       break;
     }
-    case 'r': /* Image storage class and colorspace.  */
+    case 'r': /* Image storage class, colorspace, and alpha enabled.  */
     {
       ColorspaceType
         colorspace;
 
+      WarnNoImageReturn("\"%%%c\"",letter);
       colorspace=image->colorspace;
       if (IfMagickTrue(IsImageGray(image,exception)))
-        colorspace=GRAYColorspace;
+        colorspace=GRAYColorspace;   /* FUTURE: this is IMv6 not IMv7 */
       (void) FormatLocaleString(value,MaxTextExtent,"%s %s %s",
         CommandOptionToMnemonic(MagickClassOptions,(ssize_t) image->storage_class),
         CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) colorspace),
-        image->alpha_trait == BlendPixelTrait ? "Matte" : "");
+        image->alpha_trait == BlendPixelTrait ? "Alpha" : "");
       break;
     }
     case 's': /* Image scene number */
     {
+#if 0  /* this seems non-sensical -- simplifing */
       if (image_info->number_scenes != 0)
         (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
           image_info->scene);
-      else
+      else if (image != (Image *)NULL)
         (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
           image->scene);
+      else
+          string="0";
+#else
+      WarnNoImageReturn("\"%%%c\"",letter);
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+         image->scene);
+#endif
       break;
     }
     case 't': /* Base filename without directory or extention */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       GetPathComponent(image->magick_filename,BasePath,value);
+      if (*value == '\0') string="";
       break;
     }
     case 'u': /* Unique filename */
-    {
+      WarnNoImageInfoReturn("\"%%%c\"",letter);
       string=image_info->unique;
       break;
-    }
     case 'w': /* Image width (current) */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (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));
+      WarnNoImageReturn("\"%%%c\"",letter);
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
+        fabs(image->resolution.x) > MagickEpsilon ? image->resolution.x : 72.0);
       break;
     }
     case 'y': /* Image vertical resolution (with units) */
     {
-      (void) FormatLocaleString(value,MaxTextExtent,"%g %s",
-        image->resolution.y,CommandOptionToMnemonic(MagickResolutionOptions,
-        (ssize_t) image->units));
+      WarnNoImageReturn("\"%%%c\"",letter);
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
+        fabs(image->resolution.y) > MagickEpsilon ? image->resolution.y : 72.0);
       break;
     }
     case 'z': /* Image depth as read in */
     {
-      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
-        image->depth);
+      WarnNoImageReturn("\"%%%c\"",letter);
+      (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));
+      WarnNoImageReturn("\"%%%c\"",letter);
+      string=CommandOptionToMnemonic(MagickBooleanOptions,
+        (ssize_t) image->alpha_trait);
       break;
     }
     case 'C': /* Image compression method.  */
     {
-      (void) FormatLocaleString(value,MaxTextExtent,"%s",
-        CommandOptionToMnemonic(MagickCompressOptions,(ssize_t)
-          image->compression));
+      WarnNoImageReturn("\"%%%c\"",letter);
+      string=CommandOptionToMnemonic(MagickCompressOptions,
+        (ssize_t) image->compression);
       break;
     }
     case 'D': /* Image dispose method.  */
     {
-      (void) FormatLocaleString(value,MaxTextExtent,"%s",
-        CommandOptionToMnemonic(MagickDisposeOptions,(ssize_t) image->dispose));
+      WarnNoImageReturn("\"%%%c\"",letter);
+      string=CommandOptionToMnemonic(MagickDisposeOptions,
+        (ssize_t) image->dispose);
       break;
     }
     case 'G': /* Image size as geometry = "%wx%h" */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (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);
+      WarnNoImageReturn("\"%%%c\"",letter);
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
+        (double) image->page.height);
       break;
     }
     case 'M': /* Magick filename - filename given incl. coder & read mods */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       string=image->magick_filename;
       break;
     }
     case 'O': /* layer canvas offset with sign = "+%X+%Y" */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (void) FormatLocaleString(value,MaxTextExtent,"%+ld%+ld",(long)
         image->page.x,(long) image->page.y);
       break;
     }
     case 'P': /* layer canvas page size = "%Wx%H" */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g",
         (double) image->page.width,(double) image->page.height);
       break;
     }
     case 'Q': /* image compression quality */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
-        image->quality);
+        (image->quality == 0 ? 92 : image->quality));
       break;
     }
-    case 'S': /* Image scenes  ???? */
+    case 'S': /* Number of scenes in image list.  */
     {
+      WarnNoImageInfoReturn("\"%%%c\"",letter);
+#if 0 /* What is this number? -- it makes no sense - simplifing */
       if (image_info->number_scenes == 0)
-        string="2147483647";
-      else
+         string="2147483647";
+      else if ( image != (Image *) NULL )
         (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
-          image_info->scene+image_info->number_scenes);
+                image_info->scene+image_info->number_scenes);
+      else
+        string="0";
+#else
+      (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+        (image_info->number_scenes == 0 ? 2147483647 :
+         image_info->number_scenes));
+#endif
       break;
     }
     case 'T': /* image time delay for animations */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
         image->delay);
       break;
     }
+    case 'U': /* Image resolution units. */
+    {
+      WarnNoImageReturn("\"%%%c\"",letter);
+      string=CommandOptionToMnemonic(MagickResolutionOptions,
+        (ssize_t) image->units);
+      break;
+    }
     case 'W': /* layer canvas width */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
         image->page.width);
       break;
     }
     case 'X': /* layer canvas X offset */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double)
         image->page.x);
       break;
     }
     case 'Y': /* layer canvas Y offset */
     {
+      WarnNoImageReturn("\"%%%c\"",letter);
       (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double)
         image->page.y);
       break;
     }
     case 'Z': /* Zero filename ??? */
-    {
+      WarnNoImageInfoReturn("\"%%%c\"",letter);
       string=image_info->zero;
       break;
-    }
-    case '@': /* Trim bounding box, without Trimming! */
+    case '%': /* percent escaped */
+      string="%";
+      break;
+    case '@': /* Trim bounding box, without actually Trimming! */
     {
       RectangleInfo
         page;
 
+      WarnNoImageReturn("\"%%%c\"",letter);
       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 */
+    case '#':
     {
+      /*
+        Image signature.
+      */
+      WarnNoImageReturn("\"%%%c\"",letter);
       (void) SignatureImage(image,exception);
       string=GetImageProperty(image,"signature",exception);
       break;
     }
-    case '%': /* percent escaped */
-    {
-      string="%";
-      break;
-    }
   }
-  if (*value != '\0')
-    string=value;
   if (string != (char *) NULL)
+    return(string);
+  if (*value != '\0')
     {
-      (void) SetImageArtifact(image,"get-property",string);
-      return(GetImageArtifact(image,"get-property"));
+      /*
+        Create a cloned copy of result.
+      */
+      if (image != (Image *)NULL)
+        {
+          (void) SetImageArtifact(image,"get-property",value);
+          return(GetImageArtifact(image,"get-property"));
+        }
+      else
+        {
+          (void) SetImageOption(image_info,"get-property",value);
+          return(GetImageOption(image_info,"get-property"));
+        }
     }
   return((char *)NULL);
 }
 
-MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
+MagickExport const char *GetMagickProperty(ImageInfo *image_info,
   Image *image,const char *property,ExceptionInfo *exception)
 {
   char
@@ -2498,11 +2614,17 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     *string;
 
   assert(property[0] != '\0');
+  assert(image != (Image *)NULL || image_info != (ImageInfo *)NULL );
+
   if (property[1] == '\0')  /* single letter property request */
     return(GetMagickPropertyLetter(image_info,image,*property,exception));
-  if ((image != (Image *) NULL) && IfMagickTrue(image->debug))
+
+  if (image != (Image *) NULL && IfMagickTrue(image->debug))
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  *value='\0';  /* formated string */
+  else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images");
+
+  *value='\0';           /* formated string */
   string=(char *) NULL;  /* constant string reference */
   switch (*property)
   {
@@ -2511,7 +2633,15 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
       if ((LocaleCompare("base",property) == 0) ||
           (LocaleCompare("basename",property) == 0) )
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           GetPathComponent(image->magick_filename,BasePath,value);
+          if (*value == '\0') string="";
+          break;
+        }
+      if (LocaleCompare("bit-depth",property) == 0)
+        {
+          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
+            GetImageDepth(image, exception));
           break;
         }
       break;
@@ -2520,6 +2650,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     {
       if (LocaleCompare("channels",property) == 0)
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           /* FUTURE: return actual image channels */
           (void) FormatLocaleString(value,MaxTextExtent,"%s",
             CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
@@ -2531,15 +2662,10 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
         }
       if (LocaleCompare("colorspace",property) == 0)
         {
-          ColorspaceType
-            colorspace;
-
+          WarnNoImageReturn("\"%%[%s]\"",property);
           /* FUTURE: return actual colorspace - no 'gray' stuff */
-          colorspace=image->colorspace;
-          if( IfMagickTrue(IsImageGray(image,exception)) )
-            colorspace=GRAYColorspace;
           string=CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
-            colorspace);
+            image->colorspace);
           break;
         }
       if (LocaleCompare("copyright",property) == 0)
@@ -2553,13 +2679,16 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     {
       if (LocaleCompare("depth",property) == 0)
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
             image->depth);
           break;
         }
       if (LocaleCompare("directory",property) == 0)
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           GetPathComponent(image->magick_filename,HeadPath,value);
+          if (*value == '\0') string="";
           break;
         }
       break;
@@ -2568,7 +2697,9 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     {
       if (LocaleCompare("extension",property) == 0)
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           GetPathComponent(image->magick_filename,ExtensionPath,value);
+          if (*value == '\0') string="";
           break;
         }
       break;
@@ -2577,13 +2708,14 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     {
       if (LocaleCompare("gamma",property) == 0)
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
             GetMagickPrecision(),image->gamma);
           break;
         }
-      if ( (image_info != (ImageInfo *) NULL) &&
-           (LocaleCompare("group",property) == 0) )
+      if (LocaleCompare("group",property) == 0)
         {
+          WarnNoImageInfoReturn("\"%%[%s]\"",property);
           (void) FormatLocaleString(value,MaxTextExtent,"0x%lx",(unsigned long)
             image_info->group);
           break;
@@ -2594,6 +2726,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     {
       if (LocaleCompare("height",property) == 0)
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
             image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
           break;
@@ -2604,6 +2737,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     {
       if (LocaleCompare("input",property) == 0)
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           string=image->filename;
           break;
         }
@@ -2617,6 +2751,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
             kurtosis,
             skewness;
 
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
             GetMagickPrecision(),kurtosis);
@@ -2628,6 +2763,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     {
       if (LocaleCompare("magick",property) == 0)
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           string=image->magick;
           break;
         }
@@ -2637,6 +2773,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
             maximum,
             minimum;
 
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) GetImageRange(image,&minimum,&maximum,exception);
           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
             GetMagickPrecision(),maximum);
@@ -2648,6 +2785,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
             mean,
             standard_deviation;
 
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) GetImageMean(image,&mean,&standard_deviation,exception);
           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
             GetMagickPrecision(),mean);
@@ -2659,6 +2797,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
             maximum,
             minimum;
 
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) GetImageRange(image,&minimum,&maximum,exception);
           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
             GetMagickPrecision(),minimum);
@@ -2670,23 +2809,21 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     {
       if (LocaleCompare("opaque",property) == 0)
         {
-          MagickBooleanType
-            opaque;
-
-          opaque=IsImageOpaque(image,exception);
-          (void) CopyMagickString(value,IfMagickTrue(opaque) ? "true" : "false",
-            MaxTextExtent);
+          WarnNoImageReturn("\"%%[%s]\"",property);
+          string=CommandOptionToMnemonic(MagickBooleanOptions,
+               (ssize_t) IsImageOpaque(image,exception));
           break;
         }
       if (LocaleCompare("orientation",property) == 0)
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           string=CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t)
             image->orientation);
           break;
         }
-      if ((image_info != (ImageInfo *) NULL) &&
-          (LocaleCompare("output",property) == 0))
+      if (LocaleCompare("output",property) == 0)
         {
+          WarnNoImageInfoReturn("\"%%[%s]\"",property);
           (void) CopyMagickString(value,image_info->filename,MaxTextExtent);
           break;
         }
@@ -2694,26 +2831,72 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     }
     case 'p':
     {
-      if (LocaleCompare("page",property) == 0)
+#if defined(MAGICKCORE_LCMS_DELEGATE)
+      if (LocaleCompare("profile:icc",property) == 0 ||
+          LocaleCompare("profile:icm",property) == 0)
         {
-          (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
-            GetImageIndexInList(image)+1);
+#if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000)
+#define cmsUInt32Number  DWORD
+#endif
+
+          const StringInfo
+            *profile;
+
+          cmsHPROFILE
+            icc_profile;
+
+          profile=GetImageProfile(image,property+8);
+          if (profile == (StringInfo *) NULL)
+            break;
+
+          icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
+            (cmsUInt32Number) GetStringInfoLength(profile));
+          if (icc_profile != (cmsHPROFILE *) NULL)
+            {
+#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000)
+              string=cmsTakeProductName(icc_profile);
+#else
+              (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,
+                "en","US",value,MaxTextExtent);
+#endif
+              (void) cmsCloseProfile(icc_profile);
+            }
+      }
+#endif
+      if (LocaleCompare("profiles",property) == 0)
+        {
+          const char
+            *name;
+
+          ResetImageProfileIterator(image);
+          name=GetNextImageProfile(image);
+          if (name != (char *) NULL)
+            {
+              (void) CopyMagickString(value,name,MaxTextExtent);
+              name=GetNextImageProfile(image);
+              while (name != (char *) NULL)
+              {
+                ConcatenateMagickString(value,",",MaxTextExtent);
+                ConcatenateMagickString(value,name,MaxTextExtent);
+                name=GetNextImageProfile(image);
+              }
+            }
           break;
         }
       break;
     }
     case 'r':
     {
-      /* This matches %[fx:resolution.x] */
       if (LocaleCompare("resolution.x",property) == 0)
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) FormatLocaleString(value,MaxTextExtent,"%g",
             image->resolution.x);
           break;
         }
-      /* This matches %[fx:resolution.y] */
       if (LocaleCompare("resolution.y",property) == 0)
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) FormatLocaleString(value,MaxTextExtent,"%g",
             image->resolution.y);
           break;
@@ -2724,17 +2907,21 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     {
       if (LocaleCompare("scene",property) == 0)
         {
-          if ((image_info != (ImageInfo *) NULL) &&
-              (image_info->number_scenes != 0))
+          WarnNoImageInfoReturn("\"%%[%s]\"",property);
+          if (image_info->number_scenes != 0)
             (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
               image_info->scene);
-          else
+          else {
+            WarnNoImageReturn("\"%%[%s]\"",property);
             (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
               image->scene);
+          }
           break;
         }
       if (LocaleCompare("scenes",property) == 0)
         {
+          /* FUTURE: equivelent to %n? */
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
             GetImageListLength(image));
           break;
@@ -2744,6 +2931,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
           char
             format[MaxTextExtent];
 
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format);
           (void) FormatLocaleString(value,MaxTextExtent,"%sB",format);
           break;
@@ -2754,17 +2942,20 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
             kurtosis,
             skewness;
 
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
             GetMagickPrecision(),skewness);
           break;
         }
-      if (LocaleCompare("standard-deviation",property) == 0)
+      if ((LocaleCompare("standard-deviation",property) == 0) ||
+          (LocaleCompare("standard_deviation",property) == 0))
         {
           double
             mean,
             standard_deviation;
 
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) GetImageMean(image,&mean,&standard_deviation,exception);
           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
             GetMagickPrecision(),standard_deviation);
@@ -2776,6 +2967,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     {
       if (LocaleCompare("type",property) == 0)
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           string=CommandOptionToMnemonic(MagickTypeOptions,(ssize_t)
             GetImageType(image,exception));
           break;
@@ -2784,12 +2976,20 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     }
     case 'u':
     {
-      if ((image_info != (ImageInfo *) NULL) &&
-          (LocaleCompare("unique",property) == 0))
+      if (LocaleCompare("unique",property) == 0)
         {
+          WarnNoImageInfoReturn("\"%%[%s]\"",property);
           string=image_info->unique;
           break;
         }
+      if (LocaleCompare("units",property) == 0)
+        {
+          WarnNoImageReturn("\"%%[%s]\"",property);
+          string=CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
+            image->units);
+          break;
+        }
+      if (LocaleCompare("copyright",property) == 0)
       break;
     }
     case 'v':
@@ -2805,6 +3005,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     {
       if (LocaleCompare("width",property) == 0)
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
             (image->magick_columns != 0 ? image->magick_columns : 256));
           break;
@@ -2816,6 +3017,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
       if ((LocaleCompare("xresolution",property) == 0) ||
           (LocaleCompare("x-resolution",property) == 0) )
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
             image->resolution.x);
           break;
@@ -2827,6 +3029,7 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
       if ((LocaleCompare("yresolution",property) == 0) ||
           (LocaleCompare("y-resolution",property) == 0) )
         {
+          WarnNoImageReturn("\"%%[%s]\"",property);
           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
             image->resolution.y);
           break;
@@ -2835,24 +3038,34 @@ MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
     }
     case 'z':
     {
-      if ((image_info != (ImageInfo *) NULL) &&
-          (LocaleCompare("zero",property) == 0))
+      if (LocaleCompare("zero",property) == 0)
         {
+          WarnNoImageInfoReturn("\"%%[%s]\"",property);
           string=image_info->zero;
           break;
         }
       break;
     }
   }
+  if (string != (char *) NULL)
+    return(string);
   if (*value != '\0')
-    string=value;
-  if (string != (char *)NULL)
-    {
-      (void) SetImageArtifact(image,"get-property", string);
-      return(GetImageArtifact(image,"get-property"));
-    }
+  {
+    /* create a cloned copy of result, that will get cleaned up, eventually */
+    if (image != (Image *)NULL)
+      {
+        (void) SetImageArtifact(image,"get-property",value);
+        return(GetImageArtifact(image,"get-property"));
+      }
+    else
+      {
+        (void) SetImageOption(image_info,"get-property",value);
+        return(GetImageOption(image_info,"get-property"));
+      }
+  }
   return((char *)NULL);
 }
+#undef WarnNoImageReturn
 \f
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2921,20 +3134,20 @@ MagickExport char *GetNextImageProperty(const Image *image)
 %
 %  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 associacted set of properties.
 %
 %  The returned string must be freed using DestoryString() by the caller.
 %
 %  The format of the InterpretImageProperties method is:
 %
-%      char *InterpretImageProperties(const ImageInfo *image_info,
+%      char *InterpretImageProperties(ImageInfo *image_info,
 %        Image *image,const char *embed_text,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
-%    o image_info: the image info.
+%    o image_info: the image info. (required)
 %
-%    o image: the image.
+%    o image: the image. (optional)
 %
 %    o embed_text: the address of a character string containing the embedded
 %      formatting characters.
@@ -2942,31 +3155,81 @@ MagickExport char *GetNextImageProperty(const Image *image)
 %    o exception: return any errors or warnings in this structure.
 %
 */
-MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
+
+/* common inline code to expand the interpreted text string */
+#define ExtendInterpretText(string_length)  do { \
+DisableMSCWarning(4127) \
+    size_t length=(string_length); \
+    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); \
+   } } while (0)  /* no trailing ; */ \
+RestoreMSCWarning
+
+/* same but append the given string */
+#define AppendString2Text(string)  do { \
+DisableMSCWarning(4127) \
+    size_t length=strlen((string)); \
+    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,(string),extent); \
+     q+=length; \
+   } while (0)  /* no trailing ; */ \
+RestoreMSCWarning
+
+/* same but append a 'key' and 'string' pair */
+#define AppendKeyValue2Text(key,string)  do { \
+DisableMSCWarning(4127) \
+    size_t length=strlen(key)+strlen(string)+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) \
+        return((char *)NULL); \
+      q=interpret_text+strlen(interpret_text); \
+     } \
+     q+=FormatLocaleString(q,extent,"%s=%s\n",(key),(string)); \
+   } while (0)  /* no trailing ; */ \
+RestoreMSCWarning
+
+MagickExport char *InterpretImageProperties(ImageInfo *image_info,
   Image *image,const char *embed_text,ExceptionInfo *exception)
 {
   char
     *interpret_text;
 
   register char
-    *q;
+    *q;     /* current position in interpret_text */
 
   register const char
-    *p;
+    *p;     /* position in embed_text string being expanded */
 
   size_t
-    extent,
-    length;
+    extent; /* allocated length of interpret_text */
 
   MagickBooleanType
     number;
 
-  assert(image != (Image *) NULL);
-  assert(image->signature == MagickSignature);
-  if( IfMagickTrue(image->debug) )
+  assert(image == NULL || image->signature == MagickSignature);
+  assert(image_info == NULL || image_info->signature == MagickSignature);
+
+  if (image != (Image *) NULL && IfMagickTrue(image->debug))
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+  else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug))
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-image");
 
-  if ((embed_text == (const char *) NULL))
+  if (embed_text == (const char *) NULL)
     return((char *) NULL);
   p=embed_text;
 
@@ -2981,27 +3244,19 @@ MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
            OptionError,"UnableToAccessPath","%s",p);
        return((char *) NULL);
      }
-     return(FileToString(p,~0,exception));
+     return(FileToString(p,~0UL,exception));
   }
 
   /*
     Translate any embedded format characters.
   */
   interpret_text=AcquireString(embed_text); /* new string with extra space */
-  extent=MaxTextExtent;                     /* how many extra space */
+  extent=MaxTextExtent;                     /* allocated space in string */
   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)
-      {
-        extent+=MaxTextExtent;
-        interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
-          MaxTextExtent+1,sizeof(*interpret_text));
-        if (interpret_text == (char *) NULL)
-          return((char *) NULL);
-        q=interpret_text+strlen(interpret_text);
-      }
+    ExtendInterpretText(MaxTextExtent);
     /*
       Look for the various escapes, (and handle other specials)
     */
@@ -3065,7 +3320,7 @@ MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
     */
     if ( *p != '[' ) {
       const char
-        *value;
+        *string;
 
       /* But only if not preceeded by a number! */
       if ( IfMagickTrue(number) ) {
@@ -3073,24 +3328,18 @@ MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
         p--;      /* back up one */
         continue;
       }
-      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));
-            if (interpret_text == (char *) NULL)
-              return((char *)NULL);
-            q=interpret_text+strlen(interpret_text);
-          }
-        (void) CopyMagickString(q,value,extent);
-        q+=length;
-        continue;
-      }
-      (void) ThrowMagickException(exception,GetMagickModule(),
-          OptionWarning,"UnknownImageProperty","\"%%%c\"",*p);
+      string=GetMagickPropertyLetter(image_info,image,*p, exception);
+      if (string != (char *) NULL)
+        {
+          AppendString2Text(string);
+          if (image != (Image *) NULL)
+            (void)DeleteImageArtifact(image,"get-property");
+          if (image_info != (ImageInfo *) NULL)
+            (void)DeleteImageOption(image_info,"get-property");
+          continue;
+        }
+      (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
+        "UnknownImageProperty","\"%%%c\"",*p);
       continue;
     }
 
@@ -3103,7 +3352,7 @@ MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
 
       const char
         *key,
-        *value;
+        *string;
 
       register ssize_t
         len;
@@ -3111,7 +3360,7 @@ MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
       ssize_t
         depth;
 
-      /* get the string framed by the %[...] */
+      /* get the property name framed by the %[...] */
       p++;  /* advance p to just inside the opening brace */
       depth=1;
       if ( *p == ']' ) {
@@ -3151,124 +3400,245 @@ MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
       }
 
       /*
-        Special Property Prefixes
-        such as: %[exif:...] %[fx:...] %[pixel:...]
-        Otherwise a free-form property string
+        Special Lookup Prefixes %[prefix:...]
       */
-      value=GetImageProperty(image,pattern,exception);
-      if (value != (const char *) NULL)
+      /* fx - value calculator */
+      if (LocaleNCompare("fx:",pattern,3) == 0)
         {
-          length=strlen(value);
-          if ((size_t) (q-interpret_text+length+1) >= extent)
+          FxInfo
+            *fx_info;
+
+          double
+            value;
+
+          MagickBooleanType
+            status;
+
+          if (image == (Image *) NULL ) {
+            (void) ThrowMagickException(exception,GetMagickModule(),
+                OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
+            continue; /* else no image to retrieve artifact */
+          }
+          fx_info=AcquireFxInfo(image,pattern+3,exception);
+          status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0,
+            &value,exception);
+          fx_info=DestroyFxInfo(fx_info);
+          if( IfMagickTrue(status) )
             {
-              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);
+              char
+                result[MaxTextExtent];
+
+              (void) FormatLocaleString(result,MaxTextExtent,"%.*g",
+                GetMagickPrecision(),(double) value);
+              AppendString2Text(result);
             }
-          (void) CopyMagickString(q,value,extent);
-          q+=length;
           continue;
         }
+      /* pixel - color value calculator */
+      if (LocaleNCompare("pixel:",pattern,6) == 0)
+        {
+          FxInfo
+            *fx_info;
+
+          double
+            value;
+
+          MagickStatusType
+            status;
+
+          PixelInfo
+            pixel;
+
+          if (image == (Image *) NULL ) {
+            (void) ThrowMagickException(exception,GetMagickModule(),
+                OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
+            continue; /* else no image to retrieve artifact */
+          }
+          GetPixelInfo(image,&pixel);
+          fx_info=AcquireFxInfo(image,pattern+6,exception);
+          status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
+            &value,exception);
+          pixel.red=(double) QuantumRange*value;
+          status&=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0,
+            &value,exception);
+          pixel.green=(double) QuantumRange*value;
+          status&=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0,
+            &value,exception);
+          pixel.blue=(double) QuantumRange*value;
+          if (image->colorspace == CMYKColorspace)
+            {
+              status&=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0,
+                &value,exception);
+              pixel.black=(double) QuantumRange*value;
+            }
+          status&=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0,
+            &value,exception);
+          pixel.alpha=(double) QuantumRange*value;
+          fx_info=DestroyFxInfo(fx_info);
+          if( IfMagickTrue(status) )
+            {
+              char
+                name[MaxTextExtent];
+
+              (void) QueryColorname(image,&pixel,SVGCompliance,name,
+                exception);
+              AppendString2Text(name);
+            }
+          continue;
+        }
+      /* option - direct global option lookup (with globbing) */
+      if (LocaleNCompare("option:",pattern,7) == 0)
+      {
+        if (image_info == (ImageInfo *) NULL ) {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+              OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
+          continue; /* else no image to retrieve artifact */
+        }
+        if( IfMagickTrue(IsGlob(pattern+7)) )
+        {
+          ResetImageOptionIterator(image_info);
+          while ((key=GetNextImageOption(image_info)) != (const char *) NULL)
+            if( IfMagickTrue(GlobExpression(key,pattern+7,MagickTrue)) )
+              {
+                string=GetImageOption(image_info,key);
+                if (string != (const char *) NULL)
+                  AppendKeyValue2Text(key,string);
+                /* else - assertion failure? key found but no string value! */
+              }
+          continue;
+        }
+        string=GetImageOption(image_info,pattern+7);
+        if (string == (char *) NULL)
+          goto PropertyLookupFailure; /* no artifact of this specifc name */
+        AppendString2Text(string);
+        continue;
+      }
+      /* artifact - direct image artifact lookup (with glob) */
+      if (LocaleNCompare("artifact:",pattern,9) == 0)
+      {
+        if (image == (Image *) NULL ) {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+              OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
+          continue; /* else no image to retrieve artifact */
+        }
+        if( IfMagickTrue(IsGlob(pattern+9)) )
+        {
+          ResetImageArtifactIterator(image);
+          while ((key=GetNextImageArtifact(image)) != (const char *) NULL)
+            if( IfMagickTrue(GlobExpression(key,pattern+9,MagickTrue)) )
+              {
+                string=GetImageArtifact(image,key);
+                if (string != (const char *) NULL)
+                  AppendKeyValue2Text(key,string);
+                /* else - assertion failure? key found but no string value! */
+              }
+          continue;
+        }
+        string=GetImageArtifact(image,pattern+9);
+        if (string == (char *) NULL)
+          goto PropertyLookupFailure; /* no artifact of this specifc name */
+        AppendString2Text(string);
+        continue;
+      }
+      /* property - direct image property lookup (with glob) */
+      if (LocaleNCompare("property:",pattern,9) == 0)
+      {
+        if (image == (Image *) NULL ) {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+              OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern);
+          continue; /* else no image to retrieve artifact */
+        }
+        if( IfMagickTrue(IsGlob(pattern+9)) )
+        {
+          ResetImagePropertyIterator(image);
+          while ((key=GetNextImageProperty(image)) != (const char *) NULL)
+            if( IfMagickTrue(GlobExpression(key,pattern,MagickTrue)) )
+              {
+                string=GetImageProperty(image,key,exception);
+                if (string != (const char *) NULL)
+                  AppendKeyValue2Text(key,string);
+                /* else - assertion failure? */
+              }
+          continue;
+        }
+        string=GetImageProperty(image,pattern+9,exception);
+        if (string == (char *) NULL)
+          goto PropertyLookupFailure; /* no artifact of this specifc name */
+        AppendString2Text(string);
+        continue;
+      }
+      /* Properties without special prefix.
+         This handles attributes, properties, and profiles such as %[exif:...]
+         Note the profile properties may also include a glob expansion pattern.
+      */
+      if ( image != (Image *)NULL )
+        {
+          string=GetImageProperty(image,pattern,exception);
+          if (string != (const char *) NULL)
+            {
+              AppendString2Text(string);
+              if (image != (Image *) NULL)
+                (void)DeleteImageArtifact(image,"get-property");
+              if (image_info != (ImageInfo *) NULL)
+                (void)DeleteImageOption(image_info,"get-property");
+              continue;
+            }
+        }
       /*
         Handle property 'glob' patterns
         Such as:  %[*]   %[user:array_??]  %[filename:e*]
       */
       if( IfMagickTrue(IsGlob(pattern)) )
         {
+          if (image == (Image *) NULL)
+            continue; /* else no image to retrieve proprty - no list */
           ResetImagePropertyIterator(image);
-          key=GetNextImageProperty(image);
-          while (key != (const char *) NULL)
-          {
+          while ((key=GetNextImageProperty(image)) != (const char *) NULL)
             if( IfMagickTrue(GlobExpression(key,pattern,MagickTrue)) )
               {
-                value=GetImageProperty(image,key,exception);
-                if (value != (const char *) NULL)
-                  {
-                    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)
-                          return((char *)NULL);
-                        q=interpret_text+strlen(interpret_text);
-                      }
-                    q+=FormatLocaleString(q,extent,"%s=%s\n",key,value);
-                  }
+                string=GetImageProperty(image,key,exception);
+                if (string != (const char *) NULL)
+                  AppendKeyValue2Text(key,string);
+                /* else - assertion failure? */
               }
-            key=GetNextImageProperty(image);
-          }
           continue;
         }
       /*
         Look for a known property or image attribute
         Such as  %[basename]  %[denisty]  %[delay]
-        Also does wrapped single letters:  %[b] %[G] %[g]
+        Also handles a braced single letter:  %[b] %[G] %[g]
       */
-      value=GetMagickProperty(image_info,image,pattern,exception);
-      if (value != (const char *) NULL)
+      string=GetMagickProperty(image_info,image,pattern,exception);
+      if (string != (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;
+          AppendString2Text(string);
           continue;
         }
       /*
-        Look for a per-image Artifact (user option, post-interpreted)
+        Look for a per-image Artifact
+        This includes option lookup (FUTURE: interpreted according to image)
       */
-      value=GetImageArtifact(image,pattern);
-      if (value != (char *) NULL)
+      if (image != (Image *)NULL)
         {
-          length=strlen(value);
-          if ((size_t) (q-interpret_text+length+1) >= extent)
+          string=GetImageArtifact(image,pattern);
+          if (string != (char *) NULL)
             {
-              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);
+              AppendString2Text(string);
+              continue;
             }
-          (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)
+      else
+        /* no image, so direct 'option' lookup (no delayed percent escapes) */
+        if (image_info != (ImageInfo *) NULL)
           {
-            length=strlen(value);
-            if ((size_t) (q-interpret_text+length+1) >= extent)
+            string=GetImageOption(image_info,pattern);
+            if (string != (char *) NULL)
               {
-                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);
+                AppendString2Text(string);
+                continue;
               }
-            (void) CopyMagickString(q,value,extent);
-            q+=length;
-            continue;
           }
-        }
+PropertyLookupFailure:
       /*
         Failed to find any match anywhere!
       */
@@ -3416,56 +3786,61 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
   assert(image->signature == MagickSignature);
   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);
-
-  /* Delete property if NULL --  empty string values are valid! */
-  if ((value == (const char *) NULL))
-    return(DeleteImageProperty(image,property));
+      RelinquishMagickMemory,RelinquishMagickMemory);  /* create splay-tree */
+  if (value == (const char *) NULL)
+    return(DeleteImageProperty(image,property));  /* delete if NULL */
   status=MagickTrue;
-
-  /* Do not 'set' single letter properties - read only shorthand */
   if (strlen(property) <= 1)
     {
-      (void) ThrowMagickException(exception,GetMagickModule(),
-          OptionError,"SetReadOnlyProperty","'%s'",property);
+      /*
+        Do not 'set' single letter properties - read only shorthand.
+       */
+      (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!)
+  /* FUTURE: binary chars or quotes in key should produce a error */
+  /* Set attributes with known names or special prefixes
+     return result is found, or break to set a free form properity
   */
-
   switch (*property)
   {
+#if 0  /* Percent escape's sets values with this prefix: for later use
+          Throwing an exception causes this setting to fail */
+    case '8':
+    {
+      if (LocaleNCompare("8bim:",property,5) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
+        }
+      break;
+    }
+#endif
     case 'B':
     case 'b':
     {
       if (LocaleCompare("background",property) == 0)
         {
           (void) QueryColorCompliance(value,AllCompliance,
-            &image->background_color,exception);
-          break;
+               &image->background_color,exception);
+          /* check for FUTURE: value exception?? */
+          /* also add user input to splay tree */
         }
-      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
-        ConstantString(property),ConstantString(value));
-      /* FUTURE: error if status is bad? */
-      break;
+      break; /* not an attribute, add as a property */
     }
     case 'C':
     case 'c':
     {
       if (LocaleCompare("channels",property) == 0)
         {
-          (void) ThrowMagickException(exception,GetMagickModule(),
-               OptionError,"SetReadOnlyProperty","'%s'",property);
-          status=MagickFalse;
-          break;
+          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+            "SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
         }
       if (LocaleCompare("colorspace",property) == 0)
         {
@@ -3475,29 +3850,8 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
             value);
           if (colorspace < 0)
-            break;
-          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;
+            return(MagickFalse); /* FUTURE: value exception?? */
+          return(SetImageColorspace(image,(ColorspaceType) colorspace,exception));
         }
       if (LocaleCompare("compose",property) == 0)
         {
@@ -3506,9 +3860,9 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
 
           compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
           if (compose < 0)
-            break;
+            return(MagickFalse); /* FUTURE: value exception?? */
           image->compose=(CompositeOperator) compose;
-          break;
+          return(MagickTrue);
         }
       if (LocaleCompare("compress",property) == 0)
         {
@@ -3518,20 +3872,17 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
             value);
           if (compression < 0)
-            break;
+            return(MagickFalse); /* FUTURE: value exception?? */
           image->compression=(CompressionType) compression;
-          break;
+          return(MagickTrue);
         }
       if (LocaleCompare("copyright",property) == 0)
         {
           (void) ThrowMagickException(exception,GetMagickModule(),
-               OptionError,"SetReadOnlyProperty","'%s'",property);
-          status=MagickFalse;
-          break;
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
         }
-      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
-        ConstantString(property),ConstantString(value));
-      break;
+      break; /* not an attribute, add as a property */
     }
     case 'D':
     case 'd':
@@ -3551,14 +3902,20 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
             if ((flags & LessValue) != 0)
               {
                 if (image->delay < (size_t) floor(geometry_info.rho+0.5))
-                  image->ticks_per_second=(ssize_t)
+                  image->delay=(ssize_t)
                     floor(geometry_info.sigma+0.5);
               }
             else
               image->delay=(size_t) floor(geometry_info.rho+0.5);
           if ((flags & SigmaValue) != 0)
             image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
-          break;
+          return(MagickTrue);
+        }
+      if (LocaleCompare("delay_units",property) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
         }
       if (LocaleCompare("density",property) == 0)
         {
@@ -3570,11 +3927,12 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           image->resolution.y=geometry_info.sigma;
           if ((flags & SigmaValue) == 0)
             image->resolution.y=image->resolution.x;
+          return(MagickTrue);
         }
       if (LocaleCompare("depth",property) == 0)
         {
           image->depth=StringToUnsignedLong(value);
-          break;
+          return(MagickTrue);
         }
       if (LocaleCompare("dispose",property) == 0)
         {
@@ -3583,21 +3941,44 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
 
           dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
           if (dispose < 0)
-            break;
+            return(MagickFalse); /* FUTURE: value exception?? */
           image->dispose=(DisposeType) dispose;
-          break;
+          return(MagickTrue);
         }
-      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
-        ConstantString(property),ConstantString(value));
-      break;
+      break; /* not an attribute, add as a property */
     }
+#if 0  /* Percent escape's sets values with this prefix: for later use
+          Throwing an exception causes this setting to fail */
+    case 'E':
+    case 'e':
+    {
+      if (LocaleNCompare("exif:",property,5) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
+        }
+      break; /* not an attribute, add as a property */
+    }
+    case 'F':
+    case 'f':
+    {
+      if (LocaleNCompare("fx:",property,3) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
+        }
+      break; /* not an attribute, add as a property */
+    }
+#endif
     case 'G':
     case 'g':
     {
       if (LocaleCompare("gamma",property) == 0)
         {
           image->gamma=StringToDouble(value,(char **) NULL);
-          break;
+          return(MagickTrue);
         }
       if (LocaleCompare("gravity",property) == 0)
         {
@@ -3606,26 +3987,38 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
 
           gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
           if (gravity < 0)
-            break;
+            return(MagickFalse); /* FUTURE: value exception?? */
           image->gravity=(GravityType) gravity;
-          break;
+          return(MagickTrue);
         }
-      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
-        ConstantString(property),ConstantString(value));
-      break;
+      break; /* not an attribute, add as a property */
     }
     case 'H':
     case 'h':
+    {
       if (LocaleCompare("height",property) == 0)
         {
           (void) ThrowMagickException(exception,GetMagickModule(),
-               OptionError,"SetReadOnlyProperty","'%s'",property);
-          status=MagickFalse;
-          break;
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
         }
+      break; /* not an attribute, add as a property */
+    }
     case 'I':
     case 'i':
     {
+      if (LocaleCompare("intensity",property) == 0)
+        {
+          ssize_t
+            intensity;
+
+          intensity=ParseCommandOption(MagickIntentOptions,MagickFalse,
+            value);
+          if (intensity < 0)
+            return(MagickFalse);
+          image->intensity=(PixelIntensityMethod) intensity;
+          return(MagickTrue);
+        }
       if (LocaleCompare("intent",property) == 0)
         {
           ssize_t
@@ -3634,9 +4027,9 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
             value);
           if (rendering_intent < 0)
-            break;
+            return(MagickFalse); /* FUTURE: value exception?? */
           image->rendering_intent=(RenderingIntent) rendering_intent;
-          break;
+          return(MagickTrue);
         }
       if (LocaleCompare("interpolate",property) == 0)
         {
@@ -3646,34 +4039,39 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
             value);
           if (interpolate < 0)
-            break;
+            return(MagickFalse); /* FUTURE: value exception?? */
           image->interpolate=(PixelInterpolateMethod) interpolate;
-          break;
+          return(MagickTrue);
         }
-      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
-        ConstantString(property),ConstantString(value));
-      break;
+#if 0  /* Percent escape's sets values with this prefix: for later use
+          Throwing an exception causes this setting to fail */
+      if (LocaleNCompare("iptc:",property,5) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
+        }
+#endif
+      break; /* not an attribute, add as a property */
     }
     case 'K':
     case 'k':
       if (LocaleCompare("kurtosis",property) == 0)
         {
           (void) ThrowMagickException(exception,GetMagickModule(),
-               OptionError,"SetReadOnlyProperty","'%s'",property);
-          status=MagickFalse;
-          break;
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
         }
+      break; /* not an attribute, add as a property */
     case 'L':
     case 'l':
     {
       if (LocaleCompare("loop",property) == 0)
         {
           image->iterations=StringToUnsignedLong(value);
-          break;
+          return(MagickTrue);
         }
-      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
-        ConstantString(property),ConstantString(value));
-      break;
+      break; /* not an attribute, add as a property */
     }
     case 'M':
     case 'm':
@@ -3684,19 +4082,19 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
            (LocaleCompare("min",property) == 0) )
         {
           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-             "SetReadOnlyProperty","'%s'",property);
-          status=MagickFalse;
-          break;
+             "SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
         }
+      break; /* not an attribute, add as a property */
     case 'O':
     case 'o':
       if (LocaleCompare("opaque",property) == 0)
         {
           (void) ThrowMagickException(exception,GetMagickModule(),
-               OptionError,"SetReadOnlyProperty","'%s'",property);
-          status=MagickFalse;
-          break;
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
         }
+      break; /* not an attribute, add as a property */
     case 'P':
     case 'p':
     {
@@ -3708,8 +4106,17 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           geometry=GetPageGeometry(value);
           flags=ParseAbsoluteGeometry(geometry,&image->page);
           geometry=DestroyString(geometry);
-          break;
+          return(MagickTrue);
+        }
+#if 0  /* Percent escape's sets values with this prefix: for later use
+          Throwing an exception causes this setting to fail */
+      if (LocaleNCompare("pixel:",property,6) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
         }
+#endif
       if (LocaleCompare("profile",property) == 0)
         {
           ImageInfo
@@ -3725,11 +4132,9 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           if (profile != (StringInfo *) NULL)
             status=SetImageProfile(image,image_info->magick,profile,exception);
           image_info=DestroyImageInfo(image_info);
-          break;
+          return(MagickTrue);
         }
-      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
-        ConstantString(property),ConstantString(value));
-      break;
+      break; /* not an attribute, add as a property */
     }
     case 'R':
     case 'r':
@@ -3742,14 +4147,11 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
             value);
           if (rendering_intent < 0)
-            status=MagickFalse;
-          else
-            image->rendering_intent=(RenderingIntent) rendering_intent;
-          break;
+            return(MagickFalse); /* FUTURE: value exception?? */
+          image->rendering_intent=(RenderingIntent) rendering_intent;
+          return(MagickTrue);
         }
-      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
-        ConstantString(property),ConstantString(value));
-      break;
+      break; /* not an attribute, add as a property */
     }
     case 'S':
     case 's':
@@ -3759,10 +4161,10 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
            (LocaleCompare("standard-deviation",property) == 0) )
         {
           (void) ThrowMagickException(exception,GetMagickModule(),
-               OptionError,"SetReadOnlyProperty","'%s'",property);
-          status=MagickFalse;
-          break;
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
         }
+      break; /* not an attribute, add as a property */
     case 'T':
     case 't':
     {
@@ -3774,11 +4176,9 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
           geometry=GetPageGeometry(value);
           flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
           geometry=DestroyString(geometry);
-          break;
+          return(MagickTrue);
         }
-      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
-        ConstantString(property),ConstantString(value));
-      break;
+      break; /* not an attribute, add as a property */
     }
     case 'U':
     case 'u':
@@ -3790,38 +4190,52 @@ MagickExport MagickBooleanType SetImageProperty(Image *image,
 
           units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
           if (units < 0)
-            break;
+            return(MagickFalse); /* FUTURE: value exception?? */
           image->units=(ResolutionType) units;
-          break;
+          return(MagickTrue);
         }
-      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
-        ConstantString(property),ConstantString(value));
-      break;
+      break; /* not an attribute, add as a property */
     }
     case 'V':
     case 'v':
+    {
       if (LocaleCompare("version",property) == 0)
         {
           (void) ThrowMagickException(exception,GetMagickModule(),
-               OptionError,"SetReadOnlyProperty","'%s'",property);
-          status=MagickFalse;
-          break;
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
         }
+      break; /* not an attribute, add as a property */
+    }
     case 'W':
     case 'w':
+    {
       if (LocaleCompare("width",property) == 0)
         {
           (void) ThrowMagickException(exception,GetMagickModule(),
-               OptionError,"SetReadOnlyProperty","'%s'",property);
-          status=MagickFalse;
-          break;
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
         }
-    default:
+      break; /* not an attribute, add as a property */
+    }
+#if 0  /* Percent escape's sets values with this prefix: for later use
+          Throwing an exception causes this setting to fail */
+    case 'X':
+    case 'x':
     {
-      status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
-        ConstantString(property),ConstantString(value));
-      break;
+      if (LocaleNCompare("xmp:",property,4) == 0)
+        {
+          (void) ThrowMagickException(exception,GetMagickModule(),
+               OptionError,"SetReadOnlyProperty","`%s'",property);
+          return(MagickFalse);
+        }
+      break; /* not an attribute, add as a property */
     }
+#endif
   }
+  /* Default: not an attribute, add as a property */
+  status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
+    ConstantString(property),ConstantString(value));
+  /* FUTURE: error if status is bad? */
   return(status);
 }