static const char *GetMagickPropertyLetter(ImageInfo *image_info,
  Image *image,const char letter,ExceptionInfo *exception)
{
#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); \
  }

  char
    value[MagickPathExtent];  /* formatted string to store as an artifact */

  const char
    *string;     /* return a string already stored somewher */

  if ((image != (Image *) NULL) && (image->debug != MagickFalse))
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  else
    if ((image_info != (ImageInfo *) NULL) &&
        (image_info->debug != MagickFalse))
    (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) FormatMagickSize(image->extent,MagickFalse,"B",MagickPathExtent,
        value);
      if (image->extent == 0)
        (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B",
          MagickPathExtent,value);
      break;
    }
    case 'c':  /* image comment property - empty string by default */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      string=GetImageProperty(image,"comment",exception);
      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,MagickPathExtent,
        "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
        image->page.height,(double) image->page.x,(double) image->page.y);
      break;
    }
    case 'h': /* Image height (current) */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
        (image->rows != 0 ? image->rows : image->magick_rows));
      break;
    }
    case 'i': /* Filename last used for an image (read or write) */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      string=image->filename;
      break;
    }
    case 'k': /* Number of unique colors  */
    {
      /*
        FUTURE: ensure this does not generate the formatted comment!
      */
      WarnNoImageReturn("\"%%%c\"",letter);
      (void) FormatLocaleString(value,MagickPathExtent,"%.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)
        string="";
      break;
    }
    case 'm': /* Image format (file magick) */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      string=image->magick;
      break;
    }
    case 'n': /* Number of images in the list.  */
    {
      if ( image != (Image *) NULL )
        (void) FormatLocaleString(value,MagickPathExtent,"%.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 */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
        GetImageIndexInList(image));
      break;
    }
    case 'q': /* Quantum depth of image in memory */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
        MAGICKCORE_QUANTUM_DEPTH);
      break;
    }
    case 'r': /* Image storage class, colorspace, and alpha enabled.  */
    {
      ColorspaceType
        colorspace;

      WarnNoImageReturn("\"%%%c\"",letter);
      colorspace=image->colorspace;
      if ((image->columns != 0) && (image->rows != 0) &&
          (SetImageGray(image,exception) != MagickFalse))
        colorspace=GRAYColorspace;   /* FUTURE: this is IMv6 not IMv7 */
      (void) FormatLocaleString(value,MagickPathExtent,"%s %s %s",
        CommandOptionToMnemonic(MagickClassOptions,(ssize_t)
        image->storage_class),CommandOptionToMnemonic(MagickColorspaceOptions,
        (ssize_t) colorspace),image->alpha_trait != UndefinedPixelTrait ?
        "Alpha" : "");
      break;
    }
    case 's': /* Image scene number */
    {
#if 0  /* this seems non-sensical -- simplifing */
      if (image_info->number_scenes != 0)
        (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
          image_info->scene);
      else if (image != (Image *) NULL)
        (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
          image->scene);
      else
          string="0";
#else
      WarnNoImageReturn("\"%%%c\"",letter);
      (void) FormatLocaleString(value,MagickPathExtent,"%.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,MagickPathExtent,"%.20g",(double)
        (image->columns != 0 ? image->columns : image->magick_columns));
      break;
    }
    case 'x': /* Image horizontal resolution (with units) */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
        fabs(image->resolution.x) > MagickEpsilon ? image->resolution.x : 72.0);
      break;
    }
    case 'y': /* Image vertical resolution (with units) */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      (void) FormatLocaleString(value,MagickPathExtent,"%.20g",
        fabs(image->resolution.y) > MagickEpsilon ? image->resolution.y : 72.0);
      break;
    }
    case 'z': /* Image depth as read in */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
        image->depth);
      break;
    }
    case 'A': /* Image alpha channel  */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      string=CommandOptionToMnemonic(MagickPixelTraitOptions,(ssize_t)
        image->alpha_trait);
      break;
    }
    case 'B':  /* image size read in - in bytes */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
        image->extent);
      if (image->extent == 0)
        (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
          GetBlobSize(image));
      break;
    }
    case 'C': /* Image compression method.  */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      string=CommandOptionToMnemonic(MagickCompressOptions,(ssize_t)
        image->compression);
      break;
    }
    case 'D': /* Image dispose method.  */
    {
      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,MagickPathExtent,"%.20gx%.20g",(double)
        image->magick_columns,(double) image->magick_rows);
      break;
    }
    case 'H': /* layer canvas height */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      (void) FormatLocaleString(value,MagickPathExtent,"%.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,MagickPathExtent,"%+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,MagickPathExtent,"%.20gx%.20g",(double)
        image->page.width,(double) image->page.height);
      break;
    }
    case 'Q': /* image compression quality */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
        (image->quality == 0 ? 92 : image->quality));
      break;
    }
    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 if ( image != (Image *) NULL )
        (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
                image_info->scene+image_info->number_scenes);
      else
        string="0";
