MagickCore  6.7.5
property.c
Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %            PPPP    RRRR    OOO   PPPP   EEEEE  RRRR   TTTTT  Y   Y          %
00007 %            P   P   R   R  O   O  P   P  E      R   R    T     Y Y           %
00008 %            PPPP    RRRR   O   O  PPPP   EEE    RRRR     T      Y            %
00009 %            P       R R    O   O  P      E      R R      T      Y            %
00010 %            P       R  R    OOO   P      EEEEE  R  R     T      Y            %
00011 %                                                                             %
00012 %                                                                             %
00013 %                         MagickCore Property Methods                         %
00014 %                                                                             %
00015 %                              Software Design                                %
00016 %                                John Cristy                                  %
00017 %                                 March 2000                                  %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
00021 %  dedicated to making software imaging solutions freely available.           %
00022 %                                                                             %
00023 %  You may not use this file except in compliance with the License.  You may  %
00024 %  obtain a copy of the License at                                            %
00025 %                                                                             %
00026 %    http://www.imagemagick.org/script/license.php                            %
00027 %                                                                             %
00028 %  Unless required by applicable law or agreed to in writing, software        %
00029 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00030 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00031 %  See the License for the specific language governing permissions and        %
00032 %  limitations under the License.                                             %
00033 %                                                                             %
00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00035 %
00036 %
00037 %
00038 */
00039 
00040 /*
00041   Include declarations.
00042 */
00043 #include "MagickCore/studio.h"
00044 #include "MagickCore/attribute.h"
00045 #include "MagickCore/cache.h"
00046 #include "MagickCore/color.h"
00047 #include "MagickCore/compare.h"
00048 #include "MagickCore/constitute.h"
00049 #include "MagickCore/draw.h"
00050 #include "MagickCore/effect.h"
00051 #include "MagickCore/exception.h"
00052 #include "MagickCore/exception-private.h"
00053 #include "MagickCore/fx.h"
00054 #include "MagickCore/fx-private.h"
00055 #include "MagickCore/gem.h"
00056 #include "MagickCore/geometry.h"
00057 #include "MagickCore/histogram.h"
00058 #include "MagickCore/image.h"
00059 #include "MagickCore/image.h"
00060 #include "MagickCore/layer.h"
00061 #include "MagickCore/locale-private.h"
00062 #include "MagickCore/list.h"
00063 #include "MagickCore/magick.h"
00064 #include "MagickCore/memory_.h"
00065 #include "MagickCore/monitor.h"
00066 #include "MagickCore/montage.h"
00067 #include "MagickCore/option.h"
00068 #include "MagickCore/profile.h"
00069 #include "MagickCore/property.h"
00070 #include "MagickCore/quantum.h"
00071 #include "MagickCore/resource_.h"
00072 #include "MagickCore/splay-tree.h"
00073 #include "MagickCore/signature.h"
00074 #include "MagickCore/statistic.h"
00075 #include "MagickCore/string_.h"
00076 #include "MagickCore/string-private.h"
00077 #include "MagickCore/token.h"
00078 #include "MagickCore/token-private.h"
00079 #include "MagickCore/utility.h"
00080 #include "MagickCore/utility-private.h"
00081 #include "MagickCore/version.h"
00082 #include "MagickCore/xml-tree.h"
00083 #include "MagickCore/xml-tree-private.h"
00084 
00085 /*
00086 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00087 %                                                                             %
00088 %                                                                             %
00089 %                                                                             %
00090 %   C l o n e I m a g e P r o p e r t i e s                                   %
00091 %                                                                             %
00092 %                                                                             %
00093 %                                                                             %
00094 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00095 %
00096 %  CloneImageProperties() clones one or more image properties.
00097 %
00098 %  The format of the CloneImageProperties method is:
00099 %
00100 %      MagickBooleanType CloneImageProperties(Image *image,
00101 %        const Image *clone_image)
00102 %
00103 %  A description of each parameter follows:
00104 %
00105 %    o image: the image.
00106 %
00107 %    o clone_image: the clone image.
00108 %
00109 */
00110 MagickExport MagickBooleanType CloneImageProperties(Image *image,
00111   const Image *clone_image)
00112 {
00113   assert(image != (Image *) NULL);
00114   assert(image->signature == MagickSignature);
00115   if (image->debug != MagickFalse)
00116     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00117   assert(clone_image != (const Image *) NULL);
00118   assert(clone_image->signature == MagickSignature);
00119   if (clone_image->debug != MagickFalse)
00120     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
00121       clone_image->filename);
00122   (void) CopyMagickString(image->filename,clone_image->filename,MaxTextExtent);
00123   (void) CopyMagickString(image->magick_filename,clone_image->magick_filename,
00124     MaxTextExtent);
00125   image->compression=clone_image->compression;
00126   image->quality=clone_image->quality;
00127   image->depth=clone_image->depth;
00128   image->background_color=clone_image->background_color;
00129   image->border_color=clone_image->border_color;
00130   image->matte_color=clone_image->matte_color;
00131   image->transparent_color=clone_image->transparent_color;
00132   image->gamma=clone_image->gamma;
00133   image->chromaticity=clone_image->chromaticity;
00134   image->rendering_intent=clone_image->rendering_intent;
00135   image->black_point_compensation=clone_image->black_point_compensation;
00136   image->units=clone_image->units;
00137   image->montage=(char *) NULL;
00138   image->directory=(char *) NULL;
00139   (void) CloneString(&image->geometry,clone_image->geometry);
00140   image->offset=clone_image->offset;
00141   image->resolution.x=clone_image->resolution.x;
00142   image->resolution.y=clone_image->resolution.y;
00143   image->page=clone_image->page;
00144   image->tile_offset=clone_image->tile_offset;
00145   image->extract_info=clone_image->extract_info;
00146   image->bias=clone_image->bias;
00147   image->filter=clone_image->filter;
00148   image->blur=clone_image->blur;
00149   image->fuzz=clone_image->fuzz;
00150   image->interlace=clone_image->interlace;
00151   image->interpolate=clone_image->interpolate;
00152   image->endian=clone_image->endian;
00153   image->gravity=clone_image->gravity;
00154   image->compose=clone_image->compose;
00155   image->scene=clone_image->scene;
00156   image->orientation=clone_image->orientation;
00157   image->dispose=clone_image->dispose;
00158   image->delay=clone_image->delay;
00159   image->ticks_per_second=clone_image->ticks_per_second;
00160   image->iterations=clone_image->iterations;
00161   image->total_colors=clone_image->total_colors;
00162   image->taint=clone_image->taint;
00163   image->progress_monitor=clone_image->progress_monitor;
00164   image->client_data=clone_image->client_data;
00165   image->start_loop=clone_image->start_loop;
00166   image->error=clone_image->error;
00167   image->signature=clone_image->signature;
00168   if (clone_image->properties != (void *) NULL)
00169     {
00170       if (image->properties != (void *) NULL)
00171         DestroyImageProperties(image);
00172       image->properties=CloneSplayTree((SplayTreeInfo *)
00173         clone_image->properties,(void *(*)(void *)) ConstantString,
00174         (void *(*)(void *)) ConstantString);
00175     }
00176   return(MagickTrue);
00177 }
00178 
00179 /*
00180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00181 %                                                                             %
00182 %                                                                             %
00183 %                                                                             %
00184 %   D e f i n e I m a g e P r o p e r t y                                     %
00185 %                                                                             %
00186 %                                                                             %
00187 %                                                                             %
00188 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00189 %
00190 %  DefineImageProperty() associates a key/value pair with an image property.
00191 %
00192 %  The format of the DefineImageProperty method is:
00193 %
00194 %      MagickBooleanType DefineImageProperty(Image *image,
00195 %        const char *property,ExceptionInfo *exception)
00196 %
00197 %  A description of each parameter follows:
00198 %
00199 %    o image: the image.
00200 %
00201 %    o property: the image property.
00202 %
00203 %    o exception: return any errors or warnings in this structure.
00204 %
00205 */
00206 MagickExport MagickBooleanType DefineImageProperty(Image *image,
00207   const char *property,ExceptionInfo *exception)
00208 {
00209   char
00210     key[MaxTextExtent],
00211     value[MaxTextExtent];
00212 
00213   register char
00214     *p;
00215 
00216   assert(image != (Image *) NULL);
00217   assert(property != (const char *) NULL);
00218   (void) CopyMagickString(key,property,MaxTextExtent-1);
00219   for (p=key; *p != '\0'; p++)
00220     if (*p == '=')
00221       break;
00222   *value='\0';
00223   if (*p == '=')
00224     (void) CopyMagickString(value,p+1,MaxTextExtent);
00225   *p='\0';
00226   return(SetImageProperty(image,key,value,exception));
00227 }
00228 
00229 /*
00230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00231 %                                                                             %
00232 %                                                                             %
00233 %                                                                             %
00234 %   D e l e t e I m a g e P r o p e r t y                                     %
00235 %                                                                             %
00236 %                                                                             %
00237 %                                                                             %
00238 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00239 %
00240 %  DeleteImageProperty() deletes an image property.
00241 %
00242 %  The format of the DeleteImageProperty method is:
00243 %
00244 %      MagickBooleanType DeleteImageProperty(Image *image,const char *property)
00245 %
00246 %  A description of each parameter follows:
00247 %
00248 %    o image: the image.
00249 %
00250 %    o property: the image property.
00251 %
00252 */
00253 MagickExport MagickBooleanType DeleteImageProperty(Image *image,
00254   const char *property)
00255 {
00256   assert(image != (Image *) NULL);
00257   assert(image->signature == MagickSignature);
00258   if (image->debug != MagickFalse)
00259     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
00260       image->filename);
00261   if (image->properties == (void *) NULL)
00262     return(MagickFalse);
00263   return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->properties,property));
00264 }
00265 
00266 /*
00267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00268 %                                                                             %
00269 %                                                                             %
00270 %                                                                             %
00271 %   D e s t r o y I m a g e P r o p e r t i e s                               %
00272 %                                                                             %
00273 %                                                                             %
00274 %                                                                             %
00275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00276 %
00277 %  DestroyImageProperties() releases memory associated with image property
00278 %  values.
00279 %
00280 %  The format of the DestroyDefines method is:
00281 %
00282 %      void DestroyImageProperties(Image *image)
00283 %
00284 %  A description of each parameter follows:
00285 %
00286 %    o image: the image.
00287 %
00288 */
00289 MagickExport void DestroyImageProperties(Image *image)
00290 {
00291   assert(image != (Image *) NULL);
00292   assert(image->signature == MagickSignature);
00293   if (image->debug != MagickFalse)
00294     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
00295       image->filename);
00296   if (image->properties != (void *) NULL)
00297     image->properties=(void *) DestroySplayTree((SplayTreeInfo *)
00298       image->properties);
00299 }
00300 
00301 /*
00302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00303 %                                                                             %
00304 %                                                                             %
00305 %                                                                             %
00306 %  F o r m a t I m a g e P r o p e r t y                                      %
00307 %                                                                             %
00308 %                                                                             %
00309 %                                                                             %
00310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00311 %
00312 %  FormatImageProperty() permits formatted property/value pairs to be saved as
00313 %  an image property.
00314 %
00315 %  The format of the FormatImageProperty method is:
00316 %
00317 %      MagickBooleanType FormatImageProperty(Image *image,const char *property,
00318 %        const char *format,...)
00319 %
00320 %  A description of each parameter follows.
00321 %
00322 %   o  image:  The image.
00323 %
00324 %   o  property:  The attribute property.
00325 %
00326 %   o  format:  A string describing the format to use to write the remaining
00327 %      arguments.
00328 %
00329 */
00330 MagickExport MagickBooleanType FormatImageProperty(Image *image,
00331   const char *property,const char *format,...)
00332 {
00333   char
00334     value[MaxTextExtent];
00335 
00336   ExceptionInfo
00337     *exception;
00338 
00339   MagickBooleanType
00340     status;
00341 
00342   ssize_t
00343     n;
00344 
00345   va_list
00346     operands;
00347 
00348   va_start(operands,format);
00349   n=FormatLocaleStringList(value,MaxTextExtent,format,operands);
00350   (void) n;
00351   va_end(operands);
00352   exception=AcquireExceptionInfo();
00353   status=SetImageProperty(image,property,value,exception);
00354   exception=DestroyExceptionInfo(exception);
00355   return(status);
00356 }
00357 
00358 /*
00359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00360 %                                                                             %
00361 %                                                                             %
00362 %                                                                             %
00363 %   G e t I m a g e P r o p e r t y                                           %
00364 %                                                                             %
00365 %                                                                             %
00366 %                                                                             %
00367 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00368 %
00369 %  GetImageProperty() gets a value associated with an image property.
00370 %
00371 %  The format of the GetImageProperty method is:
00372 %
00373 %      const char *GetImageProperty(const Image *image,const char *key,
00374 %        ExceptionInfo *exception)
00375 %
00376 %  A description of each parameter follows:
00377 %
00378 %    o image: the image.
00379 %
00380 %    o key: the key.
00381 %
00382 %    o exception: return any errors or warnings in this structure.
00383 %
00384 */
00385 
00386 static char
00387   *TracePSClippath(const unsigned char *,size_t,const size_t,
00388     const size_t),
00389   *TraceSVGClippath(const unsigned char *,size_t,const size_t,
00390     const size_t);
00391 
00392 static MagickBooleanType GetIPTCProperty(const Image *image,const char *key,
00393   ExceptionInfo *exception)
00394 {
00395   char
00396     *attribute,
00397     *message;
00398 
00399   const StringInfo
00400     *profile;
00401 
00402   long
00403     count,
00404     dataset,
00405     record;
00406 
00407   register ssize_t
00408     i;
00409 
00410   size_t
00411     length;
00412 
00413   profile=GetImageProfile(image,"iptc");
00414   if (profile == (StringInfo *) NULL)
00415     profile=GetImageProfile(image,"8bim");
00416   if (profile == (StringInfo *) NULL)
00417     return(MagickFalse);
00418   count=sscanf(key,"IPTC:%ld:%ld",&dataset,&record);
00419   if (count != 2)
00420     return(MagickFalse);
00421   attribute=(char *) NULL;
00422   for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=(ssize_t) length)
00423   {
00424     length=1;
00425     if ((ssize_t) GetStringInfoDatum(profile)[i] != 0x1c)
00426       continue;
00427     length=(size_t) (GetStringInfoDatum(profile)[i+3] << 8);
00428     length|=GetStringInfoDatum(profile)[i+4];
00429     if (((long) GetStringInfoDatum(profile)[i+1] == dataset) &&
00430         ((long) GetStringInfoDatum(profile)[i+2] == record))
00431       {
00432         message=(char *) NULL;
00433         if (~length >= 1)
00434           message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message));
00435         if (message != (char *) NULL)
00436           {
00437             (void) CopyMagickString(message,(char *) GetStringInfoDatum(
00438               profile)+i+5,length+1);
00439             (void) ConcatenateString(&attribute,message);
00440             (void) ConcatenateString(&attribute,";");
00441             message=DestroyString(message);
00442           }
00443       }
00444     i+=5;
00445   }
00446   if ((attribute == (char *) NULL) || (*attribute == ';'))
00447     {
00448       if (attribute != (char *) NULL)
00449         attribute=DestroyString(attribute);
00450       return(MagickFalse);
00451     }
00452   attribute[strlen(attribute)-1]='\0';
00453   (void) SetImageProperty((Image *) image,key,(const char *) attribute,
00454     exception);
00455   attribute=DestroyString(attribute);
00456   return(MagickTrue);
00457 }
00458 
00459 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
00460 {
00461   if (x > y)
00462     return(x);
00463   return(y);
00464 }
00465 
00466 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
00467 {
00468   if (x < y)
00469     return(x);
00470   return(y);
00471 }
00472 
00473 static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
00474 {
00475   int
00476     c;
00477 
00478   if (*length < 1)
00479     return(EOF);
00480   c=(int) (*(*p)++);
00481   (*length)--;
00482   return(c);
00483 }
00484 
00485 static inline size_t ReadPropertyMSBLong(const unsigned char **p,
00486   size_t *length)
00487 {
00488   int
00489     c;
00490 
00491   register ssize_t
00492     i;
00493 
00494   unsigned char
00495     buffer[4];
00496 
00497   size_t
00498     value;
00499 
00500   if (*length < 4)
00501     return(~0UL);
00502   for (i=0; i < 4; i++)
00503   {
00504     c=(int) (*(*p)++);
00505     (*length)--;
00506     buffer[i]=(unsigned char) c;
00507   }
00508   value=(size_t) (buffer[0] << 24);
00509   value|=buffer[1] << 16;
00510   value|=buffer[2] << 8;
00511   value|=buffer[3];
00512   return(value & 0xffffffff);
00513 }
00514 
00515 static inline unsigned short ReadPropertyMSBShort(const unsigned char **p,
00516   size_t *length)
00517 {
00518   int
00519     c;
00520 
00521   register ssize_t
00522     i;
00523 
00524   unsigned char
00525     buffer[2];
00526 
00527   unsigned short
00528     value;
00529 
00530   if (*length < 2)
00531     return((unsigned short) ~0U);
00532   for (i=0; i < 2; i++)
00533   {
00534     c=(int) (*(*p)++);
00535     (*length)--;
00536     buffer[i]=(unsigned char) c;
00537   }
00538   value=(unsigned short) (buffer[0] << 8);
00539   value|=buffer[1];
00540   return((unsigned short) (value & 0xffff));
00541 }
00542 
00543 static MagickBooleanType Get8BIMProperty(const Image *image,const char *key,
00544   ExceptionInfo *exception)
00545 {
00546   char
00547     *attribute,
00548     format[MaxTextExtent],
00549     name[MaxTextExtent],
00550     *resource;
00551 
00552   const StringInfo
00553     *profile;
00554 
00555   const unsigned char
00556     *info;
00557 
00558   long
00559     start,
00560     stop;
00561 
00562   MagickBooleanType
00563     status;
00564 
00565   register ssize_t
00566     i;
00567 
00568   ssize_t
00569     count,
00570     id,
00571     sub_number;
00572 
00573   size_t
00574     length;
00575 
00576   /*
00577     There are no newlines in path names, so it's safe as terminator.
00578   */
00579   profile=GetImageProfile(image,"8bim");
00580   if (profile == (StringInfo *) NULL)
00581     return(MagickFalse);
00582   count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%[^\n]\n%[^\n]",&start,&stop,name,
00583     format);
00584   if ((count != 2) && (count != 3) && (count != 4))
00585     return(MagickFalse);
00586   if (count < 4)
00587     (void) CopyMagickString(format,"SVG",MaxTextExtent);
00588   if (count < 3)
00589     *name='\0';
00590   sub_number=1;
00591   if (*name == '#')
00592     sub_number=(ssize_t) StringToLong(&name[1]);
00593   sub_number=MagickMax(sub_number,1L);
00594   resource=(char *) NULL;
00595   status=MagickFalse;
00596   length=GetStringInfoLength(profile);
00597   info=GetStringInfoDatum(profile);
00598   while ((length > 0) && (status == MagickFalse))
00599   {
00600     if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
00601       continue;
00602     if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
00603       continue;
00604     if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
00605       continue;
00606     if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
00607       continue;
00608     id=(ssize_t) ((int) ReadPropertyMSBShort(&info,&length));
00609     if (id < (ssize_t) start)
00610       continue;
00611     if (id > (ssize_t) stop)
00612       continue;
00613     if (resource != (char *) NULL)
00614       resource=DestroyString(resource);
00615     count=(ssize_t) ReadPropertyByte(&info,&length);
00616     if ((count != 0) && ((size_t) count <= length))
00617       {
00618         resource=(char *) NULL;
00619         if (~(1UL*count) >= (MaxTextExtent-1))
00620           resource=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
00621             sizeof(*resource));
00622         if (resource != (char *) NULL)
00623           {
00624             for (i=0; i < (ssize_t) count; i++)
00625               resource[i]=(char) ReadPropertyByte(&info,&length);
00626             resource[count]='\0';
00627           }
00628       }
00629     if ((count & 0x01) == 0)
00630       (void) ReadPropertyByte(&info,&length);
00631     count=(ssize_t) ((int) ReadPropertyMSBLong(&info,&length));
00632     if ((*name != '\0') && (*name != '#'))
00633       if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0))
00634         {
00635           /*
00636             No name match, scroll forward and try next.
00637           */
00638           info+=count;
00639           length-=MagickMin(count,(ssize_t) length);
00640           continue;
00641         }
00642     if ((*name == '#') && (sub_number != 1))
00643       {
00644         /*
00645           No numbered match, scroll forward and try next.
00646         */
00647         sub_number--;
00648         info+=count;
00649         length-=MagickMin(count,(ssize_t) length);
00650         continue;
00651       }
00652     /*
00653       We have the resource of interest.
00654     */
00655     attribute=(char *) NULL;
00656     if (~(1UL*count) >= (MaxTextExtent-1))
00657       attribute=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
00658         sizeof(*attribute));
00659     if (attribute != (char *) NULL)
00660       {
00661         (void) CopyMagickMemory(attribute,(char *) info,(size_t) count);
00662         attribute[count]='\0';
00663         info+=count;
00664         length-=MagickMin(count,(ssize_t) length);
00665         if ((id <= 1999) || (id >= 2999))
00666           (void) SetImageProperty((Image *) image,key,(const char *)
00667             attribute,exception);
00668         else
00669           {
00670             char
00671               *path;
00672 
00673             if (LocaleCompare(format,"svg") == 0)
00674               path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
00675                 image->columns,image->rows);
00676             else
00677               path=TracePSClippath((unsigned char *) attribute,(size_t) count,
00678                 image->columns,image->rows);
00679             (void) SetImageProperty((Image *) image,key,(const char *) path,
00680               exception);
00681             path=DestroyString(path);
00682           }
00683         attribute=DestroyString(attribute);
00684         status=MagickTrue;
00685       }
00686   }
00687   if (resource != (char *) NULL)
00688     resource=DestroyString(resource);
00689   return(status);
00690 }
00691 
00692 static inline unsigned short ReadPropertyShort(const EndianType endian,
00693   const unsigned char *buffer)
00694 {
00695   unsigned short
00696     value;
00697 
00698   if (endian == MSBEndian)
00699     {
00700       value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
00701         ((unsigned char *) buffer)[1]);
00702       return((unsigned short) (value & 0xffff));
00703     }
00704   value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
00705   return((unsigned short) (value & 0xffff));
00706 }
00707 
00708 static inline size_t ReadPropertyLong(const EndianType endian,
00709   const unsigned char *buffer)
00710 {
00711   size_t
00712     value;
00713 
00714   if (endian == MSBEndian)
00715     {
00716       value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
00717         (buffer[2] << 8) | buffer[3]);
00718       return((size_t) (value & 0xffffffff));
00719     }
00720   value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
00721     (buffer[1] << 8 ) | (buffer[0]));
00722   return((size_t) (value & 0xffffffff));
00723 }
00724 
00725 static MagickBooleanType GetEXIFProperty(const Image *image,
00726   const char *property,ExceptionInfo *exception)
00727 {
00728 #define MaxDirectoryStack  16
00729 #define EXIF_DELIMITER  "\n"
00730 #define EXIF_NUM_FORMATS  12
00731 #define EXIF_FMT_BYTE  1
00732 #define EXIF_FMT_STRING  2
00733 #define EXIF_FMT_USHORT  3
00734 #define EXIF_FMT_ULONG  4
00735 #define EXIF_FMT_URATIONAL  5
00736 #define EXIF_FMT_SBYTE  6
00737 #define EXIF_FMT_UNDEFINED  7
00738 #define EXIF_FMT_SSHORT  8
00739 #define EXIF_FMT_SLONG  9
00740 #define EXIF_FMT_SRATIONAL  10
00741 #define EXIF_FMT_SINGLE  11
00742 #define EXIF_FMT_DOUBLE  12
00743 #define TAG_EXIF_OFFSET  0x8769
00744 #define TAG_GPS_OFFSET  0x8825
00745 #define TAG_INTEROP_OFFSET  0xa005
00746 
00747 #define EXIFMultipleValues(size,format,arg) \
00748 { \
00749    ssize_t \
00750      component; \
00751  \
00752    size_t \
00753      length; \
00754  \
00755    unsigned char \
00756      *p1; \
00757  \
00758    length=0; \
00759    p1=p; \
00760    for (component=0; component < components; component++) \
00761    { \
00762      length+=FormatLocaleString(buffer+length,MaxTextExtent-length, \
00763        format", ",arg); \
00764      if (length >= (MaxTextExtent-1)) \
00765        length=MaxTextExtent-1; \
00766      p1+=size; \
00767    } \
00768    if (length > 1) \
00769      buffer[length-2]='\0'; \
00770    value=AcquireString(buffer); \
00771 }
00772 
00773 #define EXIFMultipleFractions(size,format,arg1,arg2) \
00774 { \
00775    ssize_t \
00776      component; \
00777  \
00778    size_t \
00779      length; \
00780  \
00781    unsigned char \
00782      *p1; \
00783  \
00784    length=0; \
00785    p1=p; \
00786    for (component=0; component < components; component++) \
00787    { \
00788      length+=FormatLocaleString(buffer+length,MaxTextExtent-length, \
00789        format", ",arg1,arg2); \
00790      if (length >= (MaxTextExtent-1)) \
00791        length=MaxTextExtent-1; \
00792      p1+=size; \
00793    } \
00794    if (length > 1) \
00795      buffer[length-2]='\0'; \
00796    value=AcquireString(buffer); \
00797 }
00798 
00799   typedef struct _DirectoryInfo
00800   {
00801     const unsigned char
00802       *directory;
00803 
00804     size_t
00805       entry,
00806       offset;
00807   } DirectoryInfo;
00808 
00809   typedef struct _TagInfo
00810   {
00811     size_t
00812       tag;
00813 
00814     const char
00815       *description;
00816   } TagInfo;
00817 
00818   static TagInfo
00819     EXIFTag[] =
00820     {
00821       {  0x001, "exif:InteroperabilityIndex" },
00822       {  0x002, "exif:InteroperabilityVersion" },
00823       {  0x100, "exif:ImageWidth" },
00824       {  0x101, "exif:ImageLength" },
00825       {  0x102, "exif:BitsPerSample" },
00826       {  0x103, "exif:Compression" },
00827       {  0x106, "exif:PhotometricInterpretation" },
00828       {  0x10a, "exif:FillOrder" },
00829       {  0x10d, "exif:DocumentName" },
00830       {  0x10e, "exif:ImageDescription" },
00831       {  0x10f, "exif:Make" },
00832       {  0x110, "exif:Model" },
00833       {  0x111, "exif:StripOffsets" },
00834       {  0x112, "exif:Orientation" },
00835       {  0x115, "exif:SamplesPerPixel" },
00836       {  0x116, "exif:RowsPerStrip" },
00837       {  0x117, "exif:StripByteCounts" },
00838       {  0x11a, "exif:XResolution" },
00839       {  0x11b, "exif:YResolution" },
00840       {  0x11c, "exif:PlanarConfiguration" },
00841       {  0x11d, "exif:PageName" },
00842       {  0x11e, "exif:XPosition" },
00843       {  0x11f, "exif:YPosition" },
00844       {  0x118, "exif:MinSampleValue" },
00845       {  0x119, "exif:MaxSampleValue" },
00846       {  0x120, "exif:FreeOffsets" },
00847       {  0x121, "exif:FreeByteCounts" },
00848       {  0x122, "exif:GrayResponseUnit" },
00849       {  0x123, "exif:GrayResponseCurve" },
00850       {  0x124, "exif:T4Options" },
00851       {  0x125, "exif:T6Options" },
00852       {  0x128, "exif:ResolutionUnit" },
00853       {  0x12d, "exif:TransferFunction" },
00854       {  0x131, "exif:Software" },
00855       {  0x132, "exif:DateTime" },
00856       {  0x13b, "exif:Artist" },
00857       {  0x13e, "exif:WhitePoint" },
00858       {  0x13f, "exif:PrimaryChromaticities" },
00859       {  0x140, "exif:ColorMap" },
00860       {  0x141, "exif:HalfToneHints" },
00861       {  0x142, "exif:TileWidth" },
00862       {  0x143, "exif:TileLength" },
00863       {  0x144, "exif:TileOffsets" },
00864       {  0x145, "exif:TileByteCounts" },
00865       {  0x14a, "exif:SubIFD" },
00866       {  0x14c, "exif:InkSet" },
00867       {  0x14d, "exif:InkNames" },
00868       {  0x14e, "exif:NumberOfInks" },
00869       {  0x150, "exif:DotRange" },
00870       {  0x151, "exif:TargetPrinter" },
00871       {  0x152, "exif:ExtraSample" },
00872       {  0x153, "exif:SampleFormat" },
00873       {  0x154, "exif:SMinSampleValue" },
00874       {  0x155, "exif:SMaxSampleValue" },
00875       {  0x156, "exif:TransferRange" },
00876       {  0x157, "exif:ClipPath" },
00877       {  0x158, "exif:XClipPathUnits" },
00878       {  0x159, "exif:YClipPathUnits" },
00879       {  0x15a, "exif:Indexed" },
00880       {  0x15b, "exif:JPEGTables" },
00881       {  0x15f, "exif:OPIProxy" },
00882       {  0x200, "exif:JPEGProc" },
00883       {  0x201, "exif:JPEGInterchangeFormat" },
00884       {  0x202, "exif:JPEGInterchangeFormatLength" },
00885       {  0x203, "exif:JPEGRestartInterval" },
00886       {  0x205, "exif:JPEGLosslessPredictors" },
00887       {  0x206, "exif:JPEGPointTransforms" },
00888       {  0x207, "exif:JPEGQTables" },
00889       {  0x208, "exif:JPEGDCTables" },
00890       {  0x209, "exif:JPEGACTables" },
00891       {  0x211, "exif:YCbCrCoefficients" },
00892       {  0x212, "exif:YCbCrSubSampling" },
00893       {  0x213, "exif:YCbCrPositioning" },
00894       {  0x214, "exif:ReferenceBlackWhite" },
00895       {  0x2bc, "exif:ExtensibleMetadataPlatform" },
00896       {  0x301, "exif:Gamma" },
00897       {  0x302, "exif:ICCProfileDescriptor" },
00898       {  0x303, "exif:SRGBRenderingIntent" },
00899       {  0x320, "exif:ImageTitle" },
00900       {  0x5001, "exif:ResolutionXUnit" },
00901       {  0x5002, "exif:ResolutionYUnit" },
00902       {  0x5003, "exif:ResolutionXLengthUnit" },
00903       {  0x5004, "exif:ResolutionYLengthUnit" },
00904       {  0x5005, "exif:PrintFlags" },
00905       {  0x5006, "exif:PrintFlagsVersion" },
00906       {  0x5007, "exif:PrintFlagsCrop" },
00907       {  0x5008, "exif:PrintFlagsBleedWidth" },
00908       {  0x5009, "exif:PrintFlagsBleedWidthScale" },
00909       {  0x500A, "exif:HalftoneLPI" },
00910       {  0x500B, "exif:HalftoneLPIUnit" },
00911       {  0x500C, "exif:HalftoneDegree" },
00912       {  0x500D, "exif:HalftoneShape" },
00913       {  0x500E, "exif:HalftoneMisc" },
00914       {  0x500F, "exif:HalftoneScreen" },
00915       {  0x5010, "exif:JPEGQuality" },
00916       {  0x5011, "exif:GridSize" },
00917       {  0x5012, "exif:ThumbnailFormat" },
00918       {  0x5013, "exif:ThumbnailWidth" },
00919       {  0x5014, "exif:ThumbnailHeight" },
00920       {  0x5015, "exif:ThumbnailColorDepth" },
00921       {  0x5016, "exif:ThumbnailPlanes" },
00922       {  0x5017, "exif:ThumbnailRawBytes" },
00923       {  0x5018, "exif:ThumbnailSize" },
00924       {  0x5019, "exif:ThumbnailCompressedSize" },
00925       {  0x501a, "exif:ColorTransferFunction" },
00926       {  0x501b, "exif:ThumbnailData" },
00927       {  0x5020, "exif:ThumbnailImageWidth" },
00928       {  0x5021, "exif:ThumbnailImageHeight" },
00929       {  0x5022, "exif:ThumbnailBitsPerSample" },
00930       {  0x5023, "exif:ThumbnailCompression" },
00931       {  0x5024, "exif:ThumbnailPhotometricInterp" },
00932       {  0x5025, "exif:ThumbnailImageDescription" },
00933       {  0x5026, "exif:ThumbnailEquipMake" },
00934       {  0x5027, "exif:ThumbnailEquipModel" },
00935       {  0x5028, "exif:ThumbnailStripOffsets" },
00936       {  0x5029, "exif:ThumbnailOrientation" },
00937       {  0x502a, "exif:ThumbnailSamplesPerPixel" },
00938       {  0x502b, "exif:ThumbnailRowsPerStrip" },
00939       {  0x502c, "exif:ThumbnailStripBytesCount" },
00940       {  0x502d, "exif:ThumbnailResolutionX" },
00941       {  0x502e, "exif:ThumbnailResolutionY" },
00942       {  0x502f, "exif:ThumbnailPlanarConfig" },
00943       {  0x5030, "exif:ThumbnailResolutionUnit" },
00944       {  0x5031, "exif:ThumbnailTransferFunction" },
00945       {  0x5032, "exif:ThumbnailSoftwareUsed" },
00946       {  0x5033, "exif:ThumbnailDateTime" },
00947       {  0x5034, "exif:ThumbnailArtist" },
00948       {  0x5035, "exif:ThumbnailWhitePoint" },
00949       {  0x5036, "exif:ThumbnailPrimaryChromaticities" },
00950       {  0x5037, "exif:ThumbnailYCbCrCoefficients" },
00951       {  0x5038, "exif:ThumbnailYCbCrSubsampling" },
00952       {  0x5039, "exif:ThumbnailYCbCrPositioning" },
00953       {  0x503A, "exif:ThumbnailRefBlackWhite" },
00954       {  0x503B, "exif:ThumbnailCopyRight" },
00955       {  0x5090, "exif:LuminanceTable" },
00956       {  0x5091, "exif:ChrominanceTable" },
00957       {  0x5100, "exif:FrameDelay" },
00958       {  0x5101, "exif:LoopCount" },
00959       {  0x5110, "exif:PixelUnit" },
00960       {  0x5111, "exif:PixelPerUnitX" },
00961       {  0x5112, "exif:PixelPerUnitY" },
00962       {  0x5113, "exif:PaletteHistogram" },
00963       {  0x1000, "exif:RelatedImageFileFormat" },
00964       {  0x1001, "exif:RelatedImageLength" },
00965       {  0x1002, "exif:RelatedImageWidth" },
00966       {  0x800d, "exif:ImageID" },
00967       {  0x80e3, "exif:Matteing" },
00968       {  0x80e4, "exif:DataType" },
00969       {  0x80e5, "exif:ImageDepth" },
00970       {  0x80e6, "exif:TileDepth" },
00971       {  0x828d, "exif:CFARepeatPatternDim" },
00972       {  0x828e, "exif:CFAPattern2" },
00973       {  0x828f, "exif:BatteryLevel" },
00974       {  0x8298, "exif:Copyright" },
00975       {  0x829a, "exif:ExposureTime" },
00976       {  0x829d, "exif:FNumber" },
00977       {  0x83bb, "exif:IPTC/NAA" },
00978       {  0x84e3, "exif:IT8RasterPadding" },
00979       {  0x84e5, "exif:IT8ColorTable" },
00980       {  0x8649, "exif:ImageResourceInformation" },
00981       {  0x8769, "exif:ExifOffset" },
00982       {  0x8773, "exif:InterColorProfile" },
00983       {  0x8822, "exif:ExposureProgram" },
00984       {  0x8824, "exif:SpectralSensitivity" },
00985       {  0x8825, "exif:GPSInfo" },
00986       {  0x8827, "exif:ISOSpeedRatings" },
00987       {  0x8828, "exif:OECF" },
00988       {  0x8829, "exif:Interlace" },
00989       {  0x882a, "exif:TimeZoneOffset" },
00990       {  0x882b, "exif:SelfTimerMode" },
00991       {  0x9000, "exif:ExifVersion" },
00992       {  0x9003, "exif:DateTimeOriginal" },
00993       {  0x9004, "exif:DateTimeDigitized" },
00994       {  0x9101, "exif:ComponentsConfiguration" },
00995       {  0x9102, "exif:CompressedBitsPerPixel" },
00996       {  0x9201, "exif:ShutterSpeedValue" },
00997       {  0x9202, "exif:ApertureValue" },
00998       {  0x9203, "exif:BrightnessValue" },
00999       {  0x9204, "exif:ExposureBiasValue" },
01000       {  0x9205, "exif:MaxApertureValue" },
01001       {  0x9206, "exif:SubjectDistance" },
01002       {  0x9207, "exif:MeteringMode" },
01003       {  0x9208, "exif:LightSource" },
01004       {  0x9209, "exif:Flash" },
01005       {  0x920a, "exif:FocalLength" },
01006       {  0x920b, "exif:FlashEnergy" },
01007       {  0x920c, "exif:SpatialFrequencyResponse" },
01008       {  0x920d, "exif:Noise" },
01009       {  0x9211, "exif:ImageNumber" },
01010       {  0x9212, "exif:SecurityClassification" },
01011       {  0x9213, "exif:ImageHistory" },
01012       {  0x9214, "exif:SubjectArea" },
01013       {  0x9215, "exif:ExposureIndex" },
01014       {  0x9216, "exif:TIFF-EPStandardID" },
01015       {  0x927c, "exif:MakerNote" },
01016       {  0x9C9b, "exif:WinXP-Title" },
01017       {  0x9C9c, "exif:WinXP-Comments" },
01018       {  0x9C9d, "exif:WinXP-Author" },
01019       {  0x9C9e, "exif:WinXP-Keywords" },
01020       {  0x9C9f, "exif:WinXP-Subject" },
01021       {  0x9286, "exif:UserComment" },
01022       {  0x9290, "exif:SubSecTime" },
01023       {  0x9291, "exif:SubSecTimeOriginal" },
01024       {  0x9292, "exif:SubSecTimeDigitized" },
01025       {  0xa000, "exif:FlashPixVersion" },
01026       {  0xa001, "exif:ColorSpace" },
01027       {  0xa002, "exif:ExifImageWidth" },
01028       {  0xa003, "exif:ExifImageLength" },
01029       {  0xa004, "exif:RelatedSoundFile" },
01030       {  0xa005, "exif:InteroperabilityOffset" },
01031       {  0xa20b, "exif:FlashEnergy" },
01032       {  0xa20c, "exif:SpatialFrequencyResponse" },
01033       {  0xa20d, "exif:Noise" },
01034       {  0xa20e, "exif:FocalPlaneXResolution" },
01035       {  0xa20f, "exif:FocalPlaneYResolution" },
01036       {  0xa210, "exif:FocalPlaneResolutionUnit" },
01037       {  0xa214, "exif:SubjectLocation" },
01038       {  0xa215, "exif:ExposureIndex" },
01039       {  0xa216, "exif:TIFF/EPStandardID" },
01040       {  0xa217, "exif:SensingMethod" },
01041       {  0xa300, "exif:FileSource" },
01042       {  0xa301, "exif:SceneType" },
01043       {  0xa302, "exif:CFAPattern" },
01044       {  0xa401, "exif:CustomRendered" },
01045       {  0xa402, "exif:ExposureMode" },
01046       {  0xa403, "exif:WhiteBalance" },
01047       {  0xa404, "exif:DigitalZoomRatio" },
01048       {  0xa405, "exif:FocalLengthIn35mmFilm" },
01049       {  0xa406, "exif:SceneCaptureType" },
01050       {  0xa407, "exif:GainControl" },
01051       {  0xa408, "exif:Contrast" },
01052       {  0xa409, "exif:Saturation" },
01053       {  0xa40a, "exif:Sharpness" },
01054       {  0xa40b, "exif:DeviceSettingDescription" },
01055       {  0xa40c, "exif:SubjectDistanceRange" },
01056       {  0xa420, "exif:ImageUniqueID" },
01057       {  0xc4a5, "exif:PrintImageMatching" },
01058       {  0xa500, "exif:Gamma" },
01059       {  0xc640, "exif:CR2Slice" },
01060       { 0x10000, "exif:GPSVersionID" },
01061       { 0x10001, "exif:GPSLatitudeRef" },
01062       { 0x10002, "exif:GPSLatitude" },
01063       { 0x10003, "exif:GPSLongitudeRef" },
01064       { 0x10004, "exif:GPSLongitude" },
01065       { 0x10005, "exif:GPSAltitudeRef" },
01066       { 0x10006, "exif:GPSAltitude" },
01067       { 0x10007, "exif:GPSTimeStamp" },
01068       { 0x10008, "exif:GPSSatellites" },
01069       { 0x10009, "exif:GPSStatus" },
01070       { 0x1000a, "exif:GPSMeasureMode" },
01071       { 0x1000b, "exif:GPSDop" },
01072       { 0x1000c, "exif:GPSSpeedRef" },
01073       { 0x1000d, "exif:GPSSpeed" },
01074       { 0x1000e, "exif:GPSTrackRef" },
01075       { 0x1000f, "exif:GPSTrack" },
01076       { 0x10010, "exif:GPSImgDirectionRef" },
01077       { 0x10011, "exif:GPSImgDirection" },
01078       { 0x10012, "exif:GPSMapDatum" },
01079       { 0x10013, "exif:GPSDestLatitudeRef" },
01080       { 0x10014, "exif:GPSDestLatitude" },
01081       { 0x10015, "exif:GPSDestLongitudeRef" },
01082       { 0x10016, "exif:GPSDestLongitude" },
01083       { 0x10017, "exif:GPSDestBearingRef" },
01084       { 0x10018, "exif:GPSDestBearing" },
01085       { 0x10019, "exif:GPSDestDistanceRef" },
01086       { 0x1001a, "exif:GPSDestDistance" },
01087       { 0x1001b, "exif:GPSProcessingMethod" },
01088       { 0x1001c, "exif:GPSAreaInformation" },
01089       { 0x1001d, "exif:GPSDateStamp" },
01090       { 0x1001e, "exif:GPSDifferential" },
01091       {  0x0000, NULL}
01092     };
01093 
01094   const StringInfo
01095     *profile;
01096 
01097   const unsigned char
01098     *directory,
01099     *exif;
01100 
01101   DirectoryInfo
01102     directory_stack[MaxDirectoryStack];
01103 
01104   EndianType
01105     endian;
01106 
01107   MagickBooleanType
01108     status;
01109 
01110   register ssize_t
01111     i;
01112 
01113   size_t
01114     entry,
01115     length,
01116     number_entries,
01117     tag_offset,
01118     tag;
01119 
01120   SplayTreeInfo
01121     *exif_resources;
01122 
01123   ssize_t
01124     all,
01125     id,
01126     level,
01127     offset,
01128     tag_value;
01129 
01130   static int
01131     tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
01132 
01133   /*
01134     If EXIF data exists, then try to parse the request for a tag.
01135   */
01136   profile=GetImageProfile(image,"exif");
01137   if (profile == (StringInfo *) NULL)
01138     return(MagickFalse);
01139   if ((property == (const char *) NULL) || (*property == '\0'))
01140     return(MagickFalse);
01141   while (isspace((int) ((unsigned char) *property)) != 0)
01142     property++;
01143   all=0;
01144   tag=(~0UL);
01145   switch (*(property+5))
01146   {
01147     case '*':
01148     {
01149       /*
01150         Caller has asked for all the tags in the EXIF data.
01151       */
01152       tag=0;
01153       all=1; /* return the data in description=value format */
01154       break;
01155     }
01156     case '!':
01157     {
01158       tag=0;
01159       all=2; /* return the data in tagid=value format */
01160       break;
01161     }
01162     case '#':
01163     case '@':
01164     {
01165       int
01166         c;
01167 
01168       size_t
01169         n;
01170 
01171       /*
01172         Check for a hex based tag specification first.
01173       */
01174       tag=(*(property+5) == '@') ? 1UL : 0UL;
01175       property+=6;
01176       n=strlen(property);
01177       if (n != 4)
01178         return(MagickFalse);
01179       /*
01180         Parse tag specification as a hex number.
01181       */
01182       n/=4;
01183       do
01184       {
01185         for (i=(ssize_t) n-1L; i >= 0; i--)
01186         {
01187           c=(*property++);
01188           tag<<=4;
01189           if ((c >= '0') && (c <= '9'))
01190             tag|=(c-'0');
01191           else
01192             if ((c >= 'A') && (c <= 'F'))
01193               tag|=(c-('A'-10));
01194             else
01195               if ((c >= 'a') && (c <= 'f'))
01196                 tag|=(c-('a'-10));
01197               else
01198                 return(MagickFalse);
01199         }
01200       } while (*property != '\0');
01201       break;
01202     }
01203     default:
01204     {
01205       /*
01206         Try to match the text with a tag name instead.
01207       */
01208       for (i=0; ; i++)
01209       {
01210         if (EXIFTag[i].tag == 0)
01211           break;
01212         if (LocaleCompare(EXIFTag[i].description,property) == 0)
01213           {
01214             tag=(size_t) EXIFTag[i].tag;
01215             break;
01216           }
01217       }
01218       break;
01219     }
01220   }
01221   if (tag == (~0UL))
01222     return(MagickFalse);
01223   length=GetStringInfoLength(profile);
01224   exif=GetStringInfoDatum(profile);
01225   while (length != 0)
01226   {
01227     if (ReadPropertyByte(&exif,&length) != 0x45)
01228       continue;
01229     if (ReadPropertyByte(&exif,&length) != 0x78)
01230       continue;
01231     if (ReadPropertyByte(&exif,&length) != 0x69)
01232       continue;
01233     if (ReadPropertyByte(&exif,&length) != 0x66)
01234       continue;
01235     if (ReadPropertyByte(&exif,&length) != 0x00)
01236       continue;
01237     if (ReadPropertyByte(&exif,&length) != 0x00)
01238       continue;
01239     break;
01240   }
01241   if (length < 16)
01242     return(MagickFalse);
01243   id=(ssize_t) ((int) ReadPropertyShort(LSBEndian,exif));
01244   endian=LSBEndian;
01245   if (id == 0x4949)
01246     endian=LSBEndian;
01247   else
01248     if (id == 0x4D4D)
01249       endian=MSBEndian;
01250     else
01251       return(MagickFalse);
01252   if (ReadPropertyShort(endian,exif+2) != 0x002a)
01253     return(MagickFalse);
01254   /*
01255     This the offset to the first IFD.
01256   */
01257   offset=(ssize_t) ((int) ReadPropertyLong(endian,exif+4));
01258   if ((size_t) offset >= length)
01259     return(MagickFalse);
01260   /*
01261     Set the pointer to the first IFD and follow it were it leads.
01262   */
01263   status=MagickFalse;
01264   directory=exif+offset;
01265   level=0;
01266   entry=0;
01267   tag_offset=0;
01268   exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
01269     (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
01270   do
01271   {
01272     /*
01273       If there is anything on the stack then pop it off.
01274     */
01275     if (level > 0)
01276       {
01277         level--;
01278         directory=directory_stack[level].directory;
01279         entry=directory_stack[level].entry;
01280         tag_offset=directory_stack[level].offset;
01281       }
01282     /*
01283       Determine how many entries there are in the current IFD.
01284     */
01285     number_entries=(size_t) ((int) ReadPropertyShort(endian,directory));
01286     for ( ; entry < number_entries; entry++)
01287     {
01288       register unsigned char
01289         *p,
01290         *q;
01291 
01292       size_t
01293         format,
01294         number_bytes;
01295 
01296       ssize_t
01297         components;
01298 
01299       q=(unsigned char *) (directory+(12*entry)+2);
01300       if (GetValueFromSplayTree(exif_resources,q) == q)
01301         break;
01302       (void) AddValueToSplayTree(exif_resources,q,q);
01303       tag_value=(ssize_t) ((int) ReadPropertyShort(endian,q)+tag_offset);
01304       format=(size_t) ((int) ReadPropertyShort(endian,q+2));
01305       if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes)))
01306         break;
01307       components=(ssize_t) ((int) ReadPropertyLong(endian,q+4));
01308       number_bytes=(size_t) components*tag_bytes[format];
01309       if (number_bytes <= 4)
01310         p=q+8;
01311       else
01312         {
01313           ssize_t
01314             offset;
01315 
01316           /*
01317             The directory entry contains an offset.
01318           */
01319           offset=(ssize_t) ((int) ReadPropertyLong(endian,q+8));
01320           if ((size_t) (offset+number_bytes) > length)
01321             continue;
01322           p=(unsigned char *) (exif+offset);
01323         }
01324       if ((all != 0) || (tag == (size_t) tag_value))
01325         {
01326           char
01327             buffer[MaxTextExtent],
01328             *value;
01329 
01330           switch (format)
01331           {
01332             case EXIF_FMT_BYTE:
01333             case EXIF_FMT_UNDEFINED:
01334             {
01335               EXIFMultipleValues(1,"%.20g",(double) (*(unsigned char *) p1));
01336               break;
01337             }
01338             case EXIF_FMT_SBYTE:
01339             {
01340               EXIFMultipleValues(1,"%.20g",(double) (*(signed char *) p1));
01341               break;
01342             }
01343             case EXIF_FMT_SSHORT:
01344             {
01345               EXIFMultipleValues(2,"%hd",ReadPropertyShort(endian,p1));
01346               break;
01347             }
01348             case EXIF_FMT_USHORT:
01349             {
01350               EXIFMultipleValues(2,"%hu",ReadPropertyShort(endian,p1));
01351               break;
01352             }
01353             case EXIF_FMT_ULONG:
01354             {
01355               EXIFMultipleValues(4,"%.20g",(double)
01356                 ((int) ReadPropertyLong(endian,p1)));
01357               break;
01358             }
01359             case EXIF_FMT_SLONG:
01360             {
01361               EXIFMultipleValues(4,"%.20g",(double)
01362                 ((int) ReadPropertyLong(endian,p1)));
01363               break;
01364             }
01365             case EXIF_FMT_URATIONAL:
01366             {
01367               EXIFMultipleFractions(8,"%.20g/%.20g",(double)
01368                 ((int) ReadPropertyLong(endian,p1)),(double)
01369                 ((int) ReadPropertyLong(endian,p1+4)));
01370               break;
01371             }
01372             case EXIF_FMT_SRATIONAL:
01373             {
01374               EXIFMultipleFractions(8,"%.20g/%.20g",(double)
01375                 ((int) ReadPropertyLong(endian,p1)),(double)
01376                 ((int) ReadPropertyLong(endian,p1+4)));
01377               break;
01378             }
01379             case EXIF_FMT_SINGLE:
01380             {
01381               EXIFMultipleValues(4,"%f",(double) *(float *) p1);
01382               break;
01383             }
01384             case EXIF_FMT_DOUBLE:
01385             {
01386               EXIFMultipleValues(8,"%f",*(double *) p1);
01387               break;
01388             }
01389             default:
01390             case EXIF_FMT_STRING:
01391             {
01392               value=(char *) NULL;
01393               if (~(1UL*number_bytes) >= 1)
01394                 value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
01395                   sizeof(*value));
01396               if (value != (char *) NULL)
01397                 {
01398                   register ssize_t
01399                     i;
01400 
01401                   for (i=0; i < (ssize_t) number_bytes; i++)
01402                   {
01403                     value[i]='.';
01404                     if ((isprint((int) p[i]) != 0) || (p[i] == '\0'))
01405                       value[i]=(char) p[i];
01406                   }
01407                   value[i]='\0';
01408                 }
01409               break;
01410             }
01411           }
01412           if (value != (char *) NULL)
01413             {
01414               char
01415                 key[MaxTextExtent];
01416 
01417               register const char
01418                 *p;
01419 
01420               (void) CopyMagickString(key,property,MaxTextExtent);
01421               switch (all)
01422               {
01423                 case 1:
01424                 {
01425                   const char
01426                     *description;
01427 
01428                   register ssize_t
01429                     i;
01430 
01431                   description="unknown";
01432                   for (i=0; ; i++)
01433                   {
01434                     if (EXIFTag[i].tag == 0)
01435                       break;
01436                     if ((ssize_t) EXIFTag[i].tag == tag_value)
01437                       {
01438                         description=EXIFTag[i].description;
01439                         break;
01440                       }
01441                   }
01442                   (void) FormatLocaleString(key,MaxTextExtent,"%s",description);
01443                   break;
01444                 }
01445                 case 2:
01446                 {
01447                   if (tag_value < 0x10000)
01448                     (void) FormatLocaleString(key,MaxTextExtent,"#%04lx",
01449                       (unsigned long) tag_value);
01450                   else
01451                     if (tag_value < 0x20000)
01452                       (void) FormatLocaleString(key,MaxTextExtent,"@%04lx",
01453                         (unsigned long) (tag_value & 0xffff));
01454                     else
01455                       (void) FormatLocaleString(key,MaxTextExtent,"unknown");
01456                   break;
01457                 }
01458               }
01459               p=(const char *) NULL;
01460               if (image->properties != (void *) NULL)
01461                 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
01462                   image->properties,key);
01463               if (p == (const char *) NULL)
01464                 (void) SetImageProperty((Image *) image,key,value,exception);
01465               value=DestroyString(value);
01466               status=MagickTrue;
01467             }
01468         }
01469         if ((tag_value == TAG_EXIF_OFFSET) ||
01470             (tag_value == TAG_INTEROP_OFFSET) || (tag_value == TAG_GPS_OFFSET))
01471           {
01472             size_t
01473               offset;
01474 
01475             offset=(size_t) ((int) ReadPropertyLong(endian,p));
01476             if ((offset < length) && (level < (MaxDirectoryStack-2)))
01477               {
01478                 size_t
01479                   tag_offset1;
01480 
01481                 tag_offset1=(tag_value == TAG_GPS_OFFSET) ? 0x10000UL : 0UL;
01482                 directory_stack[level].directory=directory;
01483                 entry++;
01484                 directory_stack[level].entry=entry;
01485                 directory_stack[level].offset=tag_offset;
01486                 level++;
01487                 directory_stack[level].directory=exif+offset;
01488                 directory_stack[level].offset=tag_offset1;
01489                 directory_stack[level].entry=0;
01490                 level++;
01491                 if ((directory+2+(12*number_entries)) > (exif+length))
01492                   break;
01493                 offset=(size_t) ((int) ReadPropertyLong(endian,directory+2+(12*
01494                   number_entries)));
01495                 if ((offset != 0) && (offset < length) &&
01496                     (level < (MaxDirectoryStack-2)))
01497                   {
01498                     directory_stack[level].directory=exif+offset;
01499                     directory_stack[level].entry=0;
01500                     directory_stack[level].offset=tag_offset1;
01501                     level++;
01502                   }
01503               }
01504             break;
01505           }
01506     }
01507   } while (level > 0);
01508   exif_resources=DestroySplayTree(exif_resources);
01509   return(status);
01510 }
01511 
01512 static MagickBooleanType GetXMPProperty(const Image *image,const char *property)
01513 {
01514   char
01515     *xmp_profile;
01516 
01517   const StringInfo
01518     *profile;
01519 
01520   ExceptionInfo
01521     *exception;
01522 
01523   MagickBooleanType
01524     status;
01525 
01526   register const char
01527     *p;
01528 
01529   XMLTreeInfo
01530     *child,
01531     *description,
01532     *node,
01533     *rdf,
01534     *xmp;
01535 
01536   profile=GetImageProfile(image,"xmp");
01537   if (profile == (StringInfo *) NULL)
01538     return(MagickFalse);
01539   if ((property == (const char *) NULL) || (*property == '\0'))
01540     return(MagickFalse);
01541   xmp_profile=StringInfoToString(profile);
01542   if (xmp_profile == (char *) NULL)
01543     return(MagickFalse);
01544   for (p=xmp_profile; *p != '\0'; p++)
01545     if ((*p == '<') && (*(p+1) == 'x'))
01546       break;
01547   exception=AcquireExceptionInfo();
01548   xmp=NewXMLTree((char *) p,exception);
01549   xmp_profile=DestroyString(xmp_profile);
01550   exception=DestroyExceptionInfo(exception);
01551   if (xmp == (XMLTreeInfo *) NULL)
01552     return(MagickFalse);
01553   status=MagickFalse;
01554   rdf=GetXMLTreeChild(xmp,"rdf:RDF");
01555   if (rdf != (XMLTreeInfo *) NULL)
01556     {
01557       if (image->properties == (void *) NULL)
01558         ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
01559           RelinquishMagickMemory,RelinquishMagickMemory);
01560       description=GetXMLTreeChild(rdf,"rdf:Description");
01561       while (description != (XMLTreeInfo *) NULL)
01562       {
01563         node=GetXMLTreeChild(description,(const char *) NULL);
01564         while (node != (XMLTreeInfo *) NULL)
01565         {
01566           child=GetXMLTreeChild(node,(const char *) NULL);
01567           if (child == (XMLTreeInfo *) NULL)
01568             (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
01569               ConstantString(GetXMLTreeTag(node)),
01570               ConstantString(GetXMLTreeContent(node)));
01571           while (child != (XMLTreeInfo *) NULL)
01572           {
01573             if (LocaleCompare(GetXMLTreeTag(child),"rdf:Seq") != 0)
01574               (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
01575                 ConstantString(GetXMLTreeTag(child)),
01576                 ConstantString(GetXMLTreeContent(child)));
01577             child=GetXMLTreeSibling(child);
01578           }
01579           node=GetXMLTreeSibling(node);
01580         }
01581         description=GetNextXMLTreeTag(description);
01582       }
01583     }
01584   xmp=DestroyXMLTree(xmp);
01585   return(status);
01586 }
01587 
01588 static char *TracePSClippath(const unsigned char *blob,size_t length,
01589   const size_t magick_unused(columns),const size_t magick_unused(rows))
01590 {
01591   char
01592     *path,
01593     *message;
01594 
01595   MagickBooleanType
01596     in_subpath;
01597 
01598   PointInfo
01599     first[3],
01600     last[3],
01601     point[3];
01602 
01603   register ssize_t
01604     i,
01605     x;
01606 
01607   ssize_t
01608     knot_count,
01609     selector,
01610     y;
01611 
01612   path=AcquireString((char *) NULL);
01613   if (path == (char *) NULL)
01614     return((char *) NULL);
01615   message=AcquireString((char *) NULL);
01616   (void) FormatLocaleString(message,MaxTextExtent,"/ClipImage\n");
01617   (void) ConcatenateString(&path,message);
01618   (void) FormatLocaleString(message,MaxTextExtent,"{\n");
01619   (void) ConcatenateString(&path,message);
01620   (void) FormatLocaleString(message,MaxTextExtent,"  /c {curveto} bind def\n");
01621   (void) ConcatenateString(&path,message);
01622   (void) FormatLocaleString(message,MaxTextExtent,"  /l {lineto} bind def\n");
01623   (void) ConcatenateString(&path,message);
01624   (void) FormatLocaleString(message,MaxTextExtent,"  /m {moveto} bind def\n");
01625   (void) ConcatenateString(&path,message);
01626   (void) FormatLocaleString(message,MaxTextExtent,
01627     "  /v {currentpoint 6 2 roll curveto} bind def\n");
01628   (void) ConcatenateString(&path,message);
01629   (void) FormatLocaleString(message,MaxTextExtent,
01630     "  /y {2 copy curveto} bind def\n");
01631   (void) ConcatenateString(&path,message);
01632   (void) FormatLocaleString(message,MaxTextExtent,
01633     "  /z {closepath} bind def\n");
01634   (void) ConcatenateString(&path,message);
01635   (void) FormatLocaleString(message,MaxTextExtent,"  newpath\n");
01636   (void) ConcatenateString(&path,message);
01637   /*
01638     The clipping path format is defined in "Adobe Photoshop File
01639     Formats Specification" version 6.0 downloadable from adobe.com.
01640   */
01641   (void) ResetMagickMemory(point,0,sizeof(point));
01642   (void) ResetMagickMemory(first,0,sizeof(first));
01643   (void) ResetMagickMemory(last,0,sizeof(last));
01644   knot_count=0;
01645   in_subpath=MagickFalse;
01646   while (length > 0)
01647   {
01648     selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
01649     switch (selector)
01650     {
01651       case 0:
01652       case 3:
01653       {
01654         if (knot_count != 0)
01655           {
01656             blob+=24;
01657             length-=MagickMin(24,(ssize_t) length);
01658             break;
01659           }
01660         /*
01661           Expected subpath length record.
01662         */
01663         knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
01664         blob+=22;
01665         length-=MagickMin(22,(ssize_t) length);
01666         break;
01667       }
01668       case 1:
01669       case 2:
01670       case 4:
01671       case 5:
01672       {
01673         if (knot_count == 0)
01674           {
01675             /*
01676               Unexpected subpath knot
01677             */
01678             blob+=24;
01679             length-=MagickMin(24,(ssize_t) length);
01680             break;
01681           }
01682         /*
01683           Add sub-path knot
01684         */
01685         for (i=0; i < 3; i++)
01686         {
01687           size_t
01688             xx,
01689             yy;
01690 
01691           yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
01692           xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
01693           x=(ssize_t) xx;
01694           if (xx > 2147483647)
01695             x=(ssize_t) xx-4294967295U-1;
01696           y=(ssize_t) yy;
01697           if (yy > 2147483647)
01698             y=(ssize_t) yy-4294967295U-1;
01699           point[i].x=(double) x/4096/4096;
01700           point[i].y=1.0-(double) y/4096/4096;
01701         }
01702         if (in_subpath == MagickFalse)
01703           {
01704             (void) FormatLocaleString(message,MaxTextExtent,"  %g %g m\n",
01705               point[1].x,point[1].y);
01706             for (i=0; i < 3; i++)
01707             {
01708               first[i]=point[i];
01709               last[i]=point[i];
01710             }
01711           }
01712         else
01713           {
01714             /*
01715               Handle special cases when Bezier curves are used to describe
01716               corners and straight lines.
01717             */
01718             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
01719                 (point[0].x == point[1].x) && (point[0].y == point[1].y))
01720               (void) FormatLocaleString(message,MaxTextExtent,
01721                 "  %g %g l\n",point[1].x,point[1].y);
01722             else
01723               if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
01724                 (void) FormatLocaleString(message,MaxTextExtent,
01725                   "  %g %g %g %g v\n",point[0].x,point[0].y,
01726                   point[1].x,point[1].y);
01727               else
01728                 if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
01729                   (void) FormatLocaleString(message,MaxTextExtent,
01730                     "  %g %g %g %g y\n",last[2].x,last[2].y,
01731                     point[1].x,point[1].y);
01732                 else
01733                   (void) FormatLocaleString(message,MaxTextExtent,
01734                     "  %g %g %g %g %g %g c\n",last[2].x,
01735                     last[2].y,point[0].x,point[0].y,point[1].x,point[1].y);
01736             for (i=0; i < 3; i++)
01737               last[i]=point[i];
01738           }
01739         (void) ConcatenateString(&path,message);
01740         in_subpath=MagickTrue;
01741         knot_count--;
01742         /*
01743           Close the subpath if there are no more knots.
01744         */
01745         if (knot_count == 0)
01746           {
01747             /*
01748               Same special handling as above except we compare to the
01749               first point in the path and close the path.
01750             */
01751             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
01752                 (first[0].x == first[1].x) && (first[0].y == first[1].y))
01753               (void) FormatLocaleString(message,MaxTextExtent,
01754                 "  %g %g l z\n",first[1].x,first[1].y);
01755             else
01756               if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
01757                 (void) FormatLocaleString(message,MaxTextExtent,
01758                   "  %g %g %g %g v z\n",first[0].x,first[0].y,
01759                   first[1].x,first[1].y);
01760               else
01761                 if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
01762                   (void) FormatLocaleString(message,MaxTextExtent,
01763                     "  %g %g %g %g y z\n",last[2].x,last[2].y,
01764                     first[1].x,first[1].y);
01765                 else
01766                   (void) FormatLocaleString(message,MaxTextExtent,
01767                     "  %g %g %g %g %g %g c z\n",last[2].x,
01768                     last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
01769             (void) ConcatenateString(&path,message);
01770             in_subpath=MagickFalse;
01771           }
01772         break;
01773       }
01774       case 6:
01775       case 7:
01776       case 8:
01777       default:
01778       {
01779         blob+=24;
01780         length-=MagickMin(24,(ssize_t) length);
01781         break;
01782       }
01783     }
01784   }
01785   /*
01786     Returns an empty PS path if the path has no knots.
01787   */
01788   (void) FormatLocaleString(message,MaxTextExtent,"  eoclip\n");
01789   (void) ConcatenateString(&path,message);
01790   (void) FormatLocaleString(message,MaxTextExtent,"} bind def");
01791   (void) ConcatenateString(&path,message);
01792   message=DestroyString(message);
01793   return(path);
01794 }
01795 
01796 static char *TraceSVGClippath(const unsigned char *blob,size_t length,
01797   const size_t columns,const size_t rows)
01798 {
01799   char
01800     *path,
01801     *message;
01802 
01803   MagickBooleanType
01804     in_subpath;
01805 
01806   PointInfo
01807     first[3],
01808     last[3],
01809     point[3];
01810 
01811   register ssize_t
01812     i;
01813 
01814   ssize_t
01815     knot_count,
01816     selector,
01817     x,
01818     y;
01819 
01820   path=AcquireString((char *) NULL);
01821   if (path == (char *) NULL)
01822     return((char *) NULL);
01823   message=AcquireString((char *) NULL);
01824   (void) FormatLocaleString(message,MaxTextExtent,
01825     "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
01826   (void) ConcatenateString(&path,message);
01827   (void) FormatLocaleString(message,MaxTextExtent,
01828     "<svg width=\"%.20g\" height=\"%.20g\">\n",(double) columns,(double) rows);
01829   (void) ConcatenateString(&path,message);
01830   (void) FormatLocaleString(message,MaxTextExtent,"<g>\n");
01831   (void) ConcatenateString(&path,message);
01832   (void) FormatLocaleString(message,MaxTextExtent,
01833     "<path style=\"fill:#00000000;stroke:#00000000;");
01834   (void) ConcatenateString(&path,message);
01835   (void) FormatLocaleString(message,MaxTextExtent,
01836     "stroke-width:0;stroke-antialiasing:false\" d=\"\n");
01837   (void) ConcatenateString(&path,message);
01838   (void) ResetMagickMemory(point,0,sizeof(point));
01839   (void) ResetMagickMemory(first,0,sizeof(first));
01840   (void) ResetMagickMemory(last,0,sizeof(last));
01841   knot_count=0;
01842   in_subpath=MagickFalse;
01843   while (length != 0)
01844   {
01845     selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
01846     switch (selector)
01847     {
01848       case 0:
01849       case 3:
01850       {
01851         if (knot_count != 0)
01852           {
01853             blob+=24;
01854             length-=MagickMin(24,(ssize_t) length);
01855             break;
01856           }
01857         /*
01858           Expected subpath length record.
01859         */
01860         knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length));
01861         blob+=22;
01862         length-=MagickMin(22,(ssize_t) length);
01863         break;
01864       }
01865       case 1:
01866       case 2:
01867       case 4:
01868       case 5:
01869       {
01870         if (knot_count == 0)
01871           {
01872             /*
01873               Unexpected subpath knot.
01874             */
01875             blob+=24;
01876             length-=MagickMin(24,(ssize_t) length);
01877             break;
01878           }
01879         /*
01880           Add sub-path knot
01881         */
01882         for (i=0; i < 3; i++)
01883         {
01884           size_t
01885             xx,
01886             yy;
01887 
01888           yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
01889           xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length));
01890           x=(ssize_t) xx;
01891           if (xx > 2147483647)
01892             x=(ssize_t) xx-4294967295U-1;
01893           y=(ssize_t) yy;
01894           if (yy > 2147483647)
01895             y=(ssize_t) yy-4294967295U-1;
01896           point[i].x=(double) x*columns/4096/4096;
01897           point[i].y=(double) y*rows/4096/4096;
01898         }
01899         if (in_subpath == MagickFalse)
01900           {
01901             (void) FormatLocaleString(message,MaxTextExtent,"M %g,%g\n",
01902               point[1].x,point[1].y);
01903             for (i=0; i < 3; i++)
01904             {
01905               first[i]=point[i];
01906               last[i]=point[i];
01907             }
01908           }
01909         else
01910           {
01911             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
01912                 (point[0].x == point[1].x) && (point[0].y == point[1].y))
01913               (void) FormatLocaleString(message,MaxTextExtent,"L %g,%g\n",
01914                 point[1].x,point[1].y);
01915             else
01916               (void) FormatLocaleString(message,MaxTextExtent,
01917                 "C %g,%g %g,%g %g,%g\n",last[2].x,last[2].y,
01918                 point[0].x,point[0].y,point[1].x,point[1].y);
01919             for (i=0; i < 3; i++)
01920               last[i]=point[i];
01921           }
01922         (void) ConcatenateString(&path,message);
01923         in_subpath=MagickTrue;
01924         knot_count--;
01925         /*
01926           Close the subpath if there are no more knots.
01927         */
01928         if (knot_count == 0)
01929           {
01930             if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
01931                 (first[0].x == first[1].x) && (first[0].y == first[1].y))
01932               (void) FormatLocaleString(message,MaxTextExtent,
01933                 "L %g,%g Z\n",first[1].x,first[1].y);
01934             else
01935               {
01936                 (void) FormatLocaleString(message,MaxTextExtent,
01937                   "C %g,%g %g,%g %g,%g Z\n",last[2].x,
01938                   last[2].y,first[0].x,first[0].y,first[1].x,first[1].y);
01939                 (void) ConcatenateString(&path,message);
01940               }
01941             in_subpath=MagickFalse;
01942           }
01943         break;
01944       }
01945       case 6:
01946       case 7:
01947       case 8:
01948       default:
01949       {
01950         blob+=24;
01951         length-=MagickMin(24,(ssize_t) length);
01952         break;
01953       }
01954     }
01955   }
01956   /*
01957     Return an empty SVG image if the path does not have knots.
01958   */
01959   (void) FormatLocaleString(message,MaxTextExtent,"\"/>\n");
01960   (void) ConcatenateString(&path,message);
01961   (void) FormatLocaleString(message,MaxTextExtent,"</g>\n");
01962   (void) ConcatenateString(&path,message);
01963   (void) FormatLocaleString(message,MaxTextExtent,"</svg>\n");
01964   (void) ConcatenateString(&path,message);
01965   message=DestroyString(message);
01966   return(path);
01967 }
01968 
01969 MagickExport const char *GetImageProperty(const Image *image,
01970   const char *property,ExceptionInfo *exception)
01971 {
01972   FxInfo
01973     *fx_info;
01974 
01975   MagickRealType
01976     alpha;
01977 
01978   MagickStatusType
01979     status;
01980 
01981   register const char
01982     *p;
01983 
01984   assert(image != (Image *) NULL);
01985   assert(image->signature == MagickSignature);
01986   if (image->debug != MagickFalse)
01987     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01988   p=(const char *) NULL;
01989   if (image->properties != (void *) NULL)
01990     {
01991       if (property == (const char *) NULL)
01992         {
01993           ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
01994           p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
01995             image->properties);
01996           return(p);
01997         }
01998       if (LocaleNCompare("fx:",property,3) != 0)
01999         {
02000           p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
02001             image->properties,property);
02002           if (p != (const char *) NULL)
02003             return(p);
02004         }
02005     }
02006   if ((property == (const char *) NULL) ||
02007       (strchr(property,':') == (char *) NULL))
02008     return(p);
02009   switch (*property)
02010   {
02011     case '8':
02012     {
02013       if (LocaleNCompare("8bim:",property,5) == 0)
02014         {
02015           if ((Get8BIMProperty(image,property,exception) != MagickFalse) &&
02016               (image->properties != (void *) NULL))
02017             {
02018               p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
02019                 image->properties,property);
02020               return(p);
02021             }
02022         }
02023       break;
02024     }
02025     case 'E':
02026     case 'e':
02027     {
02028       if (LocaleNCompare("exif:",property,5) == 0)
02029         {
02030           if ((GetEXIFProperty(image,property,exception) != MagickFalse) &&
02031               (image->properties != (void *) NULL))
02032             {
02033               p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
02034                 image->properties,property);
02035               return(p);
02036             }
02037         }
02038       break;
02039     }
02040     case 'F':
02041     case 'f':
02042     {
02043       if (LocaleNCompare("fx:",property,3) == 0)
02044         {
02045           fx_info=AcquireFxInfo(image,property+3);
02046           status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0,
02047             &alpha,exception);
02048           fx_info=DestroyFxInfo(fx_info);
02049           if (status != MagickFalse)
02050             {
02051               char
02052                 value[MaxTextExtent];
02053 
02054               (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
02055                 GetMagickPrecision(),(double) alpha);
02056               (void) SetImageProperty((Image *) image,property,value,exception);
02057             }
02058           if (image->properties != (void *) NULL)
02059             {
02060               p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
02061                 image->properties,property);
02062               return(p);
02063             }
02064         }
02065       break;
02066     }
02067     case 'I':
02068     case 'i':
02069     {
02070       if (LocaleNCompare("iptc:",property,5) == 0)
02071         {
02072           if ((GetIPTCProperty(image,property,exception) != MagickFalse) &&
02073               (image->properties != (void *) NULL))
02074             {
02075               p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
02076                 image->properties,property);
02077               return(p);
02078             }
02079         }
02080       break;
02081     }
02082     case 'P':
02083     case 'p':
02084     {
02085       if (LocaleNCompare("pixel:",property,6) == 0)
02086         {
02087           PixelInfo
02088             pixel;
02089 
02090           GetPixelInfo(image,&pixel);
02091           fx_info=AcquireFxInfo(image,property+6);
02092           status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0,
02093             &alpha,exception);
02094           pixel.red=(MagickRealType) QuantumRange*alpha;
02095           status|=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0,
02096             &alpha,exception);
02097           pixel.green=(MagickRealType) QuantumRange*alpha;
02098           status|=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0,
02099             &alpha,exception);
02100           pixel.blue=(MagickRealType) QuantumRange*alpha;
02101           if (image->colorspace == CMYKColorspace)
02102             {
02103               status|=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0,
02104                 &alpha,exception);
02105               pixel.black=(MagickRealType) QuantumRange*alpha;
02106             }
02107           status|=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0,
02108             &alpha,exception);
02109           pixel.alpha=(MagickRealType) QuantumRange*(1.0-alpha);
02110           fx_info=DestroyFxInfo(fx_info);
02111           if (status != MagickFalse)
02112             {
02113               char
02114                 name[MaxTextExtent];
02115 
02116               (void) QueryColorname(image,&pixel,SVGCompliance,name,
02117                 exception);
02118               (void) SetImageProperty((Image *) image,property,name,exception);
02119               return(GetImageProperty(image,property,exception));
02120             }
02121         }
02122       break;
02123     }
02124     case 'X':
02125     case 'x':
02126     {
02127       if (LocaleNCompare("xmp:",property,4) == 0)
02128         {
02129           if ((GetXMPProperty(image,property) != MagickFalse) &&
02130               (image->properties != (void *) NULL))
02131             {
02132               p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
02133                 image->properties,property);
02134               return(p);
02135             }
02136         }
02137       break;
02138     }
02139     default:
02140       break;
02141   }
02142   return(p);
02143 }
02144 
02145 /*
02146 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02147 %                                                                             %
02148 %                                                                             %
02149 %                                                                             %
02150 +   G e t M a g i c k P r o p e r t y                                         %
02151 %                                                                             %
02152 %                                                                             %
02153 %                                                                             %
02154 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02155 %
02156 %  GetMagickProperty() gets a value associated with an image property.
02157 %
02158 %  The format of the GetMagickProperty method is:
02159 %
02160 %      const char *GetMagickProperty(const ImageInfo *image_info,Image *image,
02161 %        const char *key,ExceptionInfo *exception)
02162 %
02163 %  A description of each parameter follows:
02164 %
02165 %    o image_info: the image info.
02166 %
02167 %    o image: the image.
02168 %
02169 %    o key: the key.
02170 %
02171 %    o exception: return any errors or warnings in this structure.
02172 %
02173 */
02174 MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
02175   Image *image,const char *property,ExceptionInfo *exception)
02176 {
02177   char
02178     value[MaxTextExtent],
02179     filename[MaxTextExtent];
02180 
02181   *value='\0';
02182   switch (*property)
02183   {
02184     case 'b':
02185     {
02186       if (LocaleNCompare("base",property,4) == 0)
02187         {
02188           GetPathComponent(image->magick_filename,BasePath,filename);
02189           (void) CopyMagickString(value,filename,MaxTextExtent);
02190           break;
02191         }
02192       break;
02193     }
02194     case 'c':
02195     {
02196       if (LocaleNCompare("channels",property,8) == 0)
02197         {
02198           /*
02199             Image channels.
02200           */
02201           (void) FormatLocaleString(value,MaxTextExtent,"%s",
02202             CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
02203             image->colorspace));
02204           LocaleLower(value);
02205           if (image->matte != MagickFalse)
02206             (void) ConcatenateMagickString(value,"a",MaxTextExtent);
02207           break;
02208         }
02209       if (LocaleNCompare("colorspace",property,10) == 0)
02210         {
02211           ColorspaceType
02212             colorspace;
02213 
02214           /*
02215             Image storage class and colorspace.
02216           */
02217           colorspace=image->colorspace;
02218           if (IsImageGray(image,exception) != MagickFalse)
02219             colorspace=GRAYColorspace;
02220           (void) FormatLocaleString(value,MaxTextExtent,"%s",
02221             CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t)
02222             colorspace));
02223           break;
02224         }
02225       if (LocaleNCompare("copyright",property,9) == 0)
02226         {
02227           (void) CopyMagickString(value,GetMagickCopyright(),MaxTextExtent);
02228           break;
02229         }
02230       break;
02231     }
02232     case 'd':
02233     {
02234       if (LocaleNCompare("depth",property,5) == 0)
02235         {
02236           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
02237             image->depth);
02238           break;
02239         }
02240       if (LocaleNCompare("directory",property,9) == 0)
02241         {
02242           GetPathComponent(image->magick_filename,HeadPath,filename);
02243           (void) CopyMagickString(value,filename,MaxTextExtent);
02244           break;
02245         }
02246       break;
02247     }
02248     case 'e':
02249     {
02250       if (LocaleNCompare("extension",property,9) == 0)
02251         {
02252           GetPathComponent(image->magick_filename,ExtensionPath,filename);
02253           (void) CopyMagickString(value,filename,MaxTextExtent);
02254           break;
02255         }
02256       break;
02257     }
02258     case 'g':
02259     {
02260       if (LocaleNCompare("group",property,5) == 0)
02261         {
02262           (void) FormatLocaleString(value,MaxTextExtent,"0x%lx",
02263             (unsigned long) image_info->group);
02264           break;
02265         }
02266       break;
02267     }
02268     case 'h':
02269     {
02270       if (LocaleNCompare("height",property,6) == 0)
02271         {
02272           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",
02273             image->magick_rows != 0 ? (double) image->magick_rows : 256.0);
02274           break;
02275         }
02276       break;
02277     }
02278     case 'i':
02279     {
02280       if (LocaleNCompare("input",property,5) == 0)
02281         {
02282           (void) CopyMagickString(value,image->filename,MaxTextExtent);
02283           break;
02284         }
02285       break;
02286     }
02287     case 'k':
02288     {
02289       if (LocaleNCompare("kurtosis",property,8) == 0)
02290         {
02291           double
02292             kurtosis,
02293             skewness;
02294 
02295           (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
02296           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
02297             GetMagickPrecision(),kurtosis);
02298           break;
02299         }
02300       break;
02301     }
02302     case 'm':
02303     {
02304       if (LocaleNCompare("magick",property,6) == 0)
02305         {
02306           (void) CopyMagickString(value,image->magick,MaxTextExtent);
02307           break;
02308         }
02309       if (LocaleNCompare("max",property,3) == 0)
02310         {
02311           double
02312             maximum,
02313             minimum;
02314 
02315           (void) GetImageRange(image,&minimum,&maximum,exception);
02316           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
02317             GetMagickPrecision(),maximum);
02318           break;
02319         }
02320       if (LocaleNCompare("mean",property,4) == 0)
02321         {
02322           double
02323             mean,
02324             standard_deviation;
02325 
02326           (void) GetImageMean(image,&mean,&standard_deviation,
02327              exception);
02328           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
02329             GetMagickPrecision(),mean);
02330           break;
02331         }
02332       if (LocaleNCompare("min",property,3) == 0)
02333         {
02334           double
02335             maximum,
02336             minimum;
02337 
02338           (void) GetImageRange(image,&minimum,&maximum,exception);
02339           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
02340             GetMagickPrecision(),minimum);
02341           break;
02342         }
02343       break;
02344     }
02345     case 'n':
02346     {
02347       if (LocaleNCompare("name",property,4) == 0)
02348         {
02349           (void) CopyMagickString(value,filename,MaxTextExtent);
02350           break;
02351         }
02352      break;
02353     }
02354     case 'o':
02355     {
02356       if (LocaleNCompare("opaque",property,6) == 0)
02357         {
02358           MagickBooleanType
02359             opaque;
02360 
02361           opaque=IsImageOpaque(image,exception);
02362           (void) CopyMagickString(value,opaque == MagickFalse ? "false" :
02363             "true",MaxTextExtent);
02364           break;
02365         }
02366       if (LocaleNCompare("output",property,6) == 0)
02367         {
02368           (void) CopyMagickString(value,image_info->filename,MaxTextExtent);
02369           break;
02370         }
02371      break;
02372     }
02373     case 'p':
02374     {
02375       if (LocaleNCompare("page",property,4) == 0)
02376         {
02377           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
02378             GetImageIndexInList(image)+1);
02379           break;
02380         }
02381       break;
02382     }
02383     case 's':
02384     {
02385       if (LocaleNCompare("size",property,4) == 0)
02386         {
02387           char
02388             format[MaxTextExtent];
02389 
02390           (void) FormatMagickSize(GetBlobSize(image),MagickFalse,format);
02391           (void) FormatLocaleString(value,MaxTextExtent,"%sB",format);
02392           break;
02393         }
02394       if (LocaleNCompare("scenes",property,6) == 0)
02395         {
02396           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
02397             GetImageListLength(image));
02398           break;
02399         }
02400       if (LocaleNCompare("scene",property,5) == 0)
02401         {
02402           /* FUTURE: I am not certain this property return makes sense! */
02403           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
02404             image->scene);
02405           if (image_info->number_scenes != 0)
02406             (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
02407               image_info->scene);
02408           break;
02409         }
02410       if (LocaleNCompare("skewness",property,8) == 0)
02411         {
02412           double
02413             kurtosis,
02414             skewness;
02415 
02416           (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
02417           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
02418             GetMagickPrecision(),skewness);
02419           break;
02420         }
02421       if (LocaleNCompare("standard-deviation",property,18) == 0)
02422         {
02423           double
02424             mean,
02425             standard_deviation;
02426 
02427           (void) GetImageMean(image,&mean,&standard_deviation,
02428             exception);
02429           (void) FormatLocaleString(value,MaxTextExtent,"%.*g",
02430             GetMagickPrecision(),standard_deviation);
02431           break;
02432         }
02433        break;
02434     }
02435     case 'u':
02436     {
02437       if (LocaleNCompare("unique",property,6) == 0)
02438         {
02439           (void) CopyMagickString(filename,image_info->unique,MaxTextExtent);
02440           (void) CopyMagickString(value,filename,MaxTextExtent);
02441           break;
02442         }
02443       break;
02444     }
02445     case 'v':
02446     {
02447       if (LocaleNCompare("version",property,7) == 0)
02448         {
02449           (void) CopyMagickString(value,GetMagickVersion((size_t *) NULL),
02450             MaxTextExtent);
02451           break;
02452         }
02453       break;
02454     }
02455     case 'w':
02456     {
02457       if (LocaleNCompare("width",property,5) == 0)
02458         {
02459           (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
02460             (image->magick_columns != 0 ? image->magick_columns : 256));
02461           break;
02462         }
02463       break;
02464     }
02465     case 'x':
02466     {
02467       if (LocaleNCompare("xresolution",property,11) == 0)
02468         {
02469           (void) FormatLocaleString(value,MaxTextExtent,"%g",
02470             image->resolution.x);
02471           break;
02472         }
02473       break;
02474     }
02475     case 'y':
02476     {
02477       if (LocaleNCompare("yresolution",property,11) == 0)
02478         {
02479           (void) FormatLocaleString(value,MaxTextExtent,"%g",
02480             image->resolution.y);
02481           break;
02482         }
02483       break;
02484     }
02485     case 'z':
02486     {
02487       if (LocaleNCompare("zero",property,4) == 0)
02488         {
02489           (void) CopyMagickString(filename,image_info->zero,MaxTextExtent);
02490           (void) CopyMagickString(value,filename,MaxTextExtent);
02491           break;
02492         }
02493       break;
02494     }
02495   }
02496   if (*value != '\0')
02497    {
02498      if (image->properties == (void *) NULL)
02499        image->properties=NewSplayTree(CompareSplayTreeString,
02500          RelinquishMagickMemory,RelinquishMagickMemory);
02501      (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
02502        ConstantString(property),ConstantString(value));
02503    }
02504   return(GetImageProperty(image,property,exception));
02505 }
02506 
02507 /*
02508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02509 %                                                                             %
02510 %                                                                             %
02511 %                                                                             %
02512 %   G e t N e x t I m a g e P r o p e r t y                                   %
02513 %                                                                             %
02514 %                                                                             %
02515 %                                                                             %
02516 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02517 %
02518 %  GetNextImageProperty() gets the next image property value.
02519 %
02520 %  The format of the GetNextImageProperty method is:
02521 %
02522 %      char *GetNextImageProperty(const Image *image)
02523 %
02524 %  A description of each parameter follows:
02525 %
02526 %    o image: the image.
02527 %
02528 */
02529 MagickExport char *GetNextImageProperty(const Image *image)
02530 {
02531   assert(image != (Image *) NULL);
02532   assert(image->signature == MagickSignature);
02533   if (image->debug != MagickFalse)
02534     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
02535       image->filename);
02536   if (image->properties == (void *) NULL)
02537     return((char *) NULL);
02538   return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties));
02539 }
02540 
02541 /*
02542 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02543 %                                                                             %
02544 %                                                                             %
02545 %                                                                             %
02546 %   I n t e r p r e t I m a g e P r o p e r t i e s                           %
02547 %                                                                             %
02548 %                                                                             %
02549 %                                                                             %
02550 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02551 %
02552 %  InterpretImageProperties() replaces any embedded formatting characters with
02553 %  the appropriate image property and returns the interpreted text.
02554 %
02555 %  The format of the InterpretImageProperties method is:
02556 %
02557 %      char *InterpretImageProperties(const ImageInfo *image_info,Image *image,
02558 %        const char *embed_text,ExceptionInfo *exception)
02559 %
02560 %  A description of each parameter follows:
02561 %
02562 %    o image_info: the image info.
02563 %
02564 %    o image: the image.
02565 %
02566 %    o embed_text: the address of a character string containing the embedded
02567 %      formatting characters.
02568 %
02569 %    o exception: return any errors or warnings in this structure.
02570 %
02571 */
02572 MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
02573   Image *image,const char *embed_text,ExceptionInfo *exception)
02574 {
02575   char
02576     filename[MaxTextExtent],
02577     *interpret_text,
02578     *text;
02579 
02580   const char
02581     *value;
02582 
02583   register char
02584     *q;
02585 
02586   register const char
02587     *p;
02588 
02589   register ssize_t
02590     i;
02591 
02592   size_t
02593     extent,
02594     length;
02595 
02596   assert(image != (Image *) NULL);
02597   assert(image->signature == MagickSignature);
02598   if (image->debug != MagickFalse)
02599     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02600   if ((embed_text == (const char *) NULL) || (*embed_text == '\0'))
02601     return((char *) NULL);
02602   text=(char *) embed_text;
02603   if ((*text == '@') && ((*(text+1) == '-') ||
02604       (IsPathAccessible(text+1) != MagickFalse)))
02605     return(FileToString(embed_text+1,~0,exception));
02606   /*
02607     Translate any embedded format characters.
02608   */
02609   interpret_text=AcquireString(text);
02610   extent=MaxTextExtent;
02611   p=text;
02612   for (q=interpret_text; *p != '\0'; p++)
02613   {
02614     *q='\0';
02615     if ((size_t) (q-interpret_text+MaxTextExtent) >= extent)
02616       {
02617         extent+=MaxTextExtent;
02618         interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
02619           MaxTextExtent+1,sizeof(*interpret_text));
02620         if (interpret_text == (char *) NULL)
02621           break;
02622         q=interpret_text+strlen(interpret_text);
02623       }
02624     /*
02625       Process formatting characters in text.
02626     */
02627     if ((*p == '\\') && (*(p+1) == 'r'))
02628       {
02629         *q++='\r';
02630         p++;
02631         continue;
02632       }
02633     if ((*p == '\\') && (*(p+1) == 'n'))
02634       {
02635         *q++='\n';
02636         p++;
02637         continue;
02638       }
02639     if (*p == '\\')
02640       {
02641         p++;
02642         *q++=(*p);
02643         continue;
02644       }
02645     if (*p != '%')
02646       {
02647         *q++=(*p);
02648         continue;
02649       }
02650     p++;
02651     switch (*p)
02652     {
02653       case '[':  /* multi-character substitution */
02654       {
02655         char
02656           pattern[MaxTextExtent];
02657 
02658         const char
02659           *key,
02660           *value;
02661 
02662         ssize_t
02663           depth;
02664 
02665         /*
02666           Image value.
02667         */
02668         if (strchr(p,']') == (char *) NULL)
02669           break;
02670         depth=1;
02671         p++;
02672         for (i=0; (i < (MaxTextExtent-1L)) && (*p != '\0'); i++)
02673         {
02674           if (*p == '[')
02675             depth++;
02676           if (*p == ']')
02677             depth--;
02678           if (depth <= 0)
02679             break;
02680           pattern[i]=(*p++);
02681         }
02682         pattern[i]='\0';
02683         value=GetImageProperty(image,pattern,exception);
02684         if (value != (const char *) NULL)
02685           {
02686             length=strlen(value);
02687             if ((size_t) (q-interpret_text+length+1) >= extent)
02688               {
02689                 extent+=length;
02690                 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
02691                   extent+MaxTextExtent,sizeof(*interpret_text));
02692                 if (interpret_text == (char *) NULL)
02693                   break;
02694                 q=interpret_text+strlen(interpret_text);
02695               }
02696             (void) CopyMagickString(q,value,extent);
02697             q+=length;
02698             break;
02699           }
02700         else
02701           if (IsGlob(pattern) != MagickFalse)
02702             {
02703               /*
02704                 Iterate over image properties.
02705               */
02706               ResetImagePropertyIterator(image);
02707               key=GetNextImageProperty(image);
02708               while (key != (const char *) NULL)
02709               {
02710                 if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
02711                   {
02712                     value=GetImageProperty(image,key,exception);
02713                     if (value != (const char *) NULL)
02714                       {
02715                         length=strlen(key)+strlen(value)+2;
02716                         if ((size_t) (q-interpret_text+length+1) >= extent)
02717                           {
02718                             extent+=length;
02719                             interpret_text=(char *) ResizeQuantumMemory(
02720                               interpret_text,extent+MaxTextExtent,
02721                               sizeof(*interpret_text));
02722                             if (interpret_text == (char *) NULL)
02723                               break;
02724                             q=interpret_text+strlen(interpret_text);
02725                           }
02726                         q+=FormatLocaleString(q,extent,"%s=%s\n",key,value);
02727                       }
02728                   }
02729                 key=GetNextImageProperty(image);
02730               }
02731             }
02732         value=GetMagickProperty(image_info,image,pattern,exception);
02733         if (value != (const char *) NULL)
02734           {
02735             length=strlen(value);
02736             if ((size_t) (q-interpret_text+length+1) >= extent)
02737               {
02738                 extent+=length;
02739                 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
02740                   extent+MaxTextExtent,sizeof(*interpret_text));
02741                 if (interpret_text == (char *) NULL)
02742                   break;
02743                 q=interpret_text+strlen(interpret_text);
02744               }
02745             (void) CopyMagickString(q,value,extent);
02746             q+=length;
02747             break;
02748           }
02749         if (image_info == (ImageInfo *) NULL)
02750           break;
02751         value=GetImageOption(image_info,pattern);
02752         if (value != (char *) NULL)
02753           {
02754             length=strlen(value);
02755             if ((size_t) (q-interpret_text+length+1) >= extent)
02756               {
02757                 extent+=length;
02758                 interpret_text=(char *) ResizeQuantumMemory(interpret_text,
02759                   extent+MaxTextExtent,sizeof(*interpret_text));
02760                 if (interpret_text == (char *) NULL)
02761                   break;
02762                 q=interpret_text+strlen(interpret_text);
02763               }
02764             (void) CopyMagickString(q,value,extent);
02765             q+=length;
02766             break;
02767           }
02768         break;
02769       }
02770       case 'b':  /* image size as read in */
02771       {
02772         char
02773           format[MaxTextExtent];
02774 
02775         (void) FormatLocaleString(format,MaxTextExtent,"%.20g",(double)
02776           ((MagickOffsetType) image->extent));
02777         if (image->extent != (MagickSizeType) ((size_t) image->extent))
02778           (void) FormatMagickSize(image->extent,MagickFalse,format);
02779         q+=ConcatenateMagickString(q,format,extent);
02780         q+=ConcatenateMagickString(q,"B",extent);
02781         break;
02782       }
02783       case 'c':  /* image comment properity */
02784       {
02785         value=GetImageProperty(image,"comment",exception);
02786         if (value == (const char *) NULL)
02787           break;
02788         length=strlen(value);
02789         if ((size_t) (q-interpret_text+length+1) >= extent)
02790           {
02791             extent+=length;
02792             interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
02793               MaxTextExtent,sizeof(*interpret_text));
02794             if (interpret_text == (char *) NULL)
02795               break;
02796             q=interpret_text+strlen(interpret_text);
02797           }
02798         (void) CopyMagickString(q,value,extent);
02799         q+=length;
02800         break;
02801       }
02802       case 'd':  /* Directory component of filename */
02803       {
02804         GetPathComponent(image->magick_filename,HeadPath,filename);
02805         q+=CopyMagickString(q,filename,extent);
02806         break;
02807       }
02808       case 'e': /* Filename extension (suffix) of image file */
02809       {
02810         GetPathComponent(image->magick_filename,ExtensionPath,filename);
02811         q+=CopyMagickString(q,filename,extent);
02812         break;
02813       }
02814       case 'f': /* Filename without directory component */
02815       {
02816         GetPathComponent(image->magick_filename,TailPath,filename);
02817         q+=CopyMagickString(q,filename,extent);
02818         break;
02819       }
02820       case 't': /* Base filename without directory or extention */
02821       {
02822         GetPathComponent(image->magick_filename,BasePath,filename);
02823         q+=CopyMagickString(q,filename,extent);
02824         break;
02825       }
02826       case 'g': /* Image geometry,  canvas and offset */
02827       {
02828         q+=FormatLocaleString(q,extent,"%.20gx%.20g%+.20g%+.20g",(double)
02829           image->page.width,(double) image->page.height,(double) image->page.x,
02830           (double) image->page.y);
02831         break;
02832       }
02833       case 'h': /* Image height */
02834       {
02835         q+=FormatLocaleString(q,extent,"%.20g",(double) (image->rows != 0 ?
02836           image->rows : image->magick_rows));
02837         break;
02838       }
02839       case 'i': /* Images filename - (output filename with "info:" ) */
02840       {
02841         q+=CopyMagickString(q,image->filename,extent);
02842         break;
02843       }
02844       case 'k': /* Number of unique colors  */
02845       {
02846         q+=FormatLocaleString(q,extent,"%.20g",(double) GetNumberColors(image,
02847           (FILE *) NULL,exception));
02848         break;
02849       }
02850       case 'l': /* Image label  */
02851       {
02852         value=GetImageProperty(image,"label",exception);
02853         if (value == (const char *) NULL)
02854           break;
02855         length=strlen(value);
02856         if ((size_t) (q-interpret_text+length+1) >= extent)
02857           {
02858             extent+=length;
02859             interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
02860               MaxTextExtent,sizeof(*interpret_text));
02861             if (interpret_text == (char *) NULL)
02862               break;
02863             q=interpret_text+strlen(interpret_text);
02864           }
02865         q+=CopyMagickString(q,value,extent);
02866         break;
02867       }
02868       case 'm': /* Image format (file magick) */
02869       {
02870         q+=CopyMagickString(q,image->magick,extent);
02871         break;
02872       }
02873       case 'M': /* Magick filename - exactly as given incl. read mods */
02874       {
02875         q+=CopyMagickString(q,image->magick_filename,extent);
02876         break;
02877       }
02878       case 'n': /* Number of images in the list.  */
02879       {
02880         q+=FormatLocaleString(q,extent,"%.20g",(double)
02881           GetImageListLength(image));
02882         break;
02883       }
02884       case 'o': /* Image output filename */
02885       {
02886         q+=CopyMagickString(q,image_info->filename,extent);
02887         break;
02888       }
02889       case 'p': /* Image index in current image list  */
02890       {
02891         q+=FormatLocaleString(q,extent,"%.20g",(double)
02892           GetImageIndexInList(image));
02893         break;
02894       }
02895       case 'q': /* Quantum depth of image in memory */
02896       {
02897         q+=FormatLocaleString(q,extent,"%.20g",(double)
02898           MAGICKCORE_QUANTUM_DEPTH);
02899         break;
02900       }
02901       case 'r': /* Image storage class and colorspace.  */
02902       {
02903         ColorspaceType
02904           colorspace;
02905 
02906         colorspace=image->colorspace;
02907         if (IsImageGray(image,exception) != MagickFalse)
02908           colorspace=GRAYColorspace;
02909         q+=FormatLocaleString(q,extent,"%s%s%s",CommandOptionToMnemonic(
02910           MagickClassOptions,(ssize_t) image->storage_class),
02911           CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) colorspace),
02912           image->matte != MagickFalse ? "Matte" : "");
02913         break;
02914       }
02915       case 's': /* Image scene number  */
02916       {
02917         if (image_info->number_scenes == 0)
02918           q+=FormatLocaleString(q,extent,"%.20g",(double) image->scene);
02919         else
02920           q+=FormatLocaleString(q,extent,"%.20g",(double) image_info->scene);
02921         break;
02922       }
02923       case 'u': /* Unique filename */
02924       {
02925         (void) CopyMagickString(filename,image_info->unique,extent);
02926         q+=CopyMagickString(q,filename,extent);
02927         break;
02928       }
02929       case 'w': /* Image width  */
02930       {
02931         q+=FormatLocaleString(q,extent,"%.20g",(double) (image->columns != 0 ?
02932           image->columns : image->magick_columns));
02933         break;
02934       }
02935       case 'x': /* Image horizontal resolution (density).  */
02936       {
02937         q+=FormatLocaleString(q,extent,"%g %s",image->resolution.x,
02938           CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
02939             image->units));
02940         break;
02941       }
02942       case 'y': /* Image vertical resolution (density)  */
02943       {
02944         q+=FormatLocaleString(q,extent,"%g %s",image->resolution.y,
02945           CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t)
02946           image->units));
02947         break;
02948       }
02949       case 'z': /* Image depth as read in */
02950       {
02951         q+=FormatLocaleString(q,extent,"%.20g",(double) image->depth);
02952         break;
02953       }
02954       case 'A': /* Image alpha channel  */
02955       {
02956         q+=FormatLocaleString(q,extent,"%s",CommandOptionToMnemonic(
02957           MagickBooleanOptions,(ssize_t) image->matte));
02958         break;
02959       }
02960       case 'C': /* Image compression method.  */
02961       {
02962         q+=FormatLocaleString(q,extent,"%s",CommandOptionToMnemonic(
02963           MagickCompressOptions,(ssize_t) image->compression));
02964         break;
02965       }
02966       case 'D': /* Image dispose method.  */
02967       {
02968         q+=FormatLocaleString(q,extent,"%s",CommandOptionToMnemonic(
02969           MagickDisposeOptions,(ssize_t) image->dispose));
02970         break;
02971       }
02972       case 'G': /* Image size as geometry = "%wx%h" */
02973       {
02974         q+=FormatLocaleString(q,extent,"%.20gx%.20g",(double)
02975           image->magick_columns,(double) image->magick_rows);
02976         break;
02977       }
02978       case 'H': /* layer canvas height */
02979       {
02980         q+=FormatLocaleString(q,extent,"%.20g",(double) image->page.height);
02981         break;
02982       }
02983       case 'O': /* layer canvas offset with sign = "+%X+%Y" */
02984       {
02985         q+=FormatLocaleString(q,extent,"%+ld%+ld",(long) image->page.x,(long)
02986           image->page.y);
02987         break;
02988       }
02989       case 'P': /* layer canvas page size = "%Wx%H" */
02990       {
02991         q+=FormatLocaleString(q,extent,"%.20gx%.20g",(double) image->page.width,
02992           (double) image->page.height);
02993         break;
02994       }
02995       case 'Q': /* image compression quality */
02996       {
02997         q+=FormatLocaleString(q,extent,"%.20g",(double) image->quality);
02998         break;
02999       }
03000       case 'S': /* Image scenes  */
03001       {
03002         if (image_info->number_scenes == 0)
03003           q+=CopyMagickString(q,"2147483647",extent);
03004         else
03005           q+=FormatLocaleString(q,extent,"%.20g",(double) (image_info->scene+
03006             image_info->number_scenes));
03007         break;
03008       }
03009       case 'T': /* image time delay for animations */
03010       {
03011         q+=FormatLocaleString(q,extent,"%.20g",(double) image->delay);
03012         break;
03013       }
03014       case 'W': /* layer canvas width */
03015       {
03016         q+=FormatLocaleString(q,extent,"%.20g",(double) image->page.width);
03017         break;
03018       }
03019       case 'X': /* layer canvas X offset */
03020       {
03021         q+=FormatLocaleString(q,extent,"%+.20g",(double) image->page.x);
03022         break;
03023       }
03024       case 'Y': /* layer canvas Y offset */
03025       {
03026         q+=FormatLocaleString(q,extent,"%+.20g",(double) image->page.y);
03027         break;
03028       }
03029       case 'Z': /* Unique filename. */
03030       {
03031         (void) CopyMagickString(filename,image_info->zero,extent);
03032         q+=CopyMagickString(q,filename,extent);
03033         break;
03034       }
03035       case '@': /* Image bounding box.  */
03036       {
03037         RectangleInfo
03038           page;
03039 
03040         page=GetImageBoundingBox(image,exception);
03041         q+=FormatLocaleString(q,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g",
03042           (double) page.width,(double) page.height,(double) page.x,(double)
03043           page.y);
03044         break;
03045       }
03046       case '#': /* Image signature */
03047       {
03048         (void) SignatureImage(image,exception);
03049         value=GetImageProperty(image,"signature",exception);
03050         if (value == (const char *) NULL)
03051           break;
03052         q+=CopyMagickString(q,value,extent);
03053         break;
03054       }
03055       case '%': /* percent escaped */
03056       {
03057         *q++=(*p);
03058         break;
03059       }
03060       default: /* percent not expanded */
03061       {
03062         *q++='%';
03063         *q++=(*p);
03064         break;
03065       }
03066     }
03067   }
03068   *q='\0';
03069   if (text != (const char *) embed_text)
03070     text=DestroyString(text);
03071   (void) SubstituteString(&interpret_text,"&lt;","<");
03072   (void) SubstituteString(&interpret_text,"&gt;",">");
03073   return(interpret_text);
03074 }
03075 
03076 /*
03077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03078 %                                                                             %
03079 %                                                                             %
03080 %                                                                             %
03081 %   R e m o v e I m a g e P r o p e r t y                                     %
03082 %                                                                             %
03083 %                                                                             %
03084 %                                                                             %
03085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03086 %
03087 %  RemoveImageProperty() removes a property from the image and returns its
03088 %  value.
03089 %
03090 %  The format of the RemoveImageProperty method is:
03091 %
03092 %      char *RemoveImageProperty(Image *image,const char *property)
03093 %
03094 %  A description of each parameter follows:
03095 %
03096 %    o image: the image.
03097 %
03098 %    o property: the image property.
03099 %
03100 */
03101 MagickExport char *RemoveImageProperty(Image *image,
03102   const char *property)
03103 {
03104   char
03105     *value;
03106 
03107   assert(image != (Image *) NULL);
03108   assert(image->signature == MagickSignature);
03109   if (image->debug != MagickFalse)
03110     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
03111       image->filename);
03112   if (image->properties == (void *) NULL)
03113     return((char *) NULL);
03114   value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
03115     property);
03116   return(value);
03117 }
03118 
03119 /*
03120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03121 %                                                                             %
03122 %                                                                             %
03123 %                                                                             %
03124 %   R e s e t I m a g e P r o p e r t y I t e r a t o r                       %
03125 %                                                                             %
03126 %                                                                             %
03127 %                                                                             %
03128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03129 %
03130 %  ResetImagePropertyIterator() resets the image properties iterator.  Use it
03131 %  in conjunction with GetNextImageProperty() to iterate over all the values
03132 %  associated with an image property.
03133 %
03134 %  The format of the ResetImagePropertyIterator method is:
03135 %
03136 %      ResetImagePropertyIterator(Image *image)
03137 %
03138 %  A description of each parameter follows:
03139 %
03140 %    o image: the image.
03141 %
03142 */
03143 MagickExport void ResetImagePropertyIterator(const Image *image)
03144 {
03145   assert(image != (Image *) NULL);
03146   assert(image->signature == MagickSignature);
03147   if (image->debug != MagickFalse)
03148     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
03149       image->filename);
03150   if (image->properties == (void *) NULL)
03151     return;
03152   ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
03153 }
03154 
03155 /*
03156 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03157 %                                                                             %
03158 %                                                                             %
03159 %                                                                             %
03160 %   S e t I m a g e P r o p e r t y                                           %
03161 %                                                                             %
03162 %                                                                             %
03163 %                                                                             %
03164 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03165 %
03166 %  SetImageProperty() associates an value with an image property.
03167 %
03168 %  The format of the SetImageProperty method is:
03169 %
03170 %      MagickBooleanType SetImageProperty(Image *image,const char *property,
03171 %        const char *value,ExceptionInfo *exception)
03172 %
03173 %  A description of each parameter follows:
03174 %
03175 %    o image: the image.
03176 %
03177 %    o property: the image property.
03178 %
03179 %    o values: the image property values.
03180 %
03181 %    o exception: return any errors or warnings in this structure.
03182 %
03183 */
03184 MagickExport MagickBooleanType SetImageProperty(Image *image,
03185   const char *property,const char *value,ExceptionInfo *exception)
03186 {
03187   MagickBooleanType
03188     status;
03189 
03190   MagickStatusType
03191     flags;
03192 
03193   assert(image != (Image *) NULL);
03194   assert(image->signature == MagickSignature);
03195   if (image->debug != MagickFalse)
03196     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
03197       image->filename);
03198   if (image->properties == (void *) NULL)
03199     image->properties=NewSplayTree(CompareSplayTreeString,
03200       RelinquishMagickMemory,RelinquishMagickMemory);
03201   if ((value == (const char *) NULL) || (*value == '\0'))
03202     return(DeleteImageProperty(image,property));
03203   status=MagickTrue;
03204   switch (*property)
03205   {
03206     case 'B':
03207     case 'b':
03208     {
03209       if (LocaleCompare(property,"background") == 0)
03210         {
03211           (void) QueryColorCompliance(value,AllCompliance,
03212             &image->background_color,exception);
03213           break;
03214         }
03215       if (LocaleCompare(property,"bias") == 0)
03216         {
03217           image->bias=StringToDoubleInterval(value,(double) QuantumRange+1.0);
03218           break;
03219         }
03220       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03221         ConstantString(property),ConstantString(value));
03222       break;
03223     }
03224     case 'C':
03225     case 'c':
03226     {
03227       if (LocaleCompare(property,"colorspace") == 0)
03228         {
03229           ssize_t
03230             colorspace;
03231 
03232           colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
03233             value);
03234           if (colorspace < 0)
03235             break;
03236           (void) SetImageColorspace(image,(ColorspaceType) colorspace,
03237             exception);
03238           break;
03239         }
03240       if (LocaleCompare(property,"compose") == 0)
03241         {
03242           ssize_t
03243             compose;
03244 
03245           compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value);
03246           if (compose < 0)
03247             break;
03248           image->compose=(CompositeOperator) compose;
03249           break;
03250         }
03251       if (LocaleCompare(property,"compress") == 0)
03252         {
03253           ssize_t
03254             compression;
03255 
03256           compression=ParseCommandOption(MagickCompressOptions,MagickFalse,
03257             value);
03258           if (compression < 0)
03259             break;
03260           image->compression=(CompressionType) compression;
03261           break;
03262         }
03263       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03264         ConstantString(property),ConstantString(value));
03265       break;
03266     }
03267     case 'D':
03268     case 'd':
03269     {
03270       if (LocaleCompare(property,"delay") == 0)
03271         {
03272           GeometryInfo
03273             geometry_info;
03274 
03275           flags=ParseGeometry(value,&geometry_info);
03276           if ((flags & GreaterValue) != 0)
03277             {
03278               if (image->delay > (size_t) floor(geometry_info.rho+0.5))
03279                 image->delay=(size_t) floor(geometry_info.rho+0.5);
03280             }
03281           else
03282             if ((flags & LessValue) != 0)
03283               {
03284                 if (image->delay < (size_t) floor(geometry_info.rho+0.5))
03285                   image->ticks_per_second=(ssize_t)
03286                     floor(geometry_info.sigma+0.5);
03287               }
03288             else
03289               image->delay=(size_t) floor(geometry_info.rho+0.5);
03290           if ((flags & SigmaValue) != 0)
03291             image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
03292           break;
03293         }
03294       if (LocaleCompare(property,"density") == 0)
03295         {
03296           GeometryInfo
03297             geometry_info;
03298 
03299           flags=ParseGeometry(value,&geometry_info);
03300           image->resolution.x=geometry_info.rho;
03301           image->resolution.y=geometry_info.sigma;
03302           if ((flags & SigmaValue) == 0)
03303             image->resolution.y=image->resolution.x;
03304         }
03305       if (LocaleCompare(property,"depth") == 0)
03306         {
03307           image->depth=StringToUnsignedLong(value);
03308           break;
03309         }
03310       if (LocaleCompare(property,"dispose") == 0)
03311         {
03312           ssize_t
03313             dispose;
03314 
03315           dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value);
03316           if (dispose < 0)
03317             break;
03318           image->dispose=(DisposeType) dispose;
03319           break;
03320         }
03321       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03322         ConstantString(property),ConstantString(value));
03323       break;
03324     }
03325     case 'G':
03326     case 'g':
03327     {
03328       if (LocaleCompare(property,"gravity") == 0)
03329         {
03330           ssize_t
03331             gravity;
03332 
03333           gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value);
03334           if (gravity < 0)
03335             break;
03336           image->gravity=(GravityType) gravity;
03337           break;
03338         }
03339       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03340         ConstantString(property),ConstantString(value));
03341       break;
03342     }
03343     case 'I':
03344     case 'i':
03345     {
03346       if (LocaleCompare(property,"intent") == 0)
03347         {
03348           ssize_t
03349             rendering_intent;
03350 
03351           rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
03352             value);
03353           if (rendering_intent < 0)
03354             break;
03355           image->rendering_intent=(RenderingIntent) rendering_intent;
03356           break;
03357         }
03358       if (LocaleCompare(property,"interpolate") == 0)
03359         {
03360           ssize_t
03361             interpolate;
03362 
03363           interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse,
03364             value);
03365           if (interpolate < 0)
03366             break;
03367           image->interpolate=(PixelInterpolateMethod) interpolate;
03368           break;
03369         }
03370       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03371         ConstantString(property),ConstantString(value));
03372       break;
03373     }
03374     case 'L':
03375     case 'l':
03376     {
03377       if (LocaleCompare(property,"loop") == 0)
03378         {
03379           image->iterations=StringToUnsignedLong(value);
03380           break;
03381         }
03382       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03383         ConstantString(property),ConstantString(value));
03384       break;
03385     }
03386     case 'P':
03387     case 'p':
03388     {
03389       if (LocaleCompare(property,"page") == 0)
03390         {
03391           char
03392             *geometry;
03393 
03394           geometry=GetPageGeometry(value);
03395           flags=ParseAbsoluteGeometry(geometry,&image->page);
03396           geometry=DestroyString(geometry);
03397           break;
03398         }
03399       if (LocaleCompare(property,"profile") == 0)
03400         {
03401           ImageInfo
03402             *image_info;
03403 
03404           StringInfo
03405             *profile;
03406 
03407           image_info=AcquireImageInfo();
03408           (void) CopyMagickString(image_info->filename,value,MaxTextExtent);
03409           (void) SetImageInfo(image_info,1,exception);
03410           profile=FileToStringInfo(image_info->filename,~0UL,exception);
03411           if (profile != (StringInfo *) NULL)
03412             status=SetImageProfile(image,image_info->magick,profile,exception);
03413           image_info=DestroyImageInfo(image_info);
03414           break;
03415         }
03416       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03417         ConstantString(property),ConstantString(value));
03418       break;
03419     }
03420     case 'R':
03421     case 'r':
03422     {
03423       if (LocaleCompare(property,"rendering-intent") == 0)
03424         {
03425           ssize_t
03426             rendering_intent;
03427 
03428           rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse,
03429             value);
03430           if (rendering_intent < 0)
03431             break;
03432           image->rendering_intent=(RenderingIntent) rendering_intent;
03433           break;
03434         }
03435       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03436         ConstantString(property),ConstantString(value));
03437       break;
03438     }
03439     case 'T':
03440     case 't':
03441     {
03442       if (LocaleCompare(property,"tile-offset") == 0)
03443         {
03444           char
03445             *geometry;
03446 
03447           geometry=GetPageGeometry(value);
03448           flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
03449           geometry=DestroyString(geometry);
03450           break;
03451         }
03452       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03453         ConstantString(property),ConstantString(value));
03454       break;
03455     }
03456     case 'U':
03457     case 'u':
03458     {
03459       if (LocaleCompare(property,"units") == 0)
03460         {
03461           ssize_t
03462             units;
03463 
03464           units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value);
03465           if (units < 0)
03466             break;
03467           image->units=(ResolutionType) units;
03468           break;
03469         }
03470       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03471         ConstantString(property),ConstantString(value));
03472       break;
03473     }
03474     default:
03475     {
03476       status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
03477         ConstantString(property),ConstantString(value));
03478       break;
03479     }
03480   }
03481   return(status);
03482 }