#else
      (void) FormatLocaleString(value,MagickPathExtent,"%.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,MagickPathExtent,"%.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,MagickPathExtent,"%.20g",(double)
        image->page.width);
      break;
    }
    case 'X': /* layer canvas X offset */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double)
        image->page.x);
      break;
    }
    case 'Y': /* layer canvas Y offset */
    {
      WarnNoImageReturn("\"%%%c\"",letter);
      (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double)
        image->page.y);
      break;
    }
    case '%': /* percent escaped */
    {
      string="%";
      break;
    }
    case '@': /* Trim bounding box, without actually Trimming! */
    {
      RectangleInfo
        page;

      WarnNoImageReturn("\"%%%c\"",letter);
      page=GetImageBoundingBox(image,exception);
      (void) FormatLocaleString(value,MagickPathExtent,
        "%.20gx%.20g%+.20g%+.20g",(double) page.width,(double) page.height,
        (double) page.x,(double)page.y);
      break;
    }
    case '#':
    {
      /*
        Image signature.
      */
      WarnNoImageReturn("\"%%%c\"",letter);
      if ((image->columns != 0) && (image->rows != 0))
        (void) SignatureImage(image,exception);
      string=GetImageProperty(image,"signature",exception);
      break;
    }
  }
  if (string != (char *) NULL)
    return(string);
  if (*value != '\0')
    {
      /*
        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 MagickBooleanType SetImageProperty(Image *image,
  const char *property,const char *value,ExceptionInfo *exception)
{
  MagickBooleanType
    status;

  MagickStatusType
    flags;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  if (image->properties == (void *) NULL)
    image->properties=NewSplayTree(CompareSplayTreeString,
      RelinquishMagickMemory,RelinquishMagickMemory);  /* create splay-tree */
  if (value == (const char *) NULL)
    return(DeleteImageProperty(image,property));  /* delete if NULL */
  status=MagickTrue;
  if (strlen(property) <= 1)
    {
      /*
        Do not 'set' single letter properties - read only shorthand.
       */
      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
        "SetReadOnlyProperty","`%s'",property);
      return(MagickFalse);
    }

  /* 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);
          /* check for FUTURE: value exception?? */
          /* also add user input to splay tree */
        }
      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);
          return(MagickFalse);
        }
      if (LocaleCompare("colorspace",property) == 0)
        {
          ssize_t
            colorspace;

          colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
            value);
          if (colorspace < 0)
            return(MagickFalse); /* FUTURE: value exception?? */
          return(SetImageColorspace(image,(ColorspaceType) colorspace,exception));
        }
      if (LocaleCompare("compose",property) == 0)
        {
          ssize_t
            compose;

          compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
          if (compose < 0)
            return(MagickFalse); /* FUTURE: value exception?? */
          image->compose=(CompositeOperator) compose;
          return(MagickTrue);
        }
      if (LocaleCompare("compress",property) == 0)
        {
          ssize_t
            compression;

          compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
            value);
          if (compression < 0)
            return(MagickFalse); /* FUTURE: value exception?? */
          image->compression=(CompressionType) compression;
          return(MagickTrue);
        }
      break; /* not an attribute, add as a property */
    }
    case 'D':
    case 'd':
    {
      if (LocaleCompare("delay",property) == 0)
        {
          GeometryInfo
            geometry_info;

          flags=ParseGeometry(value,&geometry_info);
          if ((flags & GreaterValue) != 0)
            {
              if (image->delay > (size_t) floor(geometry_info.rho+0.5))
                image->delay=(size_t) floor(geometry_info.rho+0.5);
            }
          else
            if ((flags & LessValue) != 0)
              {
                if (image->delay < (size_t) floor(geometry_info.rho+0.5))
                  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);
          return(MagickTrue);
        }
      if (LocaleCompare("delay_units",property) == 0)
        {
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
            "SetReadOnlyProperty","`%s'",property);
          return(MagickFalse);
        }
      if (LocaleCompare("density",property) == 0)
        {
          GeometryInfo
            geometry_info;

          flags=ParseGeometry(value,&geometry_info);
          if ((flags & RhoValue) != 0)
            image->resolution.x=geometry_info.rho;
          image->resolution.y=image->resolution.x;
          if ((flags & SigmaValue) != 0)
            image->resolution.y=geometry_info.sigma;
          return(MagickTrue);
        }
      if (LocaleCompare("depth",property) == 0)
        {
          image->depth=StringToUnsignedLong(value);
          return(MagickTrue);
        }
      if (LocaleCompare("dispose",property) == 0)
        {
          ssize_t
            dispose;

          dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
          if (dispose < 0)
            return(MagickFalse); /* FUTURE: value exception?? */
          image->dispose=(DisposeType) dispose;
          return(MagickTrue);
        }
      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);
          return(MagickTrue);
        }
      if (LocaleCompare("gravity",property) == 0)
        {
          ssize_t
            gravity;

          gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
          if (gravity < 0)
            return(MagickFalse); /* FUTURE: value exception?? */
          image->gravity=(GravityType) gravity;
          return(MagickTrue);
        }
      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);
          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
            rendering_intent;

          rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
            value);
          if (rendering_intent < 0)
            return(MagickFalse); /* FUTURE: value exception?? */
          image->rendering_intent=(RenderingIntent) rendering_intent;
          return(MagickTrue);
        }
      if (LocaleCompare("interpolate",property) == 0)
        {
          ssize_t
            interpolate;

          interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
            value);
          if (interpolate < 0)
            return(MagickFalse); /* FUTURE: value exception?? */
          image->interpolate=(PixelInterpolateMethod) interpolate;
          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("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);
          return(MagickFalse);
        }
      break; /* not an attribute, add as a property */
    case 'L':
    case 'l':
    {
      if (LocaleCompare("loop",property) == 0)
        {
          image->iterations=StringToUnsignedLong(value);
          return(MagickTrue);
        }
      break; /* not an attribute, add as a property */
    }
    case 'M':
    case 'm':
      if ((LocaleCompare("magick",property) == 0) ||
          (LocaleCompare("max",property) == 0) ||
          (LocaleCompare("mean",property) == 0) ||
          (LocaleCompare("min",property) == 0) ||
          (LocaleCompare("min",property) == 0))
        {
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
             "SetReadOnlyProperty","`%s'",property);
          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);
          return(MagickFalse);
        }
      break; /* not an attribute, add as a property */
    case 'P':
    case 'p':
    {
      if (LocaleCompare("page",property) == 0)
        {
          char
            *geometry;

          geometry=GetPageGeometry(value);
          flags=ParseAbsoluteGeometry(geometry,&image->page);
          geometry=DestroyString(geometry);
          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
            *image_info;

          StringInfo
            *profile;

          image_info=AcquireImageInfo();
          (void) CopyMagickString(image_info->filename,value,MagickPathExtent);
          (void) SetImageInfo(image_info,1,exception);
          profile=FileToStringInfo(image_info->filename,~0UL,exception);
          if (profile != (StringInfo *) NULL)
            status=SetImageProfile(image,image_info->magick,profile,exception);
          image_info=DestroyImageInfo(image_info);
          return(MagickTrue);
        }
      break; /* not an attribute, add as a property */
    }
    case 'R':
    case 'r':
    {
      if (LocaleCompare("rendering-intent",property) == 0)
        {
          ssize_t
            rendering_intent;

          rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
            value);
          if (rendering_intent < 0)
            return(MagickFalse); /* FUTURE: value exception?? */
          image->rendering_intent=(RenderingIntent) rendering_intent;
          return(MagickTrue);
        }
      break; /* not an attribute, add as a property */
    }
    case 'S':
    case 's':
      if ((LocaleCompare("size",property) == 0) ||
          (LocaleCompare("skewness",property) == 0) ||
          (LocaleCompare("scenes",property) == 0) ||
          (LocaleCompare("standard-deviation",property) == 0))
        {
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
            "SetReadOnlyProperty","`%s'",property);
          return(MagickFalse);
        }
      break; /* not an attribute, add as a property */
    case 'T':
    case 't':
    {
      if (LocaleCompare("tile-offset",property) == 0)
        {
          char
            *geometry;

          geometry=GetPageGeometry(value);
          flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
          geometry=DestroyString(geometry);
          return(MagickTrue);
        }
      break; /* not an attribute, add as a property */
    }
    case 'U':
    case 'u':
    {
      if (LocaleCompare("units",property) == 0)
        {
          ssize_t
            units;

          units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
          if (units < 0)
            return(MagickFalse); /* FUTURE: value exception?? */
          image->units=(ResolutionType) units;
          return(MagickTrue);
        }
      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);
          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);
          return(MagickFalse);
        }
      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':
    {
      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);
}


static MagickBooleanType LoadDelegateCache(LinkedListInfo *cache,
  const char *xml,const char *filename,const size_t depth,
  ExceptionInfo *exception)
{
  char
    keyword[MagickPathExtent],
    *token;

  const char
    *q;

  DelegateInfo
    *delegate_info;

  MagickStatusType
    status;

  size_t
    extent;

  /*
    Load the delegate map file.
  */
  (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
    "Loading delegate configuration file \"%s\" ...",filename);
  if (xml == (const char *) NULL)
    return(MagickFalse);
  status=MagickTrue;
  delegate_info=(DelegateInfo *) NULL;
  token=AcquireString(xml);
  extent=strlen(token)+MagickPathExtent;
  for (q=(const char *) xml; *q != '\0'; )
  {
    /*
      Interpret XML.
    */
    GetNextToken(q,&q,extent,token);
    if (*token == '\0')
      break;
    (void) CopyMagickString(keyword,token,MagickPathExtent);
    if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
      {
        /*
          Doctype element.
        */
        while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
          GetNextToken(q,&q,extent,token);
        continue;
      }
    if (LocaleNCompare(keyword,"<!--",4) == 0)
      {
        /*
          Comment element.
        */
        while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
          GetNextToken(q,&q,extent,token);
        continue;
      }
    if (LocaleCompare(keyword,"<include") == 0)
      {
        /*
          Include element.
        */
        while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
        {
          (void) CopyMagickString(keyword,token,MagickPathExtent);
          GetNextToken(q,&q,extent,token);
          if (*token != '=')
            continue;
          GetNextToken(q,&q,extent,token);
          if (LocaleCompare(keyword,"file") == 0)
            {
              if (depth > MagickMaxRecursionDepth)
                (void) ThrowMagickException(exception,GetMagickModule(),
                  ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
              else
                {
                  char
                    path[MagickPathExtent],
                    *file_xml;

                  GetPathComponent(filename,HeadPath,path);
                  if (*path != '\0')
                    (void) ConcatenateMagickString(path,DirectorySeparator,
                      MagickPathExtent);
                  if (*token == *DirectorySeparator)
                    (void) CopyMagickString(path,token,MagickPathExtent);
                  else
                    (void) ConcatenateMagickString(path,token,MagickPathExtent);
                  file_xml=FileToXML(path,~0UL);
                  if (file_xml != (char *) NULL)
                    {
                      status&=LoadDelegateCache(cache,file_xml,path,
                        depth+1,exception);
                      file_xml=DestroyString(file_xml);
                    }
                }
            }
        }
        continue;
      }
    if (LocaleCompare(keyword,"<delegate") == 0)
      {
        /*
          Delegate element.
        */
        delegate_info=(DelegateInfo *) AcquireCriticalMemory(
          sizeof(*delegate_info));
        (void) memset(delegate_info,0,sizeof(*delegate_info));
        delegate_info->path=ConstantString(filename);
        delegate_info->thread_support=MagickTrue;
        delegate_info->signature=MagickCoreSignature;
        continue;
      }
    if (delegate_info == (DelegateInfo *) NULL)
      continue;
    if ((LocaleCompare(keyword,"/>") == 0) ||
        (LocaleCompare(keyword,"</policy>") == 0))
      {
        status=AppendValueToLinkedList(cache,delegate_info);
        if (status == MagickFalse)
          (void) ThrowMagickException(exception,GetMagickModule(),
            ResourceLimitError,"MemoryAllocationFailed","`%s'",
            delegate_info->commands);
        delegate_info=(DelegateInfo *) NULL;
        continue;
      }
    GetNextToken(q,(const char **) NULL,extent,token);
    if (*token != '=')
      continue;
    GetNextToken(q,&q,extent,token);
    GetNextToken(q,&q,extent,token);
    switch (*keyword)
    {
      case 'C':
      case 'c':
      {
        if (LocaleCompare((char *) keyword,"command") == 0)
          {
            char
              *commands;

            commands=AcquireString(token);
#if defined(MAGICKCORE_WINDOWS_SUPPORT)
            if (strchr(commands,'@') != (char *) NULL)
              {
                char
                  path[MagickPathExtent];

                NTGhostscriptEXE(path,MagickPathExtent);
                (void) SubstituteString((char **) &commands,"@PSDelegate@",
                  path);
                (void) SubstituteString((char **) &commands,"\\","/");
              }
            (void) SubstituteString((char **) &commands,""","\"");
#else
            (void) SubstituteString((char **) &commands,""","'");
#endif
            (void) SubstituteString((char **) &commands,"&","&");
            (void) SubstituteString((char **) &commands,">",">");
            (void) SubstituteString((char **) &commands,"<","<");
            delegate_info->commands=commands;
            break;
          }
        break;
      }
      case 'D':
      case 'd':
      {
        if (LocaleCompare((char *) keyword,"decode") == 0)
          {
            delegate_info->decode=ConstantString(token);
            delegate_info->mode=1;
            break;
          }
        break;
      }
      case 'E':
      case 'e':
      {
        if (LocaleCompare((char *) keyword,"encode") == 0)
          {
            delegate_info->encode=ConstantString(token);
            delegate_info->mode=(-1);
            break;
          }
        break;
      }
      case 'M':
      case 'm':
      {
        if (LocaleCompare((char *) keyword,"mode") == 0)
          {
            delegate_info->mode=1;
            if (LocaleCompare(token,"bi") == 0)
              delegate_info->mode=0;
            else
              if (LocaleCompare(token,"encode") == 0)
                delegate_info->mode=(-1);
            break;
          }
        break;
      }
      case 'S':
      case 's':
      {
        if (LocaleCompare((char *) keyword,"spawn") == 0)
          {
            delegate_info->spawn=IsStringTrue(token);
            break;
          }
        if (LocaleCompare((char *) keyword,"stealth") == 0)
          {
            delegate_info->stealth=IsStringTrue(token);
            break;
          }
        break;
      }
      case 'T':
      case 't':
      {
        if (LocaleCompare((char *) keyword,"thread-support") == 0)
          {
            delegate_info->thread_support=IsStringTrue(token);
            if (delegate_info->thread_support == MagickFalse)
              delegate_info->semaphore=AcquireSemaphoreInfo();
            break;
          }
        break;
      }
      default:
        break;
    }
  }
  token=(char *) RelinquishMagickMemory(token);
  return(status != 0 ? MagickTrue : MagickFalse);
}


static MagickBooleanType GetXMPProperty(const Image *image,const char *property)
{
  char
    *xmp_profile;

  const char
    *content;

  const StringInfo
    *profile;

  ExceptionInfo
    *exception;

  MagickBooleanType
    status;

  register const char
    *p;

  XMLTreeInfo
    *child,
    *description,
    *node,
    *rdf,
    *xmp;

  profile=GetImageProfile(image,"xmp");
  if (profile == (StringInfo *) NULL)
    return(MagickFalse);
  if (GetStringInfoLength(profile) < 17)
    return(MagickFalse);
  if ((property == (const char *) NULL) || (*property == '\0'))
    return(MagickFalse);
  xmp_profile=StringInfoToString(profile);
  if (xmp_profile == (char *) NULL)
    return(MagickFalse);
  for (p=xmp_profile; *p != '\0'; p++)
    if ((*p == '<') && (*(p+1) == 'x'))
      break;
  exception=AcquireExceptionInfo();
  xmp=NewXMLTree((char *) p,exception);
  xmp_profile=DestroyString(xmp_profile);
  exception=DestroyExceptionInfo(exception);
  if (xmp == (XMLTreeInfo *) NULL)
    return(MagickFalse);
  status=MagickFalse;
  rdf=GetXMLTreeChild(xmp,"rdf:RDF");
  if (rdf != (XMLTreeInfo *) NULL)
    {
      if (image->properties == (void *) NULL)
        ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
          RelinquishMagickMemory,RelinquishMagickMemory);
      description=GetXMLTreeChild(rdf,"rdf:Description");
      while (description != (XMLTreeInfo *) NULL)
      {
        node=GetXMLTreeChild(description,(const char *) NULL);
        while (node != (XMLTreeInfo *) NULL)
        {
          char
            *xmp_namespace;

          child=GetXMLTreeChild(node,(const char *) NULL);
          content=GetXMLTreeContent(node);
          if ((child == (XMLTreeInfo *) NULL) &&
              (SkipXMPValue(content) == MagickFalse))
            {
              xmp_namespace=ConstantString(GetXMLTreeTag(node));
              (void) SubstituteString(&xmp_namespace,"exif:","xmp:");
              (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
                xmp_namespace,ConstantString(content));
            }
          while (child != (XMLTreeInfo *) NULL)
          {
            content=GetXMLTreeContent(child);
            if (SkipXMPValue(content) == MagickFalse)
              {
                xmp_namespace=ConstantString(GetXMLTreeTag(node));
                (void) SubstituteString(&xmp_namespace,"exif:","xmp:");
                (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
                  xmp_namespace,ConstantString(content));
              }
            child=GetXMLTreeSibling(child);
          }
          node=GetXMLTreeSibling(node);
        }
        description=GetNextXMLTreeTag(description);
      }
    }
  xmp=DestroyXMLTree(xmp);
  return(status);
}


    DataBuf PngChunk::readRawProfile(const DataBuf& text,bool iTXt)
    {
        DataBuf                 info;
        register long           i;
        register unsigned char *dp;
        const char             *sp;
        unsigned int            nibbles;
        long                    length;
        unsigned char           unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
                                            0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
                                            0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
                                            0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
                                            0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
                                            13,14,15};
        if (text.size_ == 0) {
            return DataBuf();
        }

        if ( iTXt ) {
            info.alloc(text.size_);
            ::memcpy(info.pData_,text.pData_,text.size_);
            return  info;
        }

        sp = (char*)text.pData_+1;

        // Look for newline

        while (*sp != '\n')
            sp++;

        // Look for length

        while (*sp == '\0' || *sp == ' ' || *sp == '\n')
            sp++;

        length = (long) atol(sp);

        while (*sp != ' ' && *sp != '\n')
            sp++;

        // Allocate space

        if (length == 0)
        {
#ifdef DEBUG
            std::cerr << "Exiv2::PngChunk::readRawProfile: Unable To Copy Raw Profile: invalid profile length\n";
#endif
        }

        info.alloc(length);

        if (info.size_ != length)
        {
#ifdef DEBUG
            std::cerr << "Exiv2::PngChunk::readRawProfile: Unable To Copy Raw Profile: cannot allocate memory\n";
#endif
            return DataBuf();
        }

        // Copy profile, skipping white space and column 1 "=" signs

        dp      = (unsigned char*)info.pData_;
        nibbles = length * 2;

        for (i = 0; i < (long) nibbles; i++)
        {
            while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
            {
                if (*sp == '\0')
                {
#ifdef DEBUG
                    std::cerr << "Exiv2::PngChunk::readRawProfile: Unable To Copy Raw Profile: ran out of data\n";
#endif
                    return DataBuf();
                }

                sp++;
            }

            if (i%2 == 0)
                *dp = (unsigned char) (16*unhex[(int) *sp++]);
            else
                (*dp++) += unhex[(int) *sp++];
        }

        return info;

    } // PngChunk::readRawProfile


static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
  Image *image)
{
  char
    layer_name[MaxTextExtent];

  const char
    *property;

  const StringInfo
    *icc_profile,
    *info;

  Image
    *base_image,
    *next_image;

  MagickBooleanType
    status;

  MagickOffsetType
    *layer_size_offsets,
    size_offset;

  PSDInfo
    psd_info;

  register ssize_t
    i;

  size_t
    layer_count,
    layer_index,
    length,
    name_length,
    num_channels,
    packet_size,
    rounded_size,
    size;

  StringInfo
    *bim_profile;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
  if (status == MagickFalse)
    return(status);
  packet_size=(size_t) (image->depth > 8 ? 6 : 3);
  if (image->matte != MagickFalse)
    packet_size+=image->depth > 8 ? 2 : 1;
  psd_info.version=1;
  if ((LocaleCompare(image_info->magick,"PSB") == 0) ||
      (image->columns > 30000) || (image->rows > 30000))
    psd_info.version=2;
  (void) WriteBlob(image,4,(const unsigned char *) "8BPS");
  (void) WriteBlobMSBShort(image,psd_info.version);  /* version */
  for (i=1; i <= 6; i++)
    (void) WriteBlobByte(image, 0);  /* 6 bytes of reserved */
  /* When the image has a color profile it won't be converted to gray scale */
  if ((GetImageProfile(image,"icc") == (StringInfo *) NULL) &&
      (SetImageGray(image,&image->exception) != MagickFalse))
    num_channels=(image->matte != MagickFalse ? 2UL : 1UL);
  else
    if ((image_info->type != TrueColorType) && (image_info->type !=
         TrueColorMatteType) && (image->storage_class == PseudoClass))
      num_channels=(image->matte != MagickFalse ? 2UL : 1UL);
    else
      {
        if (image->storage_class == PseudoClass)
          (void) SetImageStorageClass(image,DirectClass);
        if (image->colorspace != CMYKColorspace)
          num_channels=(image->matte != MagickFalse ? 4UL : 3UL);
        else
          num_channels=(image->matte != MagickFalse ? 5UL : 4UL);
      }
  (void) WriteBlobMSBShort(image,(unsigned short) num_channels);
  (void) WriteBlobMSBLong(image,(unsigned int) image->rows);
  (void) WriteBlobMSBLong(image,(unsigned int) image->columns);
  if (IsGrayImage(image,&image->exception) != MagickFalse)
    {
      MagickBooleanType
        monochrome;

      /*
        Write depth & mode.
      */
      monochrome=IsMonochromeImage(image,&image->exception) &&
        (image->depth == 1) ? MagickTrue : MagickFalse;
      (void) WriteBlobMSBShort(image,(unsigned short)
        (monochrome != MagickFalse ? 1 : image->depth > 8 ? 16 : 8));
      (void) WriteBlobMSBShort(image,(unsigned short)
        (monochrome != MagickFalse ? BitmapMode : GrayscaleMode));
    }
  else
    {
      (void) WriteBlobMSBShort(image,(unsigned short) (image->storage_class ==
        PseudoClass ? 8 : image->depth > 8 ? 16 : 8));
      if (((image_info->colorspace != UndefinedColorspace) ||
           (image->colorspace != CMYKColorspace)) &&
          (image_info->colorspace != CMYKColorspace))
        {
          (void) TransformImageColorspace(image,sRGBColorspace);
          (void) WriteBlobMSBShort(image,(unsigned short)
            (image->storage_class == PseudoClass ? IndexedMode : RGBMode));
        }
      else
        {
          if (image->colorspace != CMYKColorspace)
            (void) TransformImageColorspace(image,CMYKColorspace);
          (void) WriteBlobMSBShort(image,CMYKMode);
        }
    }
  if ((IsGrayImage(image,&image->exception) != MagickFalse) ||
      (image->storage_class == DirectClass) || (image->colors > 256))
    (void) WriteBlobMSBLong(image,0);
  else
    {
      /*
        Write PSD raster colormap.
      */
      (void) WriteBlobMSBLong(image,768);
      for (i=0; i < (ssize_t) image->colors; i++)
        (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].red));
      for ( ; i < 256; i++)
        (void) WriteBlobByte(image,0);
      for (i=0; i < (ssize_t) image->colors; i++)
        (void) WriteBlobByte(image,ScaleQuantumToChar(
          image->colormap[i].green));
      for ( ; i < 256; i++)
        (void) WriteBlobByte(image,0);
      for (i=0; i < (ssize_t) image->colors; i++)
        (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].blue));
      for ( ; i < 256; i++)
        (void) WriteBlobByte(image,0);
    }
  /*
    Image resource block.
  */
  length=28; /* 0x03EB */
  bim_profile=(StringInfo *) GetImageProfile(image,"8bim");
  icc_profile=GetImageProfile(image,"icc");
  if (bim_profile != (StringInfo *) NULL)
    {
      bim_profile=CloneStringInfo(bim_profile);
      if (icc_profile != (StringInfo *) NULL)
        RemoveICCProfileFromResourceBlock(bim_profile);
      RemoveResolutionFromResourceBlock(bim_profile);
      length+=PSDQuantum(GetStringInfoLength(bim_profile));
    }
  if (icc_profile != (const StringInfo *) NULL)
    length+=PSDQuantum(GetStringInfoLength(icc_profile))+12;
  (void) WriteBlobMSBLong(image,(unsigned int) length);
  WriteResolutionResourceBlock(image);
  if (bim_profile != (StringInfo *) NULL)
    {
      (void) WriteBlob(image,GetStringInfoLength(bim_profile),
        GetStringInfoDatum(bim_profile));
      bim_profile=DestroyStringInfo(bim_profile);
    }
  if (icc_profile != (StringInfo *) NULL)
    {
      (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
      (void) WriteBlobMSBShort(image,0x0000040F);
      (void) WriteBlobMSBShort(image,0);
      (void) WriteBlobMSBLong(image,(unsigned int) GetStringInfoLength(
        icc_profile));
      (void) WriteBlob(image,GetStringInfoLength(icc_profile),
        GetStringInfoDatum(icc_profile));
      if ((MagickOffsetType) GetStringInfoLength(icc_profile) !=
          PSDQuantum(GetStringInfoLength(icc_profile)))
        (void) WriteBlobByte(image,0);
    }
  base_image=GetNextImageInList(image);
  if (base_image == (Image *)NULL)
    base_image=image;
  size=0;
  size_offset=TellBlob(image);
  SetPSDSize(&psd_info,image,0);
  SetPSDSize(&psd_info,image,0);
  layer_count=0;
  for (next_image=base_image; next_image != NULL; )
  {
    layer_count++;
    next_image=GetNextImageInList(next_image);
  }
  if (image->matte != MagickFalse)
    size+=WriteBlobMSBShort(image,-(unsigned short) layer_count);
  else
    size+=WriteBlobMSBShort(image,(unsigned short) layer_count);
  layer_size_offsets=(MagickOffsetType *) AcquireQuantumMemory(
    (size_t) layer_count,sizeof(MagickOffsetType));
  if (layer_size_offsets == (MagickOffsetType *) NULL)
    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
  layer_index=0;
  for (next_image=base_image; next_image != NULL; )
  {
    Image
      *mask;

    unsigned char
      default_color;

    unsigned short
      channels,
      total_channels;

    mask=(Image *) NULL;
    property=GetImageArtifact(next_image,"psd:opacity-mask");
    default_color=0;
    if (property != (const char *) NULL)
      {
        mask=(Image *) GetImageRegistry(ImageRegistryType,property,
          &image->exception);
        default_color=strlen(property) == 9 ? 255 : 0;
      }
    size+=WriteBlobMSBLong(image,(unsigned int) next_image->page.y);
    size+=WriteBlobMSBLong(image,(unsigned int) next_image->page.x);
    size+=WriteBlobMSBLong(image,(unsigned int) (next_image->page.y+
      next_image->rows));
    size+=WriteBlobMSBLong(image,(unsigned int) (next_image->page.x+
      next_image->columns));
    channels=1U;
    if ((next_image->storage_class != PseudoClass) &&
        (IsGrayImage(next_image,&next_image->exception) == MagickFalse))
      channels=next_image->colorspace == CMYKColorspace ? 4U : 3U;
    total_channels=channels;
    if (next_image->matte != MagickFalse)
      total_channels++;
    if (mask != (Image *) NULL)
      total_channels++;
    size+=WriteBlobMSBShort(image,total_channels);
    layer_size_offsets[layer_index++]=TellBlob(image);
    for (i=0; i < (ssize_t) channels; i++)
      size+=WriteChannelSize(&psd_info,image,(signed short) i);
    if (next_image->matte != MagickFalse)
      size+=WriteChannelSize(&psd_info,image,-1);
    if (mask != (Image *) NULL)
      size+=WriteChannelSize(&psd_info,image,-2);
    size+=WriteBlob(image,4,(const unsigned char *) "8BIM");
    size+=WriteBlob(image,4,(const unsigned char *)
      CompositeOperatorToPSDBlendMode(next_image->compose));
    property=GetImageArtifact(next_image,"psd:layer.opacity");
    if (property != (const char *) NULL)
      {
        Quantum
          opacity;

        opacity=(Quantum) StringToInteger(property);
        size+=WriteBlobByte(image,ScaleQuantumToChar(opacity));
        (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue,
          &image->exception);
      }
    else
      size+=WriteBlobByte(image,255);
    size+=WriteBlobByte(image,0);
    size+=WriteBlobByte(image,next_image->compose==NoCompositeOp ?
      1 << 0x02 : 1); /* layer properties - visible, etc. */
    size+=WriteBlobByte(image,0);
    info=GetAdditionalInformation(image_info,next_image);
    property=(const char *) GetImageProperty(next_image,"label");
    if (property == (const char *) NULL)
      {
        (void) FormatLocaleString(layer_name,MaxTextExtent,"L%.20g",
          (double) layer_index);
        property=layer_name;
      }
    name_length=strlen(property)+1;
    if ((name_length % 4) != 0)
      name_length+=(4-(name_length % 4));
    if (info != (const StringInfo *) NULL)
      name_length+=GetStringInfoLength(info);
    name_length+=8;
    if (mask != (Image *) NULL)
      name_length+=20;
    size+=WriteBlobMSBLong(image,(unsigned int) name_length);
    if (mask == (Image *) NULL)
      size+=WriteBlobMSBLong(image,0);
    else
      {
        if (mask->compose != NoCompositeOp)
          (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum(
            default_color),MagickTrue,&image->exception);
        mask->page.y+=image->page.y;
        mask->page.x+=image->page.x;
        size+=WriteBlobMSBLong(image,20);
        size+=WriteBlobMSBSignedLong(image,mask->page.y);
        size+=WriteBlobMSBSignedLong(image,mask->page.x);
        size+=WriteBlobMSBSignedLong(image,(const signed int) mask->rows+
          mask->page.y);
        size+=WriteBlobMSBSignedLong(image,(const signed int) mask->columns+
          mask->page.x);
        size+=WriteBlobByte(image,default_color);
        size+=WriteBlobByte(image,mask->compose == NoCompositeOp ? 2 : 0);
        size+=WriteBlobMSBShort(image,0);
      }
    size+=WriteBlobMSBLong(image,0);
    size+=WritePascalString(image,property,4);
    if (info != (const StringInfo *) NULL)
      size+=WriteBlob(image,GetStringInfoLength(info),GetStringInfoDatum(info));
    next_image=GetNextImageInList(next_image);
  }
  /*
    Now the image data!
  */
  next_image=base_image;
  layer_index=0;
  while (next_image != NULL)
  {
    length=WritePSDChannels(&psd_info,image_info,image,next_image,
      layer_size_offsets[layer_index++],MagickTrue);
    if (length == 0)
      {
        status=MagickFalse;
        break;
      }
    size+=length;
    next_image=GetNextImageInList(next_image);
  }
  (void) WriteBlobMSBLong(image,0);  /* user mask data */
  /*
    Remove the opacity mask from the registry
  */
  next_image=base_image;
  while (next_image != (Image *) NULL)
  {
    property=GetImageArtifact(next_image,"psd:opacity-mask");
    if (property != (const char *) NULL)
      DeleteImageRegistry(property);
    next_image=GetNextImageInList(next_image);
  }
  /*
    Write the total size
  */
  size_offset+=WritePSDSize(&psd_info,image,size+
    (psd_info.version == 1 ? 8 : 12),size_offset);
  if ((size/2) != ((size+1)/2))
    rounded_size=size+1;
  else
    rounded_size=size;
  (void) WritePSDSize(&psd_info,image,rounded_size,size_offset);
  layer_size_offsets=(MagickOffsetType *) RelinquishMagickMemory(
    layer_size_offsets);
  /*
    Write composite image.
  */
  if (status != MagickFalse)
    {
      CompressionType
        compression;

      compression=image->compression;
      if (image->compression == ZipCompression)
        image->compression=RLECompression;
      if (WritePSDChannels(&psd_info,image_info,image,image,0,
          MagickFalse) == 0)
        status=MagickFalse;
      image->compression=compression;
    }
  (void) CloseBlob(image);
  return(status);
}


static MagickBooleanType CheckPSDChannels(const PSDInfo *psd_info,
  LayerInfo *layer_info)
{
  int
    channel_type;

  register ssize_t
    i;

  if (layer_info->channels < psd_info->min_channels)
    return(MagickFalse);
  channel_type=RedChannel;
  if (psd_info->min_channels >= 3)
    channel_type|=(GreenChannel | BlueChannel);
  if (psd_info->min_channels >= 4)
    channel_type|=BlackChannel;
  for (i=0; i < layer_info->channels; i++)
  {
    short
      type;

    type=layer_info->channel_info[i].type;
    if (type == -1)
      {
        channel_type|=AlphaChannel;
        continue;
      }
    if (type < -1)
      continue;
    if (type == 0)
      channel_type&=~RedChannel;
    else if (type == 1)
      channel_type&=~GreenChannel;
    else if (type == 2)
      channel_type&=~BlueChannel;
    else if (type == 3)
      channel_type&=~BlackChannel;
  }
  if (channel_type == 0)
    return(MagickTrue);
  if ((channel_type == AlphaChannel) &&
      (layer_info->channels >= psd_info->min_channels + 1))
    return(MagickTrue);
  return(MagickFalse);
}


static MagickBooleanType ReadPSDChannel(Image *image,
  const ImageInfo *image_info,const PSDInfo *psd_info,LayerInfo* layer_info,
  const size_t channel,const PSDCompressionType compression,
  ExceptionInfo *exception)
{
  Image
    *channel_image,
    *mask;

  MagickOffsetType
    offset;

  MagickBooleanType
    status;

  channel_image=image;
  mask=(Image *) NULL;
  if ((layer_info->channel_info[channel].type < -1) &&
      (layer_info->mask.page.width > 0) && (layer_info->mask.page.height > 0))
    {
      const char
        *option;

      /*
        Ignore mask that is not a user supplied layer mask, if the mask is
        disabled or if the flags have unsupported values.
      */
      option=GetImageOption(image_info,"psd:preserve-opacity-mask");
      if ((layer_info->channel_info[channel].type != -2) ||
          (layer_info->mask.flags > 2) || ((layer_info->mask.flags & 0x02) &&
           (IsStringTrue(option) == MagickFalse)))
      {
        SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
        return(MagickTrue);
      }
      mask=CloneImage(image,layer_info->mask.page.width,
        layer_info->mask.page.height,MagickFalse,exception);
      if (mask != (Image *) NULL)
        {
          mask->matte=MagickFalse;
          channel_image=mask;
        }
    }

  offset=TellBlob(image);
  status=MagickFalse;
  switch(compression)
  {
    case Raw:
      status=ReadPSDChannelRaw(channel_image,psd_info->channels,
        layer_info->channel_info[channel].type,exception);
      break;
    case RLE:
      {
        MagickOffsetType
          *sizes;

        sizes=ReadPSDRLESizes(channel_image,psd_info,channel_image->rows);
        if (sizes == (MagickOffsetType *) NULL)
          ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
            image->filename);
        status=ReadPSDChannelRLE(channel_image,psd_info,
          layer_info->channel_info[channel].type,sizes,exception);
        sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
      }
      break;
    case ZipWithPrediction:
    case ZipWithoutPrediction:
#ifdef MAGICKCORE_ZLIB_DELEGATE
      status=ReadPSDChannelZip(channel_image,layer_info->channels,
        layer_info->channel_info[channel].type,compression,
        layer_info->channel_info[channel].size-2,exception);
#else
      (void) ThrowMagickException(exception,GetMagickModule(),
          MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
            "'%s' (ZLIB)",image->filename);
#endif
      break;
    default:
      (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
        "CompressionNotSupported","'%.20g'",(double) compression);
      break;
  }

  SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
  if (status == MagickFalse)
    {
      if (mask != (Image *) NULL)
        DestroyImage(mask);
      ThrowBinaryException(CoderError,"UnableToDecompressImage",
        image->filename);
    }
  if (mask != (Image *) NULL)
    {
      if (layer_info->mask.image != (Image *) NULL)
        layer_info->mask.image=DestroyImage(layer_info->mask.image);
      layer_info->mask.image=mask;
    }
  return(status);
}


static size_t WritePSDChannels(const PSDInfo *psd_info,
  const ImageInfo *image_info,Image *image,Image *next_image,
  MagickOffsetType size_offset,const MagickBooleanType separate,
  ExceptionInfo *exception)
{
  CompressionType
    compression;

  Image
    *mask;

  MagickOffsetType
    rows_offset;

  size_t
    channels,
    count,
    length,
    offset_length;

  unsigned char
    *compact_pixels;

  count=0;
  offset_length=0;
  rows_offset=0;
  compact_pixels=(unsigned char *) NULL;
  compression=next_image->compression;
  if (image_info->compression != UndefinedCompression)
    compression=image_info->compression;
  if (compression == RLECompression)
    {
      compact_pixels=AcquireCompactPixels(next_image,exception);
      if (compact_pixels == (unsigned char *) NULL)
        return(0);
    }
  channels=1;
  if (separate == MagickFalse)
    {
      if (next_image->storage_class != PseudoClass)
        {
          if (IsImageGray(next_image) == MagickFalse)
            channels=next_image->colorspace == CMYKColorspace ? 4 : 3;
          if (next_image->alpha_trait != UndefinedPixelTrait)
            channels++;
        }
      rows_offset=TellBlob(image)+2;
      count+=WriteCompressionStart(psd_info,image,next_image,compression,
        channels);
      offset_length=(next_image->rows*(psd_info->version == 1 ? 2 : 4));
    }
  size_offset+=2;
  if (next_image->storage_class == PseudoClass)
    {
      length=WritePSDChannel(psd_info,image_info,image,next_image,
        IndexQuantum,compact_pixels,rows_offset,separate,compression,
        exception);
      if (separate != MagickFalse)
        size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
      else
        rows_offset+=offset_length;
      count+=length;
    }
  else
    {
      if (IsImageGray(next_image) != MagickFalse)
        {
          length=WritePSDChannel(psd_info,image_info,image,next_image,
            GrayQuantum,compact_pixels,rows_offset,separate,compression,
            exception);
          if (separate != MagickFalse)
            size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
          else
            rows_offset+=offset_length;
          count+=length;
        }
      else
        {
          if (next_image->colorspace == CMYKColorspace)
            (void) NegateCMYK(next_image,exception);

          length=WritePSDChannel(psd_info,image_info,image,next_image,
            RedQuantum,compact_pixels,rows_offset,separate,compression,
            exception);
          if (separate != MagickFalse)
            size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
          else
            rows_offset+=offset_length;
          count+=length;

          length=WritePSDChannel(psd_info,image_info,image,next_image,
            GreenQuantum,compact_pixels,rows_offset,separate,compression,
            exception);
          if (separate != MagickFalse)
            size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
          else
            rows_offset+=offset_length;
          count+=length;

          length=WritePSDChannel(psd_info,image_info,image,next_image,
            BlueQuantum,compact_pixels,rows_offset,separate,compression,
            exception);
          if (separate != MagickFalse)
            size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
          else
            rows_offset+=offset_length;
          count+=length;

          if (next_image->colorspace == CMYKColorspace)
            {
              length=WritePSDChannel(psd_info,image_info,image,next_image,
                BlackQuantum,compact_pixels,rows_offset,separate,compression,
                exception);
              if (separate != MagickFalse)
                size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
              else
                rows_offset+=offset_length;
              count+=length;
            }
        }
      if (next_image->alpha_trait != UndefinedPixelTrait)
        {
          length=WritePSDChannel(psd_info,image_info,image,next_image,
            AlphaQuantum,compact_pixels,rows_offset,separate,compression,
            exception);
          if (separate != MagickFalse)
            size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
          else
            rows_offset+=offset_length;
          count+=length;
        }
    }
  compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
  if (next_image->colorspace == CMYKColorspace)
    (void) NegateCMYK(next_image,exception);
  if (separate != MagickFalse)
    {
      const char
        *property;

      property=GetImageArtifact(next_image,"psd:opacity-mask");
      if (property != (const char *) NULL)
        {
          mask=(Image *) GetImageRegistry(ImageRegistryType,property,
            exception);
          if (mask != (Image *) NULL)
            {
              if (compression == RLECompression)
                {
                  compact_pixels=AcquireCompactPixels(mask,exception);
                  if (compact_pixels == (unsigned char *) NULL)
                    return(0);
                }
              length=WritePSDChannel(psd_info,image_info,image,mask,
                RedQuantum,compact_pixels,rows_offset,MagickTrue,compression,
                exception);
              (void) WritePSDSize(psd_info,image,length,size_offset);
              count+=length;
              compact_pixels=(unsigned char *) RelinquishMagickMemory(
                compact_pixels);
            }
        }
    }
  return(count);
}


static MagickBooleanType ReadPSDChannel(Image *image,
  const ImageInfo *image_info,const PSDInfo *psd_info,LayerInfo* layer_info,
  const size_t channel,const PSDCompressionType compression,
  ExceptionInfo *exception)
{
  Image
    *channel_image,
    *mask;

  MagickOffsetType
    offset;

  MagickBooleanType
    status;

  channel_image=image;
  mask=(Image *) NULL;
  if ((layer_info->channel_info[channel].type < -1) &&
      (layer_info->mask.page.width > 0) && (layer_info->mask.page.height > 0))
    {
      const char
        *option;

      /*
        Ignore mask that is not a user supplied layer mask, if the mask is
        disabled or if the flags have unsupported values.
      */
      option=GetImageOption(image_info,"psd:preserve-opacity-mask");
      if ((layer_info->channel_info[channel].type != -2) ||
          (layer_info->mask.flags > 2) || ((layer_info->mask.flags & 0x02) &&
           (IsStringTrue(option) == MagickFalse)))
        {
          SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
          return(MagickTrue);
        }
      mask=CloneImage(image,layer_info->mask.page.width,
        layer_info->mask.page.height,MagickFalse,exception);
      if (mask != (Image *) NULL)
        {
          SetImageType(mask,GrayscaleType,exception);
          channel_image=mask;
        }
    }

  offset=TellBlob(image);
  status=MagickFalse;
  switch(compression)
  {
    case Raw:
      status=ReadPSDChannelRaw(channel_image,psd_info->channels,
        layer_info->channel_info[channel].type,exception);
      break;
    case RLE:
      {
        MagickOffsetType
          *sizes;

        sizes=ReadPSDRLESizes(channel_image,psd_info,channel_image->rows);
        if (sizes == (MagickOffsetType *) NULL)
          ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
            image->filename);
        status=ReadPSDChannelRLE(channel_image,psd_info,
          layer_info->channel_info[channel].type,sizes,exception);
        sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
      }
      break;
    case ZipWithPrediction:
    case ZipWithoutPrediction:
#ifdef MAGICKCORE_ZLIB_DELEGATE
      status=ReadPSDChannelZip(channel_image,layer_info->channels,
        layer_info->channel_info[channel].type,compression,
        layer_info->channel_info[channel].size-2,exception);
#else
      (void) ThrowMagickException(exception,GetMagickModule(),
          MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
            "'%s' (ZLIB)",image->filename);
#endif
      break;
    default:
      (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
        "CompressionNotSupported","'%.20g'",(double) compression);
      break;
  }

  SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
  if (status == MagickFalse)
    {
      if (mask != (Image *) NULL)
        DestroyImage(mask);
      ThrowBinaryException(CoderError,"UnableToDecompressImage",
        image->filename);
    }
  if (mask != (Image *) NULL)
    {
      if (layer_info->mask.image != (Image *) NULL)
        layer_info->mask.image=DestroyImage(layer_info->mask.image);
      layer_info->mask.image=mask;
    }
  return(status);
}


zsetstrokecolorspace(i_ctx_t * i_ctx_p)
{
    int code;
    es_ptr iesp = esp;  /* preserve exec stack in case of error */

    code = zswapcolors(i_ctx_p);
    if (code < 0)
        return code;

    /* Set up for the continuation procedure which will finish by restoring the fill colour space */
    /* Make sure the exec stack has enough space */
    check_estack(1);
    /* Now, the actual continuation routine */
    push_op_estack(setstrokecolorspace_cont);

    code = zsetcolorspace(i_ctx_p);
    if (code >= 0)
        return o_push_estack;

    /* Something went wrong, swap back to the non-stroking space and restore the exec stack */
    esp = iesp;
    (void)zswapcolors(i_ctx_p);
    return code;
}


pdf14_cmap_separation_direct(frac all, gx_device_color * pdc, const gs_gstate * pgs,
                 gx_device * dev, gs_color_select_t select)
{
    int i, ncomps = dev->color_info.num_components;
    int num_spots = pdf14_get_num_spots(dev);
    bool additive = dev->color_info.polarity == GX_CINFO_POLARITY_ADDITIVE;
    frac cm_comps[GX_DEVICE_COLOR_MAX_COMPONENTS];
    gx_color_value cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
    gx_color_index color;

    if (pgs->color_component_map.sep_type == SEP_ALL) {
        frac comp_value = all;

        /*
         * Invert the photometric interpretation for additive
         * color spaces because separations are always subtractive.
         */
        if (additive)
            comp_value = frac_1 - comp_value;
        /* Use the "all" value for all components */
        i = pgs->color_component_map.num_colorants - 1;
        for (; i >= 0; i--)
            cm_comps[i] = comp_value;
    } else {
        frac comp_value[GX_DEVICE_COLOR_MAX_COMPONENTS];

        /* map to the color model */
        for (i = pgs->color_component_map.num_components - 1; i >= 0; i--)
            comp_value[i] = all;
        map_components_to_colorants(comp_value, &(pgs->color_component_map), cm_comps);
    }
    /* apply the transfer function(s); convert to color values */
    if (additive) {
        for (i = 0; i < ncomps; i++)
            cv[i] = frac2cv(gx_map_color_frac(pgs, cm_comps[i], effective_transfer[i]));
        /* We are in an additive mode (blend space) and drawing with a sep color
        into a sep device.  Make sure we are drawing "white" with the process
        colorants, but only if we are not in an ALL case */
        if (pgs->color_component_map.sep_type != SEP_ALL)
            for (i = 0; i < ncomps - num_spots; i++)
                cv[i] = gx_max_color_value;
    } else
        for (i = 0; i < ncomps; i++)
            cv[i] = frac2cv(frac_1 - gx_map_color_frac(pgs, (frac)(frac_1 - cm_comps[i]), effective_transfer[i]));

    /* if output device supports devn, we need to make sure we send it the
       proper color type */
    if (dev_proc(dev, dev_spec_op)(dev, gxdso_supports_devn, NULL, 0)) {
        for (i = 0; i < ncomps; i++)
            pdc->colors.devn.values[i] = cv[i];
        pdc->type = gx_dc_type_devn;
    } else {
        /* encode as a color index */
        color = dev_proc(dev, encode_color)(dev, cv);
        /* check if the encoding was successful; we presume failure is rare */
        if (color != gx_no_color_index)
            color_set_pure(pdc, color);
    }
}


gs_pdf14_clist_device_push(gs_memory_t *mem, gs_gstate *pgs, gx_device **pcdev,
                           gx_device *dev, const gs_pdf14trans_t *pdf14pct)
{
    int code;
    pdf14_clist_device *p14dev;
    gx_device_clist_writer * const cdev = &((gx_device_clist *)dev)->writer;

    code = pdf14_create_clist_device(mem, pgs, pcdev, dev, pdf14pct);
    /*
     * Set the color_info of the clist device to match the compositing
     * device.  We will restore it when the compositor is popped.
     * See pdf14_clist_create_compositor for the restore.  Do the
     * same with the gs_gstate's get_cmap_procs.  We do not want
     * the gs_gstate to use transfer functions on our color values.
     * The transfer functions will be applied at the end after we
     * have done our PDF 1.4 blend operations.
     */
    p14dev = (pdf14_clist_device *)(*pcdev);
    p14dev->saved_target_color_info = dev->color_info;
    dev->color_info = (*pcdev)->color_info;
    /* Make sure that we keep the anti-alias information though */
    dev->color_info.anti_alias = p14dev->saved_target_color_info.anti_alias;
    p14dev->color_info.anti_alias = dev->color_info.anti_alias;

    /* adjust the clist_color_info now */
    cdev->clist_color_info.depth = p14dev->color_info.depth;
    cdev->clist_color_info.polarity = p14dev->color_info.polarity;
    cdev->clist_color_info.num_components = p14dev->color_info.num_components;
    cdev->clist_color_info.max_color = p14dev->color_info.max_color;
    cdev->clist_color_info.max_gray = p14dev->color_info.max_gray;

    p14dev->saved_target_encode_color = dev_proc(dev, encode_color);
    p14dev->saved_target_decode_color = dev_proc(dev, decode_color);
    set_dev_proc(dev, encode_color, p14dev->my_encode_color);
    set_dev_proc(p14dev, encode_color, p14dev->my_encode_color);
    set_dev_proc(dev, decode_color, p14dev->my_decode_color);
    set_dev_proc(p14dev, decode_color, p14dev->my_decode_color);
    p14dev->saved_target_get_color_mapping_procs =
                              dev_proc(dev, get_color_mapping_procs);
    p14dev->saved_target_get_color_comp_index =
                              dev_proc(dev, get_color_comp_index);
    set_dev_proc(dev, get_color_mapping_procs, p14dev->my_get_color_mapping_procs);
    set_dev_proc(p14dev, get_color_mapping_procs, p14dev->my_get_color_mapping_procs);
    set_dev_proc(dev, get_color_comp_index, p14dev->my_get_color_comp_index);
    set_dev_proc(p14dev, get_color_comp_index, p14dev->my_get_color_comp_index);
    p14dev->save_get_cmap_procs = pgs->get_cmap_procs;
    pgs->get_cmap_procs = pdf14_get_cmap_procs;
    gx_set_cmap_procs(pgs, dev);
    return code;
}


gdev_x_get_params(gx_device * dev, gs_param_list * plist)
{
    gx_device_X *xdev = (gx_device_X *) dev;
    int code = gx_default_get_params(dev, plist);
    long id = (long)xdev->pwin;

    if (code < 0 ||
        (code = param_write_long(plist, "WindowID", &id)) < 0 ||
        (code = param_write_bool(plist, ".IsPageDevice", &xdev->IsPageDevice)) < 0 ||
        (code = param_write_int(plist, "MaxTempPixmap", &xdev->MaxTempPixmap)) < 0 ||
        (code = param_write_int(plist, "MaxTempImage", &xdev->MaxTempImage)) < 0
        )
        DO_NOTHING;
    return code;
}


pdf14_update_device_color_procs_push_c(gx_device *dev,
                              gs_transparency_color_t group_color, int64_t icc_hashcode,
                              gs_gstate *pgs, cmm_profile_t *icc_profile, bool is_mask)
{
    pdf14_device *pdevproto;
    pdf14_device *pdev = (pdf14_device *)dev;
    gx_device_clist_writer * cldev = (gx_device_clist_writer *)pdev->pclist_device;
    const pdf14_procs_t *new_14procs;
    bool update_color_info;
    gx_color_polarity_t new_polarity;
    int new_num_comps;
    bool new_additive = false;
    byte new_depth;
    byte comp_bits[GX_DEVICE_COLOR_MAX_COMPONENTS];
    byte comp_shift[GX_DEVICE_COLOR_MAX_COMPONENTS];
    int k;
    bool has_tags = device_encodes_tags(dev);
    cmm_profile_t *icc_profile_dev = NULL;
    gsicc_rendering_param_t render_cond;
    cmm_dev_profile_t *dev_profile;

    memset(comp_bits, 0, GX_DEVICE_COLOR_MAX_COMPONENTS);
    memset(comp_shift, 0, GX_DEVICE_COLOR_MAX_COMPONENTS);

    if (group_color == ICC && icc_profile == NULL)
        return gs_throw(gs_error_undefinedresult, "Missing ICC data");
    if_debug0m('v', cldev->memory, "[v]pdf14_update_device_color_procs_push_c\n");
   /* Check if we need to alter the device procs at this stage.  Many of the procs
      are based upon the color space of the device.  We want to remain in the
      color space defined by the color space of the soft mask or transparency
      group as opposed to the device color space. Later, when we pop the softmask
      we will collapse it to a single band and then compose with it to the device
      color space (or the parent layer space).  In the case where we pop an
      isolated transparency group, we will do the blending in the proper color
      space and then transform the data when we pop the group.  Remember that only
      isolated groups can have color spaces that are different than their parent. */
    update_color_info = false;
    switch (group_color) {
        case GRAY_SCALE:
            if (pdev->color_info.num_components != 1){
                update_color_info = true;
                new_polarity = GX_CINFO_POLARITY_ADDITIVE;
                new_num_comps = 1;
                pdevproto = (pdf14_device *)&gs_pdf14_Gray_device;
                new_additive = true;
                new_14procs = &gray_pdf14_procs;
                new_depth = 8;
                comp_bits[0] = 8;
                comp_shift[0] = 0;
            }
            break;
        case DEVICE_RGB:
        case CIE_XYZ:
            if (pdev->color_info.num_components != 3){
                update_color_info = true;
                new_polarity = GX_CINFO_POLARITY_ADDITIVE;
                new_num_comps = 3;
                pdevproto = (pdf14_device *)&gs_pdf14_RGB_device;
                new_additive = true;
                new_14procs = &rgb_pdf14_procs;
                new_depth = 24;
                for (k = 0; k < 3; k++) {
                    comp_bits[k] = 8;
                    comp_shift[k] = (2-k)*8;
                }
            }
            break;
        case DEVICE_CMYK:
            if (pdev->color_info.num_components != 4){
                update_color_info = true;
                new_polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
                new_num_comps = 4;
                pdevproto = (pdf14_device *)&gs_pdf14_CMYK_device;
                new_additive = false;
                /* This is needed due to the mismatched compressed encode decode
                   between the device procs and the pdf14 procs */
                if (dev->color_info.num_components > 4){
                    new_14procs = &cmykspot_pdf14_procs;
                } else {
                    new_14procs = &cmyk_pdf14_procs;
                }
                new_depth = 32;
                for (k = 0; k < 4; k++) {
                    comp_bits[k] = 8;
                    comp_shift[k] = (3-k)*8;
                }
            }
            break;
        case ICC:
            /* Check if the profile is different. */
            dev_proc(dev, get_profile)(dev,  &dev_profile);
            gsicc_extract_profile(GS_UNKNOWN_TAG, dev_profile,
                                  &icc_profile_dev, &render_cond);
            if (icc_profile_dev->hashcode != icc_profile->hashcode) {
                update_color_info = true;
                new_num_comps = icc_profile->num_comps;
                new_depth = icc_profile->num_comps * 8;
                switch (new_num_comps) {
                case 1:
                    if (pdev->sep_device && !is_mask) {
                        pdevproto = (pdf14_device *)&gs_pdf14_Grayspot_device;
                        new_14procs = &grayspot_pdf14_procs;
                    } else {
                        pdevproto = (pdf14_device *)&gs_pdf14_Gray_device;
                        new_14procs = &gray_pdf14_procs;
                    }
                    new_polarity = GX_CINFO_POLARITY_ADDITIVE;
                    new_additive = true;
                    comp_bits[0] = 8;
                    comp_shift[0] = 0;
                    break;
                case 3:
                    if (pdev->sep_device) {
                        pdevproto = (pdf14_device *)&gs_pdf14_RGBspot_device;
                        new_14procs = &rgbspot_pdf14_procs;
                    } else {
                        pdevproto = (pdf14_device *)&gs_pdf14_RGB_device;
                        new_14procs = &rgb_pdf14_procs;
                    }
                    new_polarity = GX_CINFO_POLARITY_ADDITIVE;
                    new_additive = true;
                    for (k = 0; k < 3; k++) {
                        comp_bits[k] = 8;
                        comp_shift[k] = (2-k)*8;
                    }
                    break;
                case 4:
                    if (pdev->sep_device) {
                        pdevproto = (pdf14_device *)&gs_pdf14_CMYKspot_device;
                        new_14procs = &cmykspot_pdf14_procs;
                    } else {
                        pdevproto = (pdf14_device *)&gs_pdf14_CMYK_device;
                        new_14procs = &cmyk_pdf14_procs;
                    }
                    new_polarity = GX_CINFO_POLARITY_SUBTRACTIVE;
                    new_additive = false;
                    for (k = 0; k < 4; k++) {
                        comp_bits[k] = 8;
                        comp_shift[k] = (3-k)*8;
                    }
                    break;
                default:
                    return gs_throw(gs_error_undefinedresult,
                                    "ICC Number of colorants illegal");
                }
            }
            break;
        case UNKNOWN:
            return 0;
            break;
        default:
            return_error(gs_error_rangecheck);
            break;
     }
    if (update_color_info){
        if (pdev->sep_device && !is_mask) {
            int num_spots;

            if (icc_profile_dev == NULL)
                return_error(gs_error_undefined);

            num_spots = pdev->color_info.num_components - icc_profile_dev->num_comps;

            if (num_spots > 0) {
                new_num_comps += num_spots;
                for (k = 0; k < new_num_comps; k++) {
                    comp_bits[k] = 8;
                    comp_shift[k] = (new_num_comps - k - 1) * 8;
                }
                new_depth = 8 * new_num_comps;
            }
        }
        if (has_tags) {
            new_depth += 8;
        }
        if_debug2m('v', pdev->memory,
                   "[v]pdf14_update_device_color_procs_push_c,num_components_old = %d num_components_new = %d\n",
                   pdev->color_info.num_components,new_num_comps);
        /* Set new information in the device */
        set_dev_proc(pdev, get_color_mapping_procs, pdevproto->static_procs->get_color_mapping_procs);
        set_dev_proc(pdev, get_color_comp_index, pdevproto->static_procs->get_color_comp_index);
        pdev->blend_procs = pdevproto->blend_procs;
        pdev->color_info.polarity = new_polarity;
        pdev->color_info.num_components = new_num_comps;
        pdev->color_info.max_color = 255;
        pdev->color_info.max_gray = 255;
        pdev->pdf14_procs = new_14procs;
        pdev->color_info.depth = new_depth;
        memset(&(pdev->color_info.comp_bits),0,GX_DEVICE_COLOR_MAX_COMPONENTS);
        memset(&(pdev->color_info.comp_shift),0,GX_DEVICE_COLOR_MAX_COMPONENTS);
        memcpy(&(pdev->color_info.comp_bits), comp_bits, new_num_comps);
        memcpy(&(pdev->color_info.comp_shift), comp_shift, new_num_comps);
        pdev->color_info.comp_shift[new_num_comps] = new_depth - 8;	/* in case we has_tags is set */

        /* If we have a compressed color codec, and we are doing a soft mask
           push operation then go ahead and update the color encode and
           decode for the pdf14 device to not used compressed color
           encoding while in the soft mask.  We will just check for gray
           and compressed.  Note that we probably don't have_tags if we
           are dealing with compressed color.  But is is possible so
           we add it in to catch for future use. */
        cldev->clist_color_info.depth = pdev->color_info.depth;
        cldev->clist_color_info.polarity = pdev->color_info.polarity;
        cldev->clist_color_info.num_components = pdev->color_info.num_components;
        cldev->clist_color_info.max_color = pdev->color_info.max_color;
        cldev->clist_color_info.max_gray = pdev->color_info.max_gray;
        /* For the ICC profiles, we want to update the ICC profile for the
           device in the ICC manager.  We already stored in in pdf14_parent_color_t.
           That will be stored in the clist and restored during the reading phase. */
        if (group_color == ICC) {
            gsicc_adjust_profile_rc(icc_profile, 1, "pdf14_update_device_color_procs_push_c");
            gsicc_adjust_profile_rc(dev->icc_struct->device_profile[0], -1, "pdf14_update_device_color_procs_push_c");
            dev->icc_struct->device_profile[0] = icc_profile;
        }
        if (pdev->ctx) {
           pdev->ctx->additive = new_additive;
        }
        return(1);  /* Lets us detect that we did do an update */
    }
    if_debug0m('v', pdev->memory, "[v]procs not updated\n");
    return 0;
}


x_catch_free_colors(Display * dpy, XErrorEvent * err)
{
    if (err->request_code == X_FreeColors ||
        x_error_handler.orighandler == x_catch_free_colors)
        return 0;
    return x_error_handler.orighandler(dpy, err);
}


pdf14_clist_text_begin(gx_device * dev,	gs_gstate	* pgs,
                 const gs_text_params_t * text, gs_font * font,
                 gx_path * path, const gx_device_color * pdcolor,
                 const gx_clip_path * pcpath, gs_memory_t * memory,
                 gs_text_enum_t ** ppenum)
{
    pdf14_clist_device * pdev = (pdf14_clist_device *)dev;
    gs_text_enum_t *penum;
    int code;
    gs_blend_mode_t blend_mode = gs_currentblendmode(pgs);
    float opacity = gs_currentopacityalpha(pgs);
    bool blend_issue = !(blend_mode == BLEND_MODE_Normal || blend_mode == BLEND_MODE_Compatible);
    bool draw = !(text->operation & TEXT_DO_NONE);

    if_debug0m('v', memory, "[v]pdf14_clist_text_begin\n");
    /*
     * Ensure that that the PDF 1.4 reading compositor will have the current
     * blending parameters.  This is needed since the fill_rectangle routines
     * do not have access to the gs_gstate.  Thus we have to pass any
     * changes explictly.
     */
    code = pdf14_clist_update_params(pdev, pgs, false, NULL);
    if (code < 0)
        return code;
    /* Pass text_begin to the target */
    code = gx_forward_text_begin(dev, pgs, text, font, path,
                                pdcolor, pcpath, memory, &penum);
    if (code < 0)
        return code;

    /* We may need to push a non-isolated transparency group if the following
    is true.
    1) We are not currently in one that we pushed for text.  This is
    is determined by looking at the pdf14 device.
    2) The blend mode is not Normal or the opacity is not 1.0
    3) Text knockout is set to true
    4) And we are actually drawing text
    */
    if (gs_currenttextknockout(pgs) && (blend_issue || opacity != 1.0) &&
        gs_currenttextrenderingmode(pgs) != 3 && /* don't bother with invisible text */
        pdev->text_group == PDF14_TEXTGROUP_BT_NOT_PUSHED) {
        if (draw) {
            code = pdf14_push_text_group(dev, pgs, path, pcpath, blend_mode, opacity, true);
            if (code == 0)
                pdev->text_group = PDF14_TEXTGROUP_BT_PUSHED;  /* Needed during clist writing */
        }
    }
    *ppenum = (gs_text_enum_t *)penum;
    return code;
}


c_pdf14trans_is_friendly(const gs_composite_t * composite_action, byte cmd0, byte cmd1)
{
    gs_pdf14trans_t *pct0 = (gs_pdf14trans_t *)composite_action;
    int op0 = pct0->params.pdf14_op;

    if (op0 == PDF14_PUSH_DEVICE || op0 == PDF14_END_TRANS_GROUP ||
        op0 == PDF14_END_TRANS_TEXT_GROUP) {
        /* Halftone commands are always passed to the target printer device,
           because transparency buffers are always contone.
           So we're safe to execute them before queued transparency compositors. */
        if (cmd0 == cmd_opv_extend && (cmd1 == cmd_opv_ext_put_halftone ||
                                       cmd1 == cmd_opv_ext_put_ht_seg))
            return true;
        if (cmd0 == cmd_opv_set_misc && (cmd1 >> 6) == (cmd_set_misc_map >> 6))
            return true;
    }
    return false;
}