1 /* 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 % % 4 % % 5 % % 6 % PPPP RRRR OOO PPPP EEEEE RRRR TTTTT Y Y % 7 % P P R R O O P P E R R T Y Y % 8 % PPPP RRRR O O PPPP EEE RRRR T Y % 9 % P R R O O P E R R T Y % 10 % P R R OOO P EEEEE R R T Y % 11 % % 12 % % 13 % MagickCore Property Methods % 14 % % 15 % Software Design % 16 % Cristy % 17 % March 2000 % 18 % % 19 % % 20 % Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization % 21 % dedicated to making software imaging solutions freely available. % 22 % % 23 % You may not use this file except in compliance with the License. You may % 24 % obtain a copy of the License at % 25 % % 26 % http://www.imagemagick.org/script/license.php % 27 % % 28 % Unless required by applicable law or agreed to in writing, software % 29 % distributed under the License is distributed on an "AS IS" BASIS, % 30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 31 % See the License for the specific language governing permissions and % 32 % limitations under the License. % 33 % % 34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35 % 36 % 37 % 38 */ 39 40 /* 41 Include declarations. 42 */ 43 #include "MagickCore/studio.h" 44 #include "MagickCore/artifact.h" 45 #include "MagickCore/attribute.h" 46 #include "MagickCore/cache.h" 47 #include "MagickCore/cache-private.h" 48 #include "MagickCore/color.h" 49 #include "MagickCore/color-private.h" 50 #include "MagickCore/colorspace-private.h" 51 #include "MagickCore/compare.h" 52 #include "MagickCore/constitute.h" 53 #include "MagickCore/draw.h" 54 #include "MagickCore/effect.h" 55 #include "MagickCore/exception.h" 56 #include "MagickCore/exception-private.h" 57 #include "MagickCore/fx.h" 58 #include "MagickCore/fx-private.h" 59 #include "MagickCore/gem.h" 60 #include "MagickCore/geometry.h" 61 #include "MagickCore/histogram.h" 62 #include "MagickCore/image.h" 63 #include "MagickCore/layer.h" 64 #include "MagickCore/locale-private.h" 65 #include "MagickCore/list.h" 66 #include "MagickCore/magick.h" 67 #include "MagickCore/memory_.h" 68 #include "MagickCore/monitor.h" 69 #include "MagickCore/montage.h" 70 #include "MagickCore/option.h" 71 #include "MagickCore/policy.h" 72 #include "MagickCore/profile.h" 73 #include "MagickCore/property.h" 74 #include "MagickCore/quantum.h" 75 #include "MagickCore/resource_.h" 76 #include "MagickCore/splay-tree.h" 77 #include "MagickCore/signature.h" 78 #include "MagickCore/statistic.h" 79 #include "MagickCore/string_.h" 80 #include "MagickCore/string-private.h" 81 #include "MagickCore/token.h" 82 #include "MagickCore/token-private.h" 83 #include "MagickCore/utility.h" 84 #include "MagickCore/utility-private.h" 85 #include "MagickCore/version.h" 86 #include "MagickCore/xml-tree.h" 87 #include "MagickCore/xml-tree-private.h" 88 #if defined(MAGICKCORE_LCMS_DELEGATE) 89 #if defined(MAGICKCORE_HAVE_LCMS2_LCMS2_H) 90 #include <lcms2/lcms2.h> 91 #elif defined(MAGICKCORE_HAVE_LCMS2_H) 92 #include "lcms2.h" 93 #elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H) 94 #include <lcms/lcms.h> 95 #else 96 #include "lcms.h" 97 #endif 98 #endif 99 100 /* 102 Define declarations. 103 */ 104 #if defined(MAGICKCORE_LCMS_DELEGATE) 105 #if defined(LCMS_VERSION) && (LCMS_VERSION < 2000) 106 #define cmsUInt32Number DWORD 107 #endif 108 #endif 109 110 /* 112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 113 % % 114 % % 115 % % 116 % C l o n e I m a g e P r o p e r t i e s % 117 % % 118 % % 119 % % 120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 121 % 122 % CloneImageProperties() clones all the image properties to another image. 123 % 124 % The format of the CloneImageProperties method is: 125 % 126 % MagickBooleanType CloneImageProperties(Image *image, 127 % const Image *clone_image) 128 % 129 % A description of each parameter follows: 130 % 131 % o image: the image. 132 % 133 % o clone_image: the clone image. 134 % 135 */ 136 MagickExport MagickBooleanType CloneImageProperties(Image *image, 137 const Image *clone_image) 138 { 139 assert(image != (Image *) NULL); 140 assert(image->signature == MagickCoreSignature); 141 if (image->debug != MagickFalse) 142 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 143 assert(clone_image != (const Image *) NULL); 144 assert(clone_image->signature == MagickCoreSignature); 145 if (clone_image->debug != MagickFalse) 146 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 147 clone_image->filename); 148 (void) CopyMagickString(image->filename,clone_image->filename, 149 MagickPathExtent); 150 (void) CopyMagickString(image->magick_filename,clone_image->magick_filename, 151 MagickPathExtent); 152 image->compression=clone_image->compression; 153 image->quality=clone_image->quality; 154 image->depth=clone_image->depth; 155 image->alpha_color=clone_image->alpha_color; 156 image->background_color=clone_image->background_color; 157 image->border_color=clone_image->border_color; 158 image->transparent_color=clone_image->transparent_color; 159 image->gamma=clone_image->gamma; 160 image->chromaticity=clone_image->chromaticity; 161 image->rendering_intent=clone_image->rendering_intent; 162 image->black_point_compensation=clone_image->black_point_compensation; 163 image->units=clone_image->units; 164 image->montage=(char *) NULL; 165 image->directory=(char *) NULL; 166 (void) CloneString(&image->geometry,clone_image->geometry); 167 image->offset=clone_image->offset; 168 image->resolution.x=clone_image->resolution.x; 169 image->resolution.y=clone_image->resolution.y; 170 image->page=clone_image->page; 171 image->tile_offset=clone_image->tile_offset; 172 image->extract_info=clone_image->extract_info; 173 image->filter=clone_image->filter; 174 image->fuzz=clone_image->fuzz; 175 image->intensity=clone_image->intensity; 176 image->interlace=clone_image->interlace; 177 image->interpolate=clone_image->interpolate; 178 image->endian=clone_image->endian; 179 image->gravity=clone_image->gravity; 180 image->compose=clone_image->compose; 181 image->orientation=clone_image->orientation; 182 image->scene=clone_image->scene; 183 image->dispose=clone_image->dispose; 184 image->delay=clone_image->delay; 185 image->ticks_per_second=clone_image->ticks_per_second; 186 image->iterations=clone_image->iterations; 187 image->total_colors=clone_image->total_colors; 188 image->taint=clone_image->taint; 189 image->progress_monitor=clone_image->progress_monitor; 190 image->client_data=clone_image->client_data; 191 image->start_loop=clone_image->start_loop; 192 image->error=clone_image->error; 193 image->signature=clone_image->signature; 194 if (clone_image->properties != (void *) NULL) 195 { 196 if (image->properties != (void *) NULL) 197 DestroyImageProperties(image); 198 image->properties=CloneSplayTree((SplayTreeInfo *) 199 clone_image->properties,(void *(*)(void *)) ConstantString, 200 (void *(*)(void *)) ConstantString); 201 } 202 return(MagickTrue); 203 } 204 205 /* 207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 208 % % 209 % % 210 % % 211 % D e f i n e I m a g e P r o p e r t y % 212 % % 213 % % 214 % % 215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 216 % 217 % DefineImageProperty() associates an assignment string of the form 218 % "key=value" with an artifact or options. It is equivelent to 219 % SetImageProperty() 220 % 221 % The format of the DefineImageProperty method is: 222 % 223 % MagickBooleanType DefineImageProperty(Image *image,const char *property, 224 % ExceptionInfo *exception) 225 % 226 % A description of each parameter follows: 227 % 228 % o image: the image. 229 % 230 % o property: the image property. 231 % 232 % o exception: return any errors or warnings in this structure. 233 % 234 */ 235 MagickExport MagickBooleanType DefineImageProperty(Image *image, 236 const char *property,ExceptionInfo *exception) 237 { 238 char 239 key[MagickPathExtent], 240 value[MagickPathExtent]; 241 242 register char 243 *p; 244 245 assert(image != (Image *) NULL); 246 assert(property != (const char *) NULL); 247 (void) CopyMagickString(key,property,MagickPathExtent-1); 248 for (p=key; *p != '\0'; p++) 249 if (*p == '=') 250 break; 251 *value='\0'; 252 if (*p == '=') 253 (void) CopyMagickString(value,p+1,MagickPathExtent); 254 *p='\0'; 255 return(SetImageProperty(image,key,value,exception)); 256 } 257 258 /* 260 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 261 % % 262 % % 263 % % 264 % D e l e t e I m a g e P r o p e r t y % 265 % % 266 % % 267 % % 268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 269 % 270 % DeleteImageProperty() deletes an image property. 271 % 272 % The format of the DeleteImageProperty method is: 273 % 274 % MagickBooleanType DeleteImageProperty(Image *image,const char *property) 275 % 276 % A description of each parameter follows: 277 % 278 % o image: the image. 279 % 280 % o property: the image property. 281 % 282 */ 283 MagickExport MagickBooleanType DeleteImageProperty(Image *image, 284 const char *property) 285 { 286 assert(image != (Image *) NULL); 287 assert(image->signature == MagickCoreSignature); 288 if (image->debug != MagickFalse) 289 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 290 if (image->properties == (void *) NULL) 291 return(MagickFalse); 292 return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->properties,property)); 293 } 294 295 /* 297 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 298 % % 299 % % 300 % % 301 % D e s t r o y I m a g e P r o p e r t i e s % 302 % % 303 % % 304 % % 305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 306 % 307 % DestroyImageProperties() destroys all properties and associated memory 308 % attached to the given image. 309 % 310 % The format of the DestroyDefines method is: 311 % 312 % void DestroyImageProperties(Image *image) 313 % 314 % A description of each parameter follows: 315 % 316 % o image: the image. 317 % 318 */ 319 MagickExport void DestroyImageProperties(Image *image) 320 { 321 assert(image != (Image *) NULL); 322 assert(image->signature == MagickCoreSignature); 323 if (image->debug != MagickFalse) 324 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 325 if (image->properties != (void *) NULL) 326 image->properties=(void *) DestroySplayTree((SplayTreeInfo *) 327 image->properties); 328 } 329 330 /* 332 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 333 % % 334 % % 335 % % 336 % F o r m a t I m a g e P r o p e r t y % 337 % % 338 % % 339 % % 340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 341 % 342 % FormatImageProperty() permits formatted property/value pairs to be saved as 343 % an image property. 344 % 345 % The format of the FormatImageProperty method is: 346 % 347 % MagickBooleanType FormatImageProperty(Image *image,const char *property, 348 % const char *format,...) 349 % 350 % A description of each parameter follows. 351 % 352 % o image: The image. 353 % 354 % o property: The attribute property. 355 % 356 % o format: A string describing the format to use to write the remaining 357 % arguments. 358 % 359 */ 360 MagickExport MagickBooleanType FormatImageProperty(Image *image, 361 const char *property,const char *format,...) 362 { 363 char 364 value[MagickPathExtent]; 365 366 ExceptionInfo 367 *exception; 368 369 MagickBooleanType 370 status; 371 372 ssize_t 373 n; 374 375 va_list 376 operands; 377 378 va_start(operands,format); 379 n=FormatLocaleStringList(value,MagickPathExtent,format,operands); 380 (void) n; 381 va_end(operands); 382 exception=AcquireExceptionInfo(); 383 status=SetImageProperty(image,property,value,exception); 384 exception=DestroyExceptionInfo(exception); 385 return(status); 386 } 387 388 /* 390 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 391 % % 392 % % 393 % % 394 % G e t I m a g e P r o p e r t y % 395 % % 396 % % 397 % % 398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 399 % 400 % GetImageProperty() gets a value associated with an image property. 401 % 402 % This includes, profile prefixes, such as "exif:", "iptc:" and "8bim:" 403 % It does not handle non-prifile prefixes, such as "fx:", "option:", or 404 % "artifact:". 405 % 406 % The returned string is stored as a properity of the same name for faster 407 % lookup later. It should NOT be freed by the caller. 408 % 409 % The format of the GetImageProperty method is: 410 % 411 % const char *GetImageProperty(const Image *image,const char *key, 412 % ExceptionInfo *exception) 413 % 414 % A description of each parameter follows: 415 % 416 % o image: the image. 417 % 418 % o key: the key. 419 % 420 % o exception: return any errors or warnings in this structure. 421 % 422 */ 423 424 static char 425 *TracePSClippath(const unsigned char *,size_t), 426 *TraceSVGClippath(const unsigned char *,size_t,const size_t, 427 const size_t); 428 429 static MagickBooleanType GetIPTCProperty(const Image *image,const char *key, 430 ExceptionInfo *exception) 431 { 432 char 433 *attribute, 434 *message; 435 436 const StringInfo 437 *profile; 438 439 long 440 count, 441 dataset, 442 record; 443 444 register ssize_t 445 i; 446 447 size_t 448 length; 449 450 profile=GetImageProfile(image,"iptc"); 451 if (profile == (StringInfo *) NULL) 452 profile=GetImageProfile(image,"8bim"); 453 if (profile == (StringInfo *) NULL) 454 return(MagickFalse); 455 count=sscanf(key,"IPTC:%ld:%ld",&dataset,&record); 456 if (count != 2) 457 return(MagickFalse); 458 attribute=(char *) NULL; 459 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=(ssize_t) length) 460 { 461 length=1; 462 if ((ssize_t) GetStringInfoDatum(profile)[i] != 0x1c) 463 continue; 464 length=(size_t) (GetStringInfoDatum(profile)[i+3] << 8); 465 length|=GetStringInfoDatum(profile)[i+4]; 466 if (((long) GetStringInfoDatum(profile)[i+1] == dataset) && 467 ((long) GetStringInfoDatum(profile)[i+2] == record)) 468 { 469 message=(char *) NULL; 470 if (~length >= 1) 471 message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message)); 472 if (message != (char *) NULL) 473 { 474 (void) CopyMagickString(message,(char *) GetStringInfoDatum( 475 profile)+i+5,length+1); 476 (void) ConcatenateString(&attribute,message); 477 (void) ConcatenateString(&attribute,";"); 478 message=DestroyString(message); 479 } 480 } 481 i+=5; 482 } 483 if ((attribute == (char *) NULL) || (*attribute == ';')) 484 { 485 if (attribute != (char *) NULL) 486 attribute=DestroyString(attribute); 487 return(MagickFalse); 488 } 489 attribute[strlen(attribute)-1]='\0'; 490 (void) SetImageProperty((Image *) image,key,(const char *) attribute, 491 exception); 492 attribute=DestroyString(attribute); 493 return(MagickTrue); 494 } 495 496 static inline int ReadPropertyByte(const unsigned char **p,size_t *length) 497 { 498 int 499 c; 500 501 if (*length < 1) 502 return(EOF); 503 c=(int) (*(*p)++); 504 (*length)--; 505 return(c); 506 } 507 508 static inline signed int ReadPropertyMSBLong(const unsigned char **p, 509 size_t *length) 510 { 511 union 512 { 513 unsigned int 514 unsigned_value; 515 516 signed int 517 signed_value; 518 } quantum; 519 520 int 521 c; 522 523 register ssize_t 524 i; 525 526 unsigned char 527 buffer[4]; 528 529 unsigned int 530 value; 531 532 if (*length < 4) 533 return(-1); 534 for (i=0; i < 4; i++) 535 { 536 c=(int) (*(*p)++); 537 (*length)--; 538 buffer[i]=(unsigned char) c; 539 } 540 value=(unsigned int) buffer[0] << 24; 541 value|=(unsigned int) buffer[1] << 16; 542 value|=(unsigned int) buffer[2] << 8; 543 value|=(unsigned int) buffer[3]; 544 quantum.unsigned_value=value & 0xffffffff; 545 return(quantum.signed_value); 546 } 547 548 static inline signed short ReadPropertyMSBShort(const unsigned char **p, 549 size_t *length) 550 { 551 union 552 { 553 unsigned short 554 unsigned_value; 555 556 signed short 557 signed_value; 558 } quantum; 559 560 int 561 c; 562 563 register ssize_t 564 i; 565 566 unsigned char 567 buffer[2]; 568 569 unsigned short 570 value; 571 572 if (*length < 2) 573 return((unsigned short) ~0); 574 for (i=0; i < 2; i++) 575 { 576 c=(int) (*(*p)++); 577 (*length)--; 578 buffer[i]=(unsigned char) c; 579 } 580 value=(unsigned short) buffer[0] << 8; 581 value|=(unsigned short) buffer[1]; 582 quantum.unsigned_value=value & 0xffff; 583 return(quantum.signed_value); 584 } 585 586 static MagickBooleanType Get8BIMProperty(const Image *image,const char *key, 587 ExceptionInfo *exception) 588 { 589 char 590 *attribute, 591 format[MagickPathExtent], 592 name[MagickPathExtent], 593 *resource; 594 595 const StringInfo 596 *profile; 597 598 const unsigned char 599 *info; 600 601 long 602 start, 603 stop; 604 605 MagickBooleanType 606 status; 607 608 register ssize_t 609 i; 610 611 size_t 612 length; 613 614 ssize_t 615 count, 616 id, 617 sub_number; 618 619 /* 620 There are no newlines in path names, so it's safe as terminator. 621 */ 622 profile=GetImageProfile(image,"8bim"); 623 if (profile == (StringInfo *) NULL) 624 return(MagickFalse); 625 count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%1024[^\n]\n%1024[^\n]",&start,&stop, 626 name,format); 627 if ((count != 2) && (count != 3) && (count != 4)) 628 return(MagickFalse); 629 if (count < 4) 630 (void) CopyMagickString(format,"SVG",MagickPathExtent); 631 if (count < 3) 632 *name='\0'; 633 sub_number=1; 634 if (*name == '#') 635 sub_number=(ssize_t) StringToLong(&name[1]); 636 sub_number=MagickMax(sub_number,1L); 637 resource=(char *) NULL; 638 status=MagickFalse; 639 length=GetStringInfoLength(profile); 640 info=GetStringInfoDatum(profile); 641 while ((length > 0) && (status == MagickFalse)) 642 { 643 if (ReadPropertyByte(&info,&length) != (unsigned char) '8') 644 continue; 645 if (ReadPropertyByte(&info,&length) != (unsigned char) 'B') 646 continue; 647 if (ReadPropertyByte(&info,&length) != (unsigned char) 'I') 648 continue; 649 if (ReadPropertyByte(&info,&length) != (unsigned char) 'M') 650 continue; 651 id=(ssize_t) ReadPropertyMSBShort(&info,&length); 652 if (id < (ssize_t) start) 653 continue; 654 if (id > (ssize_t) stop) 655 continue; 656 if (resource != (char *) NULL) 657 resource=DestroyString(resource); 658 count=(ssize_t) ReadPropertyByte(&info,&length); 659 if ((count != 0) && ((size_t) count <= length)) 660 { 661 resource=(char *) NULL; 662 if (~((size_t) count) >= (MagickPathExtent-1)) 663 resource=(char *) AcquireQuantumMemory((size_t) count+ 664 MagickPathExtent,sizeof(*resource)); 665 if (resource != (char *) NULL) 666 { 667 for (i=0; i < (ssize_t) count; i++) 668 resource[i]=(char) ReadPropertyByte(&info,&length); 669 resource[count]='\0'; 670 } 671 } 672 if ((count & 0x01) == 0) 673 (void) ReadPropertyByte(&info,&length); 674 count=(ssize_t) ReadPropertyMSBLong(&info,&length); 675 if ((*name != '\0') && (*name != '#')) 676 if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0)) 677 { 678 /* 679 No name match, scroll forward and try next. 680 */ 681 info+=count; 682 length-=MagickMin(count,(ssize_t) length); 683 continue; 684 } 685 if ((*name == '#') && (sub_number != 1)) 686 { 687 /* 688 No numbered match, scroll forward and try next. 689 */ 690 sub_number--; 691 info+=count; 692 length-=MagickMin(count,(ssize_t) length); 693 continue; 694 } 695 /* 696 We have the resource of interest. 697 */ 698 attribute=(char *) NULL; 699 if (~((size_t) count) >= (MagickPathExtent-1)) 700 attribute=(char *) AcquireQuantumMemory((size_t) count+MagickPathExtent, 701 sizeof(*attribute)); 702 if (attribute != (char *) NULL) 703 { 704 (void) CopyMagickMemory(attribute,(char *) info,(size_t) count); 705 attribute[count]='\0'; 706 info+=count; 707 length-=MagickMin(count,(ssize_t) length); 708 if ((id <= 1999) || (id >= 2999)) 709 (void) SetImageProperty((Image *) image,key,(const char *) 710 attribute,exception); 711 else 712 { 713 char 714 *path; 715 716 if (LocaleCompare(format,"svg") == 0) 717 path=TraceSVGClippath((unsigned char *) attribute,(size_t) count, 718 image->columns,image->rows); 719 else 720 path=TracePSClippath((unsigned char *) attribute,(size_t) count); 721 (void) SetImageProperty((Image *) image,key,(const char *) path, 722 exception); 723 path=DestroyString(path); 724 } 725 attribute=DestroyString(attribute); 726 status=MagickTrue; 727 } 728 } 729 if (resource != (char *) NULL) 730 resource=DestroyString(resource); 731 return(status); 732 } 733 734 static inline signed int ReadPropertySignedLong(const EndianType endian, 735 const unsigned char *buffer) 736 { 737 union 738 { 739 unsigned int 740 unsigned_value; 741 742 signed int 743 signed_value; 744 } quantum; 745 746 unsigned int 747 value; 748 749 if (endian == LSBEndian) 750 { 751 value=(unsigned int) buffer[3] << 24; 752 value|=(unsigned int) buffer[2] << 16; 753 value|=(unsigned int) buffer[1] << 8; 754 value|=(unsigned int) buffer[0]; 755 quantum.unsigned_value=value & 0xffffffff; 756 return(quantum.signed_value); 757 } 758 value=(unsigned int) buffer[0] << 24; 759 value|=(unsigned int) buffer[1] << 16; 760 value|=(unsigned int) buffer[2] << 8; 761 value|=(unsigned int) buffer[3]; 762 quantum.unsigned_value=value & 0xffffffff; 763 return(quantum.signed_value); 764 } 765 766 static inline unsigned int ReadPropertyUnsignedLong(const EndianType endian, 767 const unsigned char *buffer) 768 { 769 unsigned int 770 value; 771 772 if (endian == LSBEndian) 773 { 774 value=(unsigned int) buffer[3] << 24; 775 value|=(unsigned int) buffer[2] << 16; 776 value|=(unsigned int) buffer[1] << 8; 777 value|=(unsigned int) buffer[0]; 778 return(value & 0xffffffff); 779 } 780 value=(unsigned int) buffer[0] << 24; 781 value|=(unsigned int) buffer[1] << 16; 782 value|=(unsigned int) buffer[2] << 8; 783 value|=(unsigned int) buffer[3]; 784 return(value & 0xffffffff); 785 } 786 787 static inline signed short ReadPropertySignedShort(const EndianType endian, 788 const unsigned char *buffer) 789 { 790 union 791 { 792 unsigned short 793 unsigned_value; 794 795 signed short 796 signed_value; 797 } quantum; 798 799 unsigned short 800 value; 801 802 if (endian == LSBEndian) 803 { 804 value=(unsigned short) buffer[1] << 8; 805 value|=(unsigned short) buffer[0]; 806 quantum.unsigned_value=value & 0xffff; 807 return(quantum.signed_value); 808 } 809 value=(unsigned short) buffer[0] << 8; 810 value|=(unsigned short) buffer[1]; 811 quantum.unsigned_value=value & 0xffff; 812 return(quantum.signed_value); 813 } 814 815 static inline unsigned short ReadPropertyUnsignedShort(const EndianType endian, 816 const unsigned char *buffer) 817 { 818 unsigned short 819 value; 820 821 if (endian == LSBEndian) 822 { 823 value=(unsigned short) buffer[1] << 8; 824 value|=(unsigned short) buffer[0]; 825 return(value & 0xffff); 826 } 827 value=(unsigned short) buffer[0] << 8; 828 value|=(unsigned short) buffer[1]; 829 return(value & 0xffff); 830 } 831 832 static MagickBooleanType GetEXIFProperty(const Image *image, 833 const char *property,ExceptionInfo *exception) 834 { 835 #define MaxDirectoryStack 16 836 #define EXIF_DELIMITER "\n" 837 #define EXIF_NUM_FORMATS 12 838 #define EXIF_FMT_BYTE 1 839 #define EXIF_FMT_STRING 2 840 #define EXIF_FMT_USHORT 3 841 #define EXIF_FMT_ULONG 4 842 #define EXIF_FMT_URATIONAL 5 843 #define EXIF_FMT_SBYTE 6 844 #define EXIF_FMT_UNDEFINED 7 845 #define EXIF_FMT_SSHORT 8 846 #define EXIF_FMT_SLONG 9 847 #define EXIF_FMT_SRATIONAL 10 848 #define EXIF_FMT_SINGLE 11 849 #define EXIF_FMT_DOUBLE 12 850 #define TAG_EXIF_OFFSET 0x8769 851 #define TAG_GPS_OFFSET 0x8825 852 #define TAG_INTEROP_OFFSET 0xa005 853 854 #define EXIFMultipleValues(size,format,arg) \ 855 { \ 856 ssize_t \ 857 component; \ 858 \ 859 size_t \ 860 length; \ 861 \ 862 unsigned char \ 863 *p1; \ 864 \ 865 length=0; \ 866 p1=p; \ 867 for (component=0; component < components; component++) \ 868 { \ 869 length+=FormatLocaleString(buffer+length,MagickPathExtent-length, \ 870 format", ",arg); \ 871 if (length >= (MagickPathExtent-1)) \ 872 length=MagickPathExtent-1; \ 873 p1+=size; \ 874 } \ 875 if (length > 1) \ 876 buffer[length-2]='\0'; \ 877 value=AcquireString(buffer); \ 878 } 879 880 #define EXIFMultipleFractions(size,format,arg1,arg2) \ 881 { \ 882 ssize_t \ 883 component; \ 884 \ 885 size_t \ 886 length; \ 887 \ 888 unsigned char \ 889 *p1; \ 890 \ 891 length=0; \ 892 p1=p; \ 893 for (component=0; component < components; component++) \ 894 { \ 895 length+=FormatLocaleString(buffer+length,MagickPathExtent-length, \ 896 format", ",(arg1),(arg2)); \ 897 if (length >= (MagickPathExtent-1)) \ 898 length=MagickPathExtent-1; \ 899 p1+=size; \ 900 } \ 901 if (length > 1) \ 902 buffer[length-2]='\0'; \ 903 value=AcquireString(buffer); \ 904 } 905 906 typedef struct _DirectoryInfo 907 { 908 const unsigned char 909 *directory; 910 911 size_t 912 entry; 913 914 ssize_t 915 offset; 916 } DirectoryInfo; 917 918 typedef struct _TagInfo 919 { 920 size_t 921 tag; 922 923 const char 924 *description; 925 } TagInfo; 926 927 static TagInfo 928 EXIFTag[] = 929 { 930 { 0x001, "exif:InteroperabilityIndex" }, 931 { 0x002, "exif:InteroperabilityVersion" }, 932 { 0x100, "exif:ImageWidth" }, 933 { 0x101, "exif:ImageLength" }, 934 { 0x102, "exif:BitsPerSample" }, 935 { 0x103, "exif:Compression" }, 936 { 0x106, "exif:PhotometricInterpretation" }, 937 { 0x10a, "exif:FillOrder" }, 938 { 0x10d, "exif:DocumentName" }, 939 { 0x10e, "exif:ImageDescription" }, 940 { 0x10f, "exif:Make" }, 941 { 0x110, "exif:Model" }, 942 { 0x111, "exif:StripOffsets" }, 943 { 0x112, "exif:Orientation" }, 944 { 0x115, "exif:SamplesPerPixel" }, 945 { 0x116, "exif:RowsPerStrip" }, 946 { 0x117, "exif:StripByteCounts" }, 947 { 0x11a, "exif:XResolution" }, 948 { 0x11b, "exif:YResolution" }, 949 { 0x11c, "exif:PlanarConfiguration" }, 950 { 0x11d, "exif:PageName" }, 951 { 0x11e, "exif:XPosition" }, 952 { 0x11f, "exif:YPosition" }, 953 { 0x118, "exif:MinSampleValue" }, 954 { 0x119, "exif:MaxSampleValue" }, 955 { 0x120, "exif:FreeOffsets" }, 956 { 0x121, "exif:FreeByteCounts" }, 957 { 0x122, "exif:GrayResponseUnit" }, 958 { 0x123, "exif:GrayResponseCurve" }, 959 { 0x124, "exif:T4Options" }, 960 { 0x125, "exif:T6Options" }, 961 { 0x128, "exif:ResolutionUnit" }, 962 { 0x12d, "exif:TransferFunction" }, 963 { 0x131, "exif:Software" }, 964 { 0x132, "exif:DateTime" }, 965 { 0x13b, "exif:Artist" }, 966 { 0x13e, "exif:WhitePoint" }, 967 { 0x13f, "exif:PrimaryChromaticities" }, 968 { 0x140, "exif:ColorMap" }, 969 { 0x141, "exif:HalfToneHints" }, 970 { 0x142, "exif:TileWidth" }, 971 { 0x143, "exif:TileLength" }, 972 { 0x144, "exif:TileOffsets" }, 973 { 0x145, "exif:TileByteCounts" }, 974 { 0x14a, "exif:SubIFD" }, 975 { 0x14c, "exif:InkSet" }, 976 { 0x14d, "exif:InkNames" }, 977 { 0x14e, "exif:NumberOfInks" }, 978 { 0x150, "exif:DotRange" }, 979 { 0x151, "exif:TargetPrinter" }, 980 { 0x152, "exif:ExtraSample" }, 981 { 0x153, "exif:SampleFormat" }, 982 { 0x154, "exif:SMinSampleValue" }, 983 { 0x155, "exif:SMaxSampleValue" }, 984 { 0x156, "exif:TransferRange" }, 985 { 0x157, "exif:ClipPath" }, 986 { 0x158, "exif:XClipPathUnits" }, 987 { 0x159, "exif:YClipPathUnits" }, 988 { 0x15a, "exif:Indexed" }, 989 { 0x15b, "exif:JPEGTables" }, 990 { 0x15f, "exif:OPIProxy" }, 991 { 0x200, "exif:JPEGProc" }, 992 { 0x201, "exif:JPEGInterchangeFormat" }, 993 { 0x202, "exif:JPEGInterchangeFormatLength" }, 994 { 0x203, "exif:JPEGRestartInterval" }, 995 { 0x205, "exif:JPEGLosslessPredictors" }, 996 { 0x206, "exif:JPEGPointTransforms" }, 997 { 0x207, "exif:JPEGQTables" }, 998 { 0x208, "exif:JPEGDCTables" }, 999 { 0x209, "exif:JPEGACTables" }, 1000 { 0x211, "exif:YCbCrCoefficients" }, 1001 { 0x212, "exif:YCbCrSubSampling" }, 1002 { 0x213, "exif:YCbCrPositioning" }, 1003 { 0x214, "exif:ReferenceBlackWhite" }, 1004 { 0x2bc, "exif:ExtensibleMetadataPlatform" }, 1005 { 0x301, "exif:Gamma" }, 1006 { 0x302, "exif:ICCProfileDescriptor" }, 1007 { 0x303, "exif:SRGBRenderingIntent" }, 1008 { 0x320, "exif:ImageTitle" }, 1009 { 0x5001, "exif:ResolutionXUnit" }, 1010 { 0x5002, "exif:ResolutionYUnit" }, 1011 { 0x5003, "exif:ResolutionXLengthUnit" }, 1012 { 0x5004, "exif:ResolutionYLengthUnit" }, 1013 { 0x5005, "exif:PrintFlags" }, 1014 { 0x5006, "exif:PrintFlagsVersion" }, 1015 { 0x5007, "exif:PrintFlagsCrop" }, 1016 { 0x5008, "exif:PrintFlagsBleedWidth" }, 1017 { 0x5009, "exif:PrintFlagsBleedWidthScale" }, 1018 { 0x500A, "exif:HalftoneLPI" }, 1019 { 0x500B, "exif:HalftoneLPIUnit" }, 1020 { 0x500C, "exif:HalftoneDegree" }, 1021 { 0x500D, "exif:HalftoneShape" }, 1022 { 0x500E, "exif:HalftoneMisc" }, 1023 { 0x500F, "exif:HalftoneScreen" }, 1024 { 0x5010, "exif:JPEGQuality" }, 1025 { 0x5011, "exif:GridSize" }, 1026 { 0x5012, "exif:ThumbnailFormat" }, 1027 { 0x5013, "exif:ThumbnailWidth" }, 1028 { 0x5014, "exif:ThumbnailHeight" }, 1029 { 0x5015, "exif:ThumbnailColorDepth" }, 1030 { 0x5016, "exif:ThumbnailPlanes" }, 1031 { 0x5017, "exif:ThumbnailRawBytes" }, 1032 { 0x5018, "exif:ThumbnailSize" }, 1033 { 0x5019, "exif:ThumbnailCompressedSize" }, 1034 { 0x501a, "exif:ColorTransferFunction" }, 1035 { 0x501b, "exif:ThumbnailData" }, 1036 { 0x5020, "exif:ThumbnailImageWidth" }, 1037 { 0x5021, "exif:ThumbnailImageHeight" }, 1038 { 0x5022, "exif:ThumbnailBitsPerSample" }, 1039 { 0x5023, "exif:ThumbnailCompression" }, 1040 { 0x5024, "exif:ThumbnailPhotometricInterp" }, 1041 { 0x5025, "exif:ThumbnailImageDescription" }, 1042 { 0x5026, "exif:ThumbnailEquipMake" }, 1043 { 0x5027, "exif:ThumbnailEquipModel" }, 1044 { 0x5028, "exif:ThumbnailStripOffsets" }, 1045 { 0x5029, "exif:ThumbnailOrientation" }, 1046 { 0x502a, "exif:ThumbnailSamplesPerPixel" }, 1047 { 0x502b, "exif:ThumbnailRowsPerStrip" }, 1048 { 0x502c, "exif:ThumbnailStripBytesCount" }, 1049 { 0x502d, "exif:ThumbnailResolutionX" }, 1050 { 0x502e, "exif:ThumbnailResolutionY" }, 1051 { 0x502f, "exif:ThumbnailPlanarConfig" }, 1052 { 0x5030, "exif:ThumbnailResolutionUnit" }, 1053 { 0x5031, "exif:ThumbnailTransferFunction" }, 1054 { 0x5032, "exif:ThumbnailSoftwareUsed" }, 1055 { 0x5033, "exif:ThumbnailDateTime" }, 1056 { 0x5034, "exif:ThumbnailArtist" }, 1057 { 0x5035, "exif:ThumbnailWhitePoint" }, 1058 { 0x5036, "exif:ThumbnailPrimaryChromaticities" }, 1059 { 0x5037, "exif:ThumbnailYCbCrCoefficients" }, 1060 { 0x5038, "exif:ThumbnailYCbCrSubsampling" }, 1061 { 0x5039, "exif:ThumbnailYCbCrPositioning" }, 1062 { 0x503A, "exif:ThumbnailRefBlackWhite" }, 1063 { 0x503B, "exif:ThumbnailCopyRight" }, 1064 { 0x5090, "exif:LuminanceTable" }, 1065 { 0x5091, "exif:ChrominanceTable" }, 1066 { 0x5100, "exif:FrameDelay" }, 1067 { 0x5101, "exif:LoopCount" }, 1068 { 0x5110, "exif:PixelUnit" }, 1069 { 0x5111, "exif:PixelPerUnitX" }, 1070 { 0x5112, "exif:PixelPerUnitY" }, 1071 { 0x5113, "exif:PaletteHistogram" }, 1072 { 0x1000, "exif:RelatedImageFileFormat" }, 1073 { 0x1001, "exif:RelatedImageLength" }, 1074 { 0x1002, "exif:RelatedImageWidth" }, 1075 { 0x800d, "exif:ImageID" }, 1076 { 0x80e3, "exif:Matteing" }, 1077 { 0x80e4, "exif:DataType" }, 1078 { 0x80e5, "exif:ImageDepth" }, 1079 { 0x80e6, "exif:TileDepth" }, 1080 { 0x828d, "exif:CFARepeatPatternDim" }, 1081 { 0x828e, "exif:CFAPattern2" }, 1082 { 0x828f, "exif:BatteryLevel" }, 1083 { 0x8298, "exif:Copyright" }, 1084 { 0x829a, "exif:ExposureTime" }, 1085 { 0x829d, "exif:FNumber" }, 1086 { 0x83bb, "exif:IPTC/NAA" }, 1087 { 0x84e3, "exif:IT8RasterPadding" }, 1088 { 0x84e5, "exif:IT8ColorTable" }, 1089 { 0x8649, "exif:ImageResourceInformation" }, 1090 { 0x8769, "exif:ExifOffset" }, 1091 { 0x8773, "exif:InterColorProfile" }, 1092 { 0x8822, "exif:ExposureProgram" }, 1093 { 0x8824, "exif:SpectralSensitivity" }, 1094 { 0x8825, "exif:GPSInfo" }, 1095 { 0x8827, "exif:ISOSpeedRatings" }, 1096 { 0x8828, "exif:OECF" }, 1097 { 0x8829, "exif:Interlace" }, 1098 { 0x882a, "exif:TimeZoneOffset" }, 1099 { 0x882b, "exif:SelfTimerMode" }, 1100 { 0x9000, "exif:ExifVersion" }, 1101 { 0x9003, "exif:DateTimeOriginal" }, 1102 { 0x9004, "exif:DateTimeDigitized" }, 1103 { 0x9101, "exif:ComponentsConfiguration" }, 1104 { 0x9102, "exif:CompressedBitsPerPixel" }, 1105 { 0x9201, "exif:ShutterSpeedValue" }, 1106 { 0x9202, "exif:ApertureValue" }, 1107 { 0x9203, "exif:BrightnessValue" }, 1108 { 0x9204, "exif:ExposureBiasValue" }, 1109 { 0x9205, "exif:MaxApertureValue" }, 1110 { 0x9206, "exif:SubjectDistance" }, 1111 { 0x9207, "exif:MeteringMode" }, 1112 { 0x9208, "exif:LightSource" }, 1113 { 0x9209, "exif:Flash" }, 1114 { 0x920a, "exif:FocalLength" }, 1115 { 0x920b, "exif:FlashEnergy" }, 1116 { 0x920c, "exif:SpatialFrequencyResponse" }, 1117 { 0x920d, "exif:Noise" }, 1118 { 0x9211, "exif:ImageNumber" }, 1119 { 0x9212, "exif:SecurityClassification" }, 1120 { 0x9213, "exif:ImageHistory" }, 1121 { 0x9214, "exif:SubjectArea" }, 1122 { 0x9215, "exif:ExposureIndex" }, 1123 { 0x9216, "exif:TIFF-EPStandardID" }, 1124 { 0x927c, "exif:MakerNote" }, 1125 { 0x9C9b, "exif:WinXP-Title" }, 1126 { 0x9C9c, "exif:WinXP-Comments" }, 1127 { 0x9C9d, "exif:WinXP-Author" }, 1128 { 0x9C9e, "exif:WinXP-Keywords" }, 1129 { 0x9C9f, "exif:WinXP-Subject" }, 1130 { 0x9286, "exif:UserComment" }, 1131 { 0x9290, "exif:SubSecTime" }, 1132 { 0x9291, "exif:SubSecTimeOriginal" }, 1133 { 0x9292, "exif:SubSecTimeDigitized" }, 1134 { 0xa000, "exif:FlashPixVersion" }, 1135 { 0xa001, "exif:ColorSpace" }, 1136 { 0xa002, "exif:ExifImageWidth" }, 1137 { 0xa003, "exif:ExifImageLength" }, 1138 { 0xa004, "exif:RelatedSoundFile" }, 1139 { 0xa005, "exif:InteroperabilityOffset" }, 1140 { 0xa20b, "exif:FlashEnergy" }, 1141 { 0xa20c, "exif:SpatialFrequencyResponse" }, 1142 { 0xa20d, "exif:Noise" }, 1143 { 0xa20e, "exif:FocalPlaneXResolution" }, 1144 { 0xa20f, "exif:FocalPlaneYResolution" }, 1145 { 0xa210, "exif:FocalPlaneResolutionUnit" }, 1146 { 0xa214, "exif:SubjectLocation" }, 1147 { 0xa215, "exif:ExposureIndex" }, 1148 { 0xa216, "exif:TIFF/EPStandardID" }, 1149 { 0xa217, "exif:SensingMethod" }, 1150 { 0xa300, "exif:FileSource" }, 1151 { 0xa301, "exif:SceneType" }, 1152 { 0xa302, "exif:CFAPattern" }, 1153 { 0xa401, "exif:CustomRendered" }, 1154 { 0xa402, "exif:ExposureMode" }, 1155 { 0xa403, "exif:WhiteBalance" }, 1156 { 0xa404, "exif:DigitalZoomRatio" }, 1157 { 0xa405, "exif:FocalLengthIn35mmFilm" }, 1158 { 0xa406, "exif:SceneCaptureType" }, 1159 { 0xa407, "exif:GainControl" }, 1160 { 0xa408, "exif:Contrast" }, 1161 { 0xa409, "exif:Saturation" }, 1162 { 0xa40a, "exif:Sharpness" }, 1163 { 0xa40b, "exif:DeviceSettingDescription" }, 1164 { 0xa40c, "exif:SubjectDistanceRange" }, 1165 { 0xa420, "exif:ImageUniqueID" }, 1166 { 0xc4a5, "exif:PrintImageMatching" }, 1167 { 0xa500, "exif:Gamma" }, 1168 { 0xc640, "exif:CR2Slice" }, 1169 { 0x10000, "exif:GPSVersionID" }, 1170 { 0x10001, "exif:GPSLatitudeRef" }, 1171 { 0x10002, "exif:GPSLatitude" }, 1172 { 0x10003, "exif:GPSLongitudeRef" }, 1173 { 0x10004, "exif:GPSLongitude" }, 1174 { 0x10005, "exif:GPSAltitudeRef" }, 1175 { 0x10006, "exif:GPSAltitude" }, 1176 { 0x10007, "exif:GPSTimeStamp" }, 1177 { 0x10008, "exif:GPSSatellites" }, 1178 { 0x10009, "exif:GPSStatus" }, 1179 { 0x1000a, "exif:GPSMeasureMode" }, 1180 { 0x1000b, "exif:GPSDop" }, 1181 { 0x1000c, "exif:GPSSpeedRef" }, 1182 { 0x1000d, "exif:GPSSpeed" }, 1183 { 0x1000e, "exif:GPSTrackRef" }, 1184 { 0x1000f, "exif:GPSTrack" }, 1185 { 0x10010, "exif:GPSImgDirectionRef" }, 1186 { 0x10011, "exif:GPSImgDirection" }, 1187 { 0x10012, "exif:GPSMapDatum" }, 1188 { 0x10013, "exif:GPSDestLatitudeRef" }, 1189 { 0x10014, "exif:GPSDestLatitude" }, 1190 { 0x10015, "exif:GPSDestLongitudeRef" }, 1191 { 0x10016, "exif:GPSDestLongitude" }, 1192 { 0x10017, "exif:GPSDestBearingRef" }, 1193 { 0x10018, "exif:GPSDestBearing" }, 1194 { 0x10019, "exif:GPSDestDistanceRef" }, 1195 { 0x1001a, "exif:GPSDestDistance" }, 1196 { 0x1001b, "exif:GPSProcessingMethod" }, 1197 { 0x1001c, "exif:GPSAreaInformation" }, 1198 { 0x1001d, "exif:GPSDateStamp" }, 1199 { 0x1001e, "exif:GPSDifferential" }, 1200 { 0x00000, (const char *) NULL } 1201 }; 1202 1203 const StringInfo 1204 *profile; 1205 1206 const unsigned char 1207 *directory, 1208 *exif; 1209 1210 DirectoryInfo 1211 directory_stack[MaxDirectoryStack]; 1212 1213 EndianType 1214 endian; 1215 1216 MagickBooleanType 1217 status; 1218 1219 register ssize_t 1220 i; 1221 1222 size_t 1223 entry, 1224 length, 1225 number_entries, 1226 tag, 1227 tag_value; 1228 1229 SplayTreeInfo 1230 *exif_resources; 1231 1232 ssize_t 1233 all, 1234 id, 1235 level, 1236 offset, 1237 tag_offset; 1238 1239 static int 1240 tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8}; 1241 1242 /* 1243 If EXIF data exists, then try to parse the request for a tag. 1244 */ 1245 profile=GetImageProfile(image,"exif"); 1246 if (profile == (const StringInfo *) NULL) 1247 return(MagickFalse); 1248 if ((property == (const char *) NULL) || (*property == '\0')) 1249 return(MagickFalse); 1250 while (isspace((int) ((unsigned char) *property)) != 0) 1251 property++; 1252 if (strlen(property) <= 5) 1253 return(MagickFalse); 1254 all=0; 1255 tag=(~0UL); 1256 switch (*(property+5)) 1257 { 1258 case '*': 1259 { 1260 /* 1261 Caller has asked for all the tags in the EXIF data. 1262 */ 1263 tag=0; 1264 all=1; /* return the data in description=value format */ 1265 break; 1266 } 1267 case '!': 1268 { 1269 tag=0; 1270 all=2; /* return the data in tagid=value format */ 1271 break; 1272 } 1273 case '#': 1274 case '@': 1275 { 1276 int 1277 c; 1278 1279 size_t 1280 n; 1281 1282 /* 1283 Check for a hex based tag specification first. 1284 */ 1285 tag=(*(property+5) == '@') ? 1UL : 0UL; 1286 property+=6; 1287 n=strlen(property); 1288 if (n != 4) 1289 return(MagickFalse); 1290 /* 1291 Parse tag specification as a hex number. 1292 */ 1293 n/=4; 1294 do 1295 { 1296 for (i=(ssize_t) n-1L; i >= 0; i--) 1297 { 1298 c=(*property++); 1299 tag<<=4; 1300 if ((c >= '0') && (c <= '9')) 1301 tag|=(c-'0'); 1302 else 1303 if ((c >= 'A') && (c <= 'F')) 1304 tag|=(c-('A'-10)); 1305 else 1306 if ((c >= 'a') && (c <= 'f')) 1307 tag|=(c-('a'-10)); 1308 else 1309 return(MagickFalse); 1310 } 1311 } while (*property != '\0'); 1312 break; 1313 } 1314 default: 1315 { 1316 /* 1317 Try to match the text with a tag name instead. 1318 */ 1319 for (i=0; ; i++) 1320 { 1321 if (EXIFTag[i].tag == 0) 1322 break; 1323 if (LocaleCompare(EXIFTag[i].description,property) == 0) 1324 { 1325 tag=(size_t) EXIFTag[i].tag; 1326 break; 1327 } 1328 } 1329 break; 1330 } 1331 } 1332 if (tag == (~0UL)) 1333 return(MagickFalse); 1334 length=GetStringInfoLength(profile); 1335 exif=GetStringInfoDatum(profile); 1336 while (length != 0) 1337 { 1338 if (ReadPropertyByte(&exif,&length) != 0x45) 1339 continue; 1340 if (ReadPropertyByte(&exif,&length) != 0x78) 1341 continue; 1342 if (ReadPropertyByte(&exif,&length) != 0x69) 1343 continue; 1344 if (ReadPropertyByte(&exif,&length) != 0x66) 1345 continue; 1346 if (ReadPropertyByte(&exif,&length) != 0x00) 1347 continue; 1348 if (ReadPropertyByte(&exif,&length) != 0x00) 1349 continue; 1350 break; 1351 } 1352 if (length < 16) 1353 return(MagickFalse); 1354 id=(ssize_t) ReadPropertySignedShort(LSBEndian,exif); 1355 endian=LSBEndian; 1356 if (id == 0x4949) 1357 endian=LSBEndian; 1358 else 1359 if (id == 0x4D4D) 1360 endian=MSBEndian; 1361 else 1362 return(MagickFalse); 1363 if (ReadPropertyUnsignedShort(endian,exif+2) != 0x002a) 1364 return(MagickFalse); 1365 /* 1366 This the offset to the first IFD. 1367 */ 1368 offset=(ssize_t) ReadPropertySignedLong(endian,exif+4); 1369 if ((offset < 0) || (size_t) offset >= length) 1370 return(MagickFalse); 1371 /* 1372 Set the pointer to the first IFD and follow it were it leads. 1373 */ 1374 status=MagickFalse; 1375 directory=exif+offset; 1376 level=0; 1377 entry=0; 1378 tag_offset=0; 1379 exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL, 1380 (void *(*)(void *)) NULL,(void *(*)(void *)) NULL); 1381 do 1382 { 1383 /* 1384 If there is anything on the stack then pop it off. 1385 */ 1386 if (level > 0) 1387 { 1388 level--; 1389 directory=directory_stack[level].directory; 1390 entry=directory_stack[level].entry; 1391 tag_offset=directory_stack[level].offset; 1392 } 1393 if ((directory < exif) || (directory > (exif+length-2))) 1394 break; 1395 /* 1396 Determine how many entries there are in the current IFD. 1397 */ 1398 number_entries=(size_t) ReadPropertyUnsignedShort(endian,directory); 1399 for ( ; entry < number_entries; entry++) 1400 { 1401 register unsigned char 1402 *p, 1403 *q; 1404 1405 size_t 1406 format; 1407 1408 ssize_t 1409 number_bytes, 1410 components; 1411 1412 q=(unsigned char *) (directory+(12*entry)+2); 1413 if (q > (exif+length-12)) 1414 break; /* corrupt EXIF */ 1415 if (GetValueFromSplayTree(exif_resources,q) == q) 1416 break; 1417 (void) AddValueToSplayTree(exif_resources,q,q); 1418 tag_value=(size_t) ReadPropertyUnsignedShort(endian,q)+tag_offset; 1419 format=(size_t) ReadPropertyUnsignedShort(endian,q+2); 1420 if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes))) 1421 break; 1422 components=(ssize_t) ReadPropertySignedLong(endian,q+4); 1423 if (components < 0) 1424 break; /* corrupt EXIF */ 1425 number_bytes=(size_t) components*tag_bytes[format]; 1426 if (number_bytes < components) 1427 break; /* prevent overflow */ 1428 if (number_bytes <= 4) 1429 p=q+8; 1430 else 1431 { 1432 ssize_t 1433 offset; 1434 1435 /* 1436 The directory entry contains an offset. 1437 */ 1438 offset=(ssize_t) ReadPropertySignedLong(endian,q+8); 1439 if ((offset < 0) || (size_t) offset >= length) 1440 continue; 1441 if ((ssize_t) (offset+number_bytes) < offset) 1442 continue; /* prevent overflow */ 1443 if ((size_t) (offset+number_bytes) > length) 1444 continue; 1445 p=(unsigned char *) (exif+offset); 1446 } 1447 if ((all != 0) || (tag == (size_t) tag_value)) 1448 { 1449 char 1450 buffer[MagickPathExtent], 1451 *value; 1452 1453 value=(char *) NULL; 1454 *buffer='\0'; 1455 switch (format) 1456 { 1457 case EXIF_FMT_BYTE: 1458 case EXIF_FMT_UNDEFINED: 1459 { 1460 EXIFMultipleValues(1,"%.20g",(double) (*(unsigned char *) p1)); 1461 break; 1462 } 1463 case EXIF_FMT_SBYTE: 1464 { 1465 EXIFMultipleValues(1,"%.20g",(double) (*(signed char *) p1)); 1466 break; 1467 } 1468 case EXIF_FMT_SSHORT: 1469 { 1470 EXIFMultipleValues(2,"%hd",ReadPropertySignedShort(endian,p1)); 1471 break; 1472 } 1473 case EXIF_FMT_USHORT: 1474 { 1475 EXIFMultipleValues(2,"%hu",ReadPropertyUnsignedShort(endian,p1)); 1476 break; 1477 } 1478 case EXIF_FMT_ULONG: 1479 { 1480 EXIFMultipleValues(4,"%.20g",(double) 1481 ReadPropertyUnsignedLong(endian,p1)); 1482 break; 1483 } 1484 case EXIF_FMT_SLONG: 1485 { 1486 EXIFMultipleValues(4,"%.20g",(double) 1487 ReadPropertySignedLong(endian,p1)); 1488 break; 1489 } 1490 case EXIF_FMT_URATIONAL: 1491 { 1492 EXIFMultipleFractions(8,"%.20g/%.20g",(double) 1493 ReadPropertyUnsignedLong(endian,p1),(double) 1494 ReadPropertyUnsignedLong(endian,p1+4)); 1495 break; 1496 } 1497 case EXIF_FMT_SRATIONAL: 1498 { 1499 EXIFMultipleFractions(8,"%.20g/%.20g",(double) 1500 ReadPropertySignedLong(endian,p1),(double) 1501 ReadPropertySignedLong(endian,p1+4)); 1502 break; 1503 } 1504 case EXIF_FMT_SINGLE: 1505 { 1506 EXIFMultipleValues(4,"%f",(double) *(float *) p1); 1507 break; 1508 } 1509 case EXIF_FMT_DOUBLE: 1510 { 1511 EXIFMultipleValues(8,"%f",*(double *) p1); 1512 break; 1513 } 1514 default: 1515 case EXIF_FMT_STRING: 1516 { 1517 value=(char *) NULL; 1518 if (~((size_t) number_bytes) >= 1) 1519 value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL, 1520 sizeof(*value)); 1521 if (value != (char *) NULL) 1522 { 1523 register ssize_t 1524 i; 1525 1526 for (i=0; i < (ssize_t) number_bytes; i++) 1527 { 1528 value[i]='.'; 1529 if ((isprint((int) p[i]) != 0) || (p[i] == '\0')) 1530 value[i]=(char) p[i]; 1531 } 1532 value[i]='\0'; 1533 } 1534 break; 1535 } 1536 } 1537 if (value != (char *) NULL) 1538 { 1539 char 1540 *key; 1541 1542 register const char 1543 *p; 1544 1545 key=AcquireString(property); 1546 switch (all) 1547 { 1548 case 1: 1549 { 1550 const char 1551 *description; 1552 1553 register ssize_t 1554 i; 1555 1556 description="unknown"; 1557 for (i=0; ; i++) 1558 { 1559 if (EXIFTag[i].tag == 0) 1560 break; 1561 if (EXIFTag[i].tag == tag_value) 1562 { 1563 description=EXIFTag[i].description; 1564 break; 1565 } 1566 } 1567 (void) FormatLocaleString(key,MagickPathExtent,"%s", 1568 description); 1569 if (level == 2) 1570 (void) SubstituteString(&key,"exif:","exif:thumbnail:"); 1571 break; 1572 } 1573 case 2: 1574 { 1575 if (tag_value < 0x10000) 1576 (void) FormatLocaleString(key,MagickPathExtent,"#%04lx", 1577 (unsigned long) tag_value); 1578 else 1579 if (tag_value < 0x20000) 1580 (void) FormatLocaleString(key,MagickPathExtent,"@%04lx", 1581 (unsigned long) (tag_value & 0xffff)); 1582 else 1583 (void) FormatLocaleString(key,MagickPathExtent,"unknown"); 1584 break; 1585 } 1586 default: 1587 { 1588 if (level == 2) 1589 (void) SubstituteString(&key,"exif:","exif:thumbnail:"); 1590 } 1591 } 1592 p=(const char *) NULL; 1593 if (image->properties != (void *) NULL) 1594 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *) 1595 image->properties,key); 1596 if (p == (const char *) NULL) 1597 (void) SetImageProperty((Image *) image,key,value,exception); 1598 value=DestroyString(value); 1599 key=DestroyString(key); 1600 status=MagickTrue; 1601 } 1602 } 1603 if ((tag_value == TAG_EXIF_OFFSET) || 1604 (tag_value == TAG_INTEROP_OFFSET) || (tag_value == TAG_GPS_OFFSET)) 1605 { 1606 ssize_t 1607 offset; 1608 1609 offset=(ssize_t) ReadPropertySignedLong(endian,p); 1610 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2))) 1611 { 1612 ssize_t 1613 tag_offset1; 1614 1615 tag_offset1=(ssize_t) ((tag_value == TAG_GPS_OFFSET) ? 0x10000 : 1616 0); 1617 directory_stack[level].directory=directory; 1618 entry++; 1619 directory_stack[level].entry=entry; 1620 directory_stack[level].offset=tag_offset; 1621 level++; 1622 directory_stack[level].directory=exif+offset; 1623 directory_stack[level].offset=tag_offset1; 1624 directory_stack[level].entry=0; 1625 level++; 1626 if ((directory+2+(12*number_entries)) > (exif+length)) 1627 break; 1628 offset=(ssize_t) ReadPropertySignedLong(endian,directory+2+(12* 1629 number_entries)); 1630 if ((offset != 0) && ((size_t) offset < length) && 1631 (level < (MaxDirectoryStack-2))) 1632 { 1633 directory_stack[level].directory=exif+offset; 1634 directory_stack[level].entry=0; 1635 directory_stack[level].offset=tag_offset1; 1636 level++; 1637 } 1638 } 1639 break; 1640 } 1641 } 1642 } while (level > 0); 1643 exif_resources=DestroySplayTree(exif_resources); 1644 return(status); 1645 } 1646 1647 static MagickBooleanType GetICCProperty(const Image *image,const char *property, 1648 ExceptionInfo *exception) 1649 { 1650 const StringInfo 1651 *profile; 1652 1653 magick_unreferenced(property); 1654 1655 profile=GetImageProfile(image,"icc"); 1656 if (profile == (StringInfo *) NULL) 1657 profile=GetImageProfile(image,"icm"); 1658 if (profile == (StringInfo *) NULL) 1659 return(MagickFalse); 1660 if (GetStringInfoLength(profile) < 128) 1661 return(MagickFalse); /* minimum ICC profile length */ 1662 #if defined(MAGICKCORE_LCMS_DELEGATE) 1663 { 1664 cmsHPROFILE 1665 icc_profile; 1666 1667 icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile), 1668 (cmsUInt32Number) GetStringInfoLength(profile)); 1669 if (icc_profile != (cmsHPROFILE *) NULL) 1670 { 1671 #if defined(LCMS_VERSION) && (LCMS_VERSION < 2000) 1672 const char 1673 *name; 1674 1675 name=cmsTakeProductName(icc_profile); 1676 if (name != (const char *) NULL) 1677 (void) SetImageProperty((Image *) image,"icc:name",name,exception); 1678 #else 1679 char 1680 info[MagickPathExtent]; 1681 1682 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription,"en","US", 1683 info,MagickPathExtent); 1684 (void) SetImageProperty((Image *) image,"icc:description",info, 1685 exception); 1686 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoManufacturer,"en","US", 1687 info,MagickPathExtent); 1688 (void) SetImageProperty((Image *) image,"icc:manufacturer",info, 1689 exception); 1690 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoModel,"en","US",info, 1691 MagickPathExtent); 1692 (void) SetImageProperty((Image *) image,"icc:model",info,exception); 1693 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoCopyright,"en","US", 1694 info,MagickPathExtent); 1695 (void) SetImageProperty((Image *) image,"icc:copyright",info,exception); 1696 #endif 1697 (void) cmsCloseProfile(icc_profile); 1698 } 1699 } 1700 #endif 1701 return(MagickTrue); 1702 } 1703 1704 static MagickBooleanType SkipXMPValue(const char *value) 1705 { 1706 if (value == (const char*) NULL) 1707 return(MagickTrue); 1708 while (*value != '\0') 1709 { 1710 if (isspace((int) ((unsigned char) *value)) == 0) 1711 return(MagickFalse); 1712 value++; 1713 } 1714 return(MagickTrue); 1715 } 1716 1717 static MagickBooleanType GetXMPProperty(const Image *image,const char *property) 1718 { 1719 char 1720 *xmp_profile; 1721 1722 const char 1723 *content; 1724 1725 const StringInfo 1726 *profile; 1727 1728 ExceptionInfo 1729 *exception; 1730 1731 MagickBooleanType 1732 status; 1733 1734 register const char 1735 *p; 1736 1737 XMLTreeInfo 1738 *child, 1739 *description, 1740 *node, 1741 *rdf, 1742 *xmp; 1743 1744 profile=GetImageProfile(image,"xmp"); 1745 if (profile == (StringInfo *) NULL) 1746 return(MagickFalse); 1747 if ((property == (const char *) NULL) || (*property == '\0')) 1748 return(MagickFalse); 1749 xmp_profile=StringInfoToString(profile); 1750 if (xmp_profile == (char *) NULL) 1751 return(MagickFalse); 1752 for (p=xmp_profile; *p != '\0'; p++) 1753 if ((*p == '<') && (*(p+1) == 'x')) 1754 break; 1755 exception=AcquireExceptionInfo(); 1756 xmp=NewXMLTree((char *) p,exception); 1757 xmp_profile=DestroyString(xmp_profile); 1758 exception=DestroyExceptionInfo(exception); 1759 if (xmp == (XMLTreeInfo *) NULL) 1760 return(MagickFalse); 1761 status=MagickFalse; 1762 rdf=GetXMLTreeChild(xmp,"rdf:RDF"); 1763 if (rdf != (XMLTreeInfo *) NULL) 1764 { 1765 if (image->properties == (void *) NULL) 1766 ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString, 1767 RelinquishMagickMemory,RelinquishMagickMemory); 1768 description=GetXMLTreeChild(rdf,"rdf:Description"); 1769 while (description != (XMLTreeInfo *) NULL) 1770 { 1771 node=GetXMLTreeChild(description,(const char *) NULL); 1772 while (node != (XMLTreeInfo *) NULL) 1773 { 1774 child=GetXMLTreeChild(node,(const char *) NULL); 1775 content=GetXMLTreeContent(node); 1776 if ((child == (XMLTreeInfo *) NULL) && 1777 (SkipXMPValue(content) == MagickFalse)) 1778 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties, 1779 ConstantString(GetXMLTreeTag(node)),ConstantString(content)); 1780 while (child != (XMLTreeInfo *) NULL) 1781 { 1782 content=GetXMLTreeContent(child); 1783 if (SkipXMPValue(content) == MagickFalse) 1784 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties, 1785 ConstantString(GetXMLTreeTag(child)),ConstantString(content)); 1786 child=GetXMLTreeSibling(child); 1787 } 1788 node=GetXMLTreeSibling(node); 1789 } 1790 description=GetNextXMLTreeTag(description); 1791 } 1792 } 1793 xmp=DestroyXMLTree(xmp); 1794 return(status); 1795 } 1796 1797 static char *TracePSClippath(const unsigned char *blob,size_t length) 1798 { 1799 char 1800 *path, 1801 *message; 1802 1803 MagickBooleanType 1804 in_subpath; 1805 1806 PointInfo 1807 first[3], 1808 last[3], 1809 point[3]; 1810 1811 register ssize_t 1812 i, 1813 x; 1814 1815 ssize_t 1816 knot_count, 1817 selector, 1818 y; 1819 1820 path=AcquireString((char *) NULL); 1821 if (path == (char *) NULL) 1822 return((char *) NULL); 1823 message=AcquireString((char *) NULL); 1824 (void) FormatLocaleString(message,MagickPathExtent,"/ClipImage\n"); 1825 (void) ConcatenateString(&path,message); 1826 (void) FormatLocaleString(message,MagickPathExtent,"{\n"); 1827 (void) ConcatenateString(&path,message); 1828 (void) FormatLocaleString(message,MagickPathExtent, 1829 " /c {curveto} bind def\n"); 1830 (void) ConcatenateString(&path,message); 1831 (void) FormatLocaleString(message,MagickPathExtent, 1832 " /l {lineto} bind def\n"); 1833 (void) ConcatenateString(&path,message); 1834 (void) FormatLocaleString(message,MagickPathExtent, 1835 " /m {moveto} bind def\n"); 1836 (void) ConcatenateString(&path,message); 1837 (void) FormatLocaleString(message,MagickPathExtent, 1838 " /v {currentpoint 6 2 roll curveto} bind def\n"); 1839 (void) ConcatenateString(&path,message); 1840 (void) FormatLocaleString(message,MagickPathExtent, 1841 " /y {2 copy curveto} bind def\n"); 1842 (void) ConcatenateString(&path,message); 1843 (void) FormatLocaleString(message,MagickPathExtent, 1844 " /z {closepath} bind def\n"); 1845 (void) ConcatenateString(&path,message); 1846 (void) FormatLocaleString(message,MagickPathExtent," newpath\n"); 1847 (void) ConcatenateString(&path,message); 1848 /* 1849 The clipping path format is defined in "Adobe Photoshop File Formats 1850 Specification" version 6.0 downloadable from adobe.com. 1851 */ 1852 (void) ResetMagickMemory(point,0,sizeof(point)); 1853 (void) ResetMagickMemory(first,0,sizeof(first)); 1854 (void) ResetMagickMemory(last,0,sizeof(last)); 1855 knot_count=0; 1856 in_subpath=MagickFalse; 1857 while (length > 0) 1858 { 1859 selector=(ssize_t) ReadPropertyMSBShort(&blob,&length); 1860 switch (selector) 1861 { 1862 case 0: 1863 case 3: 1864 { 1865 if (knot_count != 0) 1866 { 1867 blob+=24; 1868 length-=MagickMin(24,(ssize_t) length); 1869 break; 1870 } 1871 /* 1872 Expected subpath length record. 1873 */ 1874 knot_count=(ssize_t) ReadPropertyMSBShort(&blob,&length); 1875 blob+=22; 1876 length-=MagickMin(22,(ssize_t) length); 1877 break; 1878 } 1879 case 1: 1880 case 2: 1881 case 4: 1882 case 5: 1883 { 1884 if (knot_count == 0) 1885 { 1886 /* 1887 Unexpected subpath knot 1888 */ 1889 blob+=24; 1890 length-=MagickMin(24,(ssize_t) length); 1891 break; 1892 } 1893 /* 1894 Add sub-path knot 1895 */ 1896 for (i=0; i < 3; i++) 1897 { 1898 size_t 1899 xx, 1900 yy; 1901 1902 yy=(size_t) ReadPropertyMSBLong(&blob,&length); 1903 xx=(size_t) ReadPropertyMSBLong(&blob,&length); 1904 x=(ssize_t) xx; 1905 if (xx > 2147483647) 1906 x=(ssize_t) xx-4294967295U-1; 1907 y=(ssize_t) yy; 1908 if (yy > 2147483647) 1909 y=(ssize_t) yy-4294967295U-1; 1910 point[i].x=(double) x/4096/4096; 1911 point[i].y=1.0-(double) y/4096/4096; 1912 } 1913 if (in_subpath == MagickFalse) 1914 { 1915 (void) FormatLocaleString(message,MagickPathExtent," %g %g m\n", 1916 point[1].x,point[1].y); 1917 for (i=0; i < 3; i++) 1918 { 1919 first[i]=point[i]; 1920 last[i]=point[i]; 1921 } 1922 } 1923 else 1924 { 1925 /* 1926 Handle special cases when Bezier curves are used to describe 1927 corners and straight lines. 1928 */ 1929 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) && 1930 (point[0].x == point[1].x) && (point[0].y == point[1].y)) 1931 (void) FormatLocaleString(message,MagickPathExtent, 1932 " %g %g l\n",point[1].x,point[1].y); 1933 else 1934 if ((last[1].x == last[2].x) && (last[1].y == last[2].y)) 1935 (void) FormatLocaleString(message,MagickPathExtent, 1936 " %g %g %g %g v\n",point[0].x,point[0].y, 1937 point[1].x,point[1].y); 1938 else 1939 if ((point[0].x == point[1].x) && (point[0].y == point[1].y)) 1940 (void) FormatLocaleString(message,MagickPathExtent, 1941 " %g %g %g %g y\n",last[2].x,last[2].y, 1942 point[1].x,point[1].y); 1943 else 1944 (void) FormatLocaleString(message,MagickPathExtent, 1945 " %g %g %g %g %g %g c\n",last[2].x, 1946 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y); 1947 for (i=0; i < 3; i++) 1948 last[i]=point[i]; 1949 } 1950 (void) ConcatenateString(&path,message); 1951 in_subpath=MagickTrue; 1952 knot_count--; 1953 /* 1954 Close the subpath if there are no more knots. 1955 */ 1956 if (knot_count == 0) 1957 { 1958 /* 1959 Same special handling as above except we compare to the 1960 first point in the path and close the path. 1961 */ 1962 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) && 1963 (first[0].x == first[1].x) && (first[0].y == first[1].y)) 1964 (void) FormatLocaleString(message,MagickPathExtent, 1965 " %g %g l z\n",first[1].x,first[1].y); 1966 else 1967 if ((last[1].x == last[2].x) && (last[1].y == last[2].y)) 1968 (void) FormatLocaleString(message,MagickPathExtent, 1969 " %g %g %g %g v z\n",first[0].x,first[0].y, 1970 first[1].x,first[1].y); 1971 else 1972 if ((first[0].x == first[1].x) && (first[0].y == first[1].y)) 1973 (void) FormatLocaleString(message,MagickPathExtent, 1974 " %g %g %g %g y z\n",last[2].x,last[2].y, 1975 first[1].x,first[1].y); 1976 else 1977 (void) FormatLocaleString(message,MagickPathExtent, 1978 " %g %g %g %g %g %g c z\n",last[2].x, 1979 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y); 1980 (void) ConcatenateString(&path,message); 1981 in_subpath=MagickFalse; 1982 } 1983 break; 1984 } 1985 case 6: 1986 case 7: 1987 case 8: 1988 default: 1989 { 1990 blob+=24; 1991 length-=MagickMin(24,(ssize_t) length); 1992 break; 1993 } 1994 } 1995 } 1996 /* 1997 Returns an empty PS path if the path has no knots. 1998 */ 1999 (void) FormatLocaleString(message,MagickPathExtent," eoclip\n"); 2000 (void) ConcatenateString(&path,message); 2001 (void) FormatLocaleString(message,MagickPathExtent,"} bind def"); 2002 (void) ConcatenateString(&path,message); 2003 message=DestroyString(message); 2004 return(path); 2005 } 2006 2007 static char *TraceSVGClippath(const unsigned char *blob,size_t length, 2008 const size_t columns,const size_t rows) 2009 { 2010 char 2011 *path, 2012 *message; 2013 2014 MagickBooleanType 2015 in_subpath; 2016 2017 PointInfo 2018 first[3], 2019 last[3], 2020 point[3]; 2021 2022 register ssize_t 2023 i; 2024 2025 ssize_t 2026 knot_count, 2027 selector, 2028 x, 2029 y; 2030 2031 path=AcquireString((char *) NULL); 2032 if (path == (char *) NULL) 2033 return((char *) NULL); 2034 message=AcquireString((char *) NULL); 2035 (void) FormatLocaleString(message,MagickPathExtent,( 2036 "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" 2037 "<svg xmlns=\"http://www.w3.org/2000/svg\"" 2038 " width=\"%.20g\" height=\"%.20g\">\n" 2039 "<g>\n" 2040 "<path fill-rule=\"evenodd\" style=\"fill:#00000000;stroke:#00000000;" 2041 "stroke-width:0;stroke-antialiasing:false\" d=\"\n"),(double) columns, 2042 (double) rows); 2043 (void) ConcatenateString(&path,message); 2044 (void) ResetMagickMemory(point,0,sizeof(point)); 2045 (void) ResetMagickMemory(first,0,sizeof(first)); 2046 (void) ResetMagickMemory(last,0,sizeof(last)); 2047 knot_count=0; 2048 in_subpath=MagickFalse; 2049 while (length != 0) 2050 { 2051 selector=(ssize_t) ReadPropertyMSBShort(&blob,&length); 2052 switch (selector) 2053 { 2054 case 0: 2055 case 3: 2056 { 2057 if (knot_count != 0) 2058 { 2059 blob+=24; 2060 length-=MagickMin(24,(ssize_t) length); 2061 break; 2062 } 2063 /* 2064 Expected subpath length record. 2065 */ 2066 knot_count=(ssize_t) ReadPropertyMSBShort(&blob,&length); 2067 blob+=22; 2068 length-=MagickMin(22,(ssize_t) length); 2069 break; 2070 } 2071 case 1: 2072 case 2: 2073 case 4: 2074 case 5: 2075 { 2076 if (knot_count == 0) 2077 { 2078 /* 2079 Unexpected subpath knot. 2080 */ 2081 blob+=24; 2082 length-=MagickMin(24,(ssize_t) length); 2083 break; 2084 } 2085 /* 2086 Add sub-path knot 2087 */ 2088 for (i=0; i < 3; i++) 2089 { 2090 unsigned int 2091 xx, 2092 yy; 2093 2094 yy=(unsigned int) ReadPropertyMSBLong(&blob,&length); 2095 xx=(unsigned int) ReadPropertyMSBLong(&blob,&length); 2096 x=(ssize_t) xx; 2097 if (xx > 2147483647) 2098 x=(ssize_t) xx-4294967295U-1; 2099 y=(ssize_t) yy; 2100 if (yy > 2147483647) 2101 y=(ssize_t) yy-4294967295U-1; 2102 point[i].x=(double) x*columns/4096/4096; 2103 point[i].y=(double) y*rows/4096/4096; 2104 } 2105 if (in_subpath == MagickFalse) 2106 { 2107 (void) FormatLocaleString(message,MagickPathExtent,"M %g %g\n", 2108 point[1].x,point[1].y); 2109 for (i=0; i < 3; i++) 2110 { 2111 first[i]=point[i]; 2112 last[i]=point[i]; 2113 } 2114 } 2115 else 2116 { 2117 /* 2118 Handle special cases when Bezier curves are used to describe 2119 corners and straight lines. 2120 */ 2121 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) && 2122 (point[0].x == point[1].x) && (point[0].y == point[1].y)) 2123 (void) FormatLocaleString(message,MagickPathExtent, 2124 "L %g %g\n",point[1].x,point[1].y); 2125 else 2126 (void) FormatLocaleString(message,MagickPathExtent, 2127 "C %g %g %g %g %g %g\n",last[2].x, 2128 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y); 2129 for (i=0; i < 3; i++) 2130 last[i]=point[i]; 2131 } 2132 (void) ConcatenateString(&path,message); 2133 in_subpath=MagickTrue; 2134 knot_count--; 2135 /* 2136 Close the subpath if there are no more knots. 2137 */ 2138 if (knot_count == 0) 2139 { 2140 /* 2141 Same special handling as above except we compare to the 2142 first point in the path and close the path. 2143 */ 2144 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) && 2145 (first[0].x == first[1].x) && (first[0].y == first[1].y)) 2146 (void) FormatLocaleString(message,MagickPathExtent, 2147 "L %g %g Z\n",first[1].x,first[1].y); 2148 else 2149 (void) FormatLocaleString(message,MagickPathExtent, 2150 "C %g %g %g %g %g %g Z\n",last[2].x, 2151 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y); 2152 (void) ConcatenateString(&path,message); 2153 in_subpath=MagickFalse; 2154 } 2155 break; 2156 } 2157 case 6: 2158 case 7: 2159 case 8: 2160 default: 2161 { 2162 blob+=24; 2163 length-=MagickMin(24,(ssize_t) length); 2164 break; 2165 } 2166 } 2167 } 2168 /* 2169 Return an empty SVG image if the path does not have knots. 2170 */ 2171 (void) ConcatenateString(&path,"\"/>\n</g>\n</svg>\n"); 2172 message=DestroyString(message); 2173 return(path); 2174 } 2175 2176 MagickExport const char *GetImageProperty(const Image *image, 2177 const char *property,ExceptionInfo *exception) 2178 { 2179 register const char 2180 *p; 2181 2182 assert(image != (Image *) NULL); 2183 assert(image->signature == MagickCoreSignature); 2184 if (image->debug != MagickFalse) 2185 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2186 p=(const char *) NULL; 2187 if (image->properties != (void *) NULL) 2188 { 2189 if (property == (const char *) NULL) 2190 { 2191 ResetSplayTreeIterator((SplayTreeInfo *) image->properties); 2192 p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *) 2193 image->properties); 2194 return(p); 2195 } 2196 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *) 2197 image->properties,property); 2198 if (p != (const char *) NULL) 2199 return(p); 2200 } 2201 if ((property == (const char *) NULL) || 2202 (strchr(property,':') == (char *) NULL)) 2203 return(p); 2204 switch (*property) 2205 { 2206 case '8': 2207 { 2208 if (LocaleNCompare("8bim:",property,5) == 0) 2209 { 2210 (void) Get8BIMProperty(image,property,exception); 2211 break; 2212 } 2213 break; 2214 } 2215 case 'E': 2216 case 'e': 2217 { 2218 if (LocaleNCompare("exif:",property,5) == 0) 2219 { 2220 (void) GetEXIFProperty(image,property,exception); 2221 break; 2222 } 2223 break; 2224 } 2225 case 'I': 2226 case 'i': 2227 { 2228 if ((LocaleNCompare("icc:",property,4) == 0) || 2229 (LocaleNCompare("icm:",property,4) == 0)) 2230 { 2231 (void) GetICCProperty(image,property,exception); 2232 break; 2233 } 2234 if (LocaleNCompare("iptc:",property,5) == 0) 2235 { 2236 (void) GetIPTCProperty(image,property,exception); 2237 break; 2238 } 2239 break; 2240 } 2241 case 'X': 2242 case 'x': 2243 { 2244 if (LocaleNCompare("xmp:",property,4) == 0) 2245 { 2246 (void) GetXMPProperty(image,property); 2247 break; 2248 } 2249 break; 2250 } 2251 default: 2252 break; 2253 } 2254 if (image->properties != (void *) NULL) 2255 { 2256 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *) 2257 image->properties,property); 2258 return(p); 2259 } 2260 return((const char *) NULL); 2261 } 2262 2263 /* 2265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2266 % % 2267 % % 2268 % % 2269 + G e t M a g i c k P r o p e r t y % 2270 % % 2271 % % 2272 % % 2273 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2274 % 2275 % GetMagickProperty() gets attributes or calculated values that is associated 2276 % with a fixed known property name, or single letter property. It may be 2277 % called if no image is defined (IMv7), in which case only global image_info 2278 % values are available: 2279 % 2280 % \n newline 2281 % \r carriage return 2282 % < less-than character. 2283 % > greater-than character. 2284 % & ampersand character. 2285 % %% a percent sign 2286 % %b file size of image read in 2287 % %c comment meta-data property 2288 % %d directory component of path 2289 % %e filename extension or suffix 2290 % %f filename (including suffix) 2291 % %g layer canvas page geometry (equivalent to "%Wx%H%X%Y") 2292 % %h current image height in pixels 2293 % %i image filename (note: becomes output filename for "info:") 2294 % %k CALCULATED: number of unique colors 2295 % %l label meta-data property 2296 % %m image file format (file magic) 2297 % %n number of images in current image sequence 2298 % %o output filename (used for delegates) 2299 % %p index of image in current image list 2300 % %q quantum depth (compile-time constant) 2301 % %r image class and colorspace 2302 % %s scene number (from input unless re-assigned) 2303 % %t filename without directory or extension (suffix) 2304 % %u unique temporary filename (used for delegates) 2305 % %w current width in pixels 2306 % %x x resolution (density) 2307 % %y y resolution (density) 2308 % %z image depth (as read in unless modified, image save depth) 2309 % %A image transparency channel enabled (true/false) 2310 % %C image compression type 2311 % %D image GIF dispose method 2312 % %G original image size (%wx%h; before any resizes) 2313 % %H page (canvas) height 2314 % %M Magick filename (original file exactly as given, including read mods) 2315 % %O page (canvas) offset ( = %X%Y ) 2316 % %P page (canvas) size ( = %Wx%H ) 2317 % %Q image compression quality ( 0 = default ) 2318 % %S ?? scenes ?? 2319 % %T image time delay (in centi-seconds) 2320 % %U image resolution units 2321 % %W page (canvas) width 2322 % %X page (canvas) x offset (including sign) 2323 % %Y page (canvas) y offset (including sign) 2324 % %Z unique filename (used for delegates) 2325 % %@ CALCULATED: trim bounding box (without actually trimming) 2326 % %# CALCULATED: 'signature' hash of image values 2327 % 2328 % This routine only handles specifically known properties. It does not 2329 % handle special prefixed properties, profiles, or expressions. Nor does 2330 % it return any free-form property strings. 2331 % 2332 % The returned string is stored in a structure somewhere, and should not be 2333 % directly freed. If the string was generated (common) the string will be 2334 % stored as as either as artifact or option 'get-property'. These may be 2335 % deleted (cleaned up) when no longer required, but neither artifact or 2336 % option is guranteed to exist. 2337 % 2338 % The format of the GetMagickProperty method is: 2339 % 2340 % const char *GetMagickProperty(ImageInfo *image_info,Image *image, 2341 % const char *property,ExceptionInfo *exception) 2342 % 2343 % A description of each parameter follows: 2344 % 2345 % o image_info: the image info (optional) 2346 % 2347 % o image: the image (optional) 2348 % 2349 % o key: the key. 2350 % 2351 % o exception: return any errors or warnings in this structure. 2352 % 2353 */ 2354 static const char *GetMagickPropertyLetter(ImageInfo *image_info, 2355 Image *image,const char letter,ExceptionInfo *exception) 2356 { 2357 #define WarnNoImageReturn(format,arg) \ 2358 if (image == (Image *) NULL ) { \ 2359 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \ 2360 "NoImageForProperty",format,arg); \ 2361 return((const char *) NULL); \ 2362 } 2363 #define WarnNoImageInfoReturn(format,arg) \ 2364 if (image_info == (ImageInfo *) NULL ) { \ 2365 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \ 2366 "NoImageInfoForProperty",format,arg); \ 2367 return((const char *) NULL); \ 2368 } 2369 2370 char 2371 value[MagickPathExtent]; /* formatted string to store as an artifact */ 2372 2373 const char 2374 *string; /* return a string already stored somewher */ 2375 2376 if ((image != (Image *) NULL) && (image->debug != MagickFalse)) 2377 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2378 else 2379 if ((image_info != (ImageInfo *) NULL) && 2380 (image_info->debug != MagickFalse)) 2381 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images"); 2382 *value='\0'; /* formatted string */ 2383 string=(char *) NULL; /* constant string reference */ 2384 /* 2385 Get properities that are directly defined by images. 2386 */ 2387 switch (letter) 2388 { 2389 case 'b': /* image size read in - in bytes */ 2390 { 2391 WarnNoImageReturn("\"%%%c\"",letter); 2392 (void) FormatMagickSize(image->extent,MagickFalse,"B",MagickPathExtent, 2393 value); 2394 if (image->extent == 0) 2395 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B", 2396 MagickPathExtent,value); 2397 break; 2398 } 2399 case 'c': /* image comment property - empty string by default */ 2400 { 2401 WarnNoImageReturn("\"%%%c\"",letter); 2402 string=GetImageProperty(image,"comment",exception); 2403 if ( string == (const char *) NULL ) 2404 string=""; 2405 break; 2406 } 2407 case 'd': /* Directory component of filename */ 2408 { 2409 WarnNoImageReturn("\"%%%c\"",letter); 2410 GetPathComponent(image->magick_filename,HeadPath,value); 2411 if (*value == '\0') 2412 string=""; 2413 break; 2414 } 2415 case 'e': /* Filename extension (suffix) of image file */ 2416 { 2417 WarnNoImageReturn("\"%%%c\"",letter); 2418 GetPathComponent(image->magick_filename,ExtensionPath,value); 2419 if (*value == '\0') 2420 string=""; 2421 break; 2422 } 2423 case 'f': /* Filename without directory component */ 2424 { 2425 WarnNoImageReturn("\"%%%c\"",letter); 2426 GetPathComponent(image->magick_filename,TailPath,value); 2427 if (*value == '\0') 2428 string=""; 2429 break; 2430 } 2431 case 'g': /* Image geometry, canvas and offset %Wx%H+%X+%Y */ 2432 { 2433 WarnNoImageReturn("\"%%%c\"",letter); 2434 (void) FormatLocaleString(value,MagickPathExtent, 2435 "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double) 2436 image->page.height,(double) image->page.x,(double) image->page.y); 2437 break; 2438 } 2439 case 'h': /* Image height (current) */ 2440 { 2441 WarnNoImageReturn("\"%%%c\"",letter); 2442 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2443 (image->rows != 0 ? image->rows : image->magick_rows)); 2444 break; 2445 } 2446 case 'i': /* Filename last used for an image (read or write) */ 2447 { 2448 WarnNoImageReturn("\"%%%c\"",letter); 2449 string=image->filename; 2450 break; 2451 } 2452 case 'k': /* Number of unique colors */ 2453 { 2454 /* 2455 FUTURE: ensure this does not generate the formatted comment! 2456 */ 2457 WarnNoImageReturn("\"%%%c\"",letter); 2458 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2459 GetNumberColors(image,(FILE *) NULL,exception)); 2460 break; 2461 } 2462 case 'l': /* Image label property - empty string by default */ 2463 { 2464 WarnNoImageReturn("\"%%%c\"",letter); 2465 string=GetImageProperty(image,"label",exception); 2466 if (string == (const char *) NULL) 2467 string=""; 2468 break; 2469 } 2470 case 'm': /* Image format (file magick) */ 2471 { 2472 WarnNoImageReturn("\"%%%c\"",letter); 2473 string=image->magick; 2474 break; 2475 } 2476 case 'n': /* Number of images in the list. */ 2477 { 2478 if ( image != (Image *) NULL ) 2479 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2480 GetImageListLength(image)); 2481 else 2482 string="0"; /* no images or scenes */ 2483 break; 2484 } 2485 case 'o': /* Output Filename - for delegate use only */ 2486 WarnNoImageInfoReturn("\"%%%c\"",letter); 2487 string=image_info->filename; 2488 break; 2489 case 'p': /* Image index in current image list */ 2490 { 2491 WarnNoImageReturn("\"%%%c\"",letter); 2492 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2493 GetImageIndexInList(image)); 2494 break; 2495 } 2496 case 'q': /* Quantum depth of image in memory */ 2497 { 2498 WarnNoImageReturn("\"%%%c\"",letter); 2499 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2500 MAGICKCORE_QUANTUM_DEPTH); 2501 break; 2502 } 2503 case 'r': /* Image storage class, colorspace, and alpha enabled. */ 2504 { 2505 ColorspaceType 2506 colorspace; 2507 2508 WarnNoImageReturn("\"%%%c\"",letter); 2509 colorspace=image->colorspace; 2510 if (SetImageGray(image,exception) != MagickFalse) 2511 colorspace=GRAYColorspace; /* FUTURE: this is IMv6 not IMv7 */ 2512 (void) FormatLocaleString(value,MagickPathExtent,"%s %s %s", 2513 CommandOptionToMnemonic(MagickClassOptions,(ssize_t) 2514 image->storage_class),CommandOptionToMnemonic(MagickColorspaceOptions, 2515 (ssize_t) colorspace),image->alpha_trait != UndefinedPixelTrait ? 2516 "Alpha" : ""); 2517 break; 2518 } 2519 case 's': /* Image scene number */ 2520 { 2521 #if 0 /* this seems non-sensical -- simplifing */ 2522 if (image_info->number_scenes != 0) 2523 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2524 image_info->scene); 2525 else if (image != (Image *) NULL) 2526 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2527 image->scene); 2528 else 2529 string="0"; 2530 #else 2531 WarnNoImageReturn("\"%%%c\"",letter); 2532 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2533 image->scene); 2534 #endif 2535 break; 2536 } 2537 case 't': /* Base filename without directory or extention */ 2538 { 2539 WarnNoImageReturn("\"%%%c\"",letter); 2540 GetPathComponent(image->magick_filename,BasePath,value); 2541 if (*value == '\0') 2542 string=""; 2543 break; 2544 } 2545 case 'u': /* Unique filename */ 2546 { 2547 WarnNoImageInfoReturn("\"%%%c\"",letter); 2548 string=image_info->unique; 2549 break; 2550 } 2551 case 'w': /* Image width (current) */ 2552 { 2553 WarnNoImageReturn("\"%%%c\"",letter); 2554 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2555 (image->columns != 0 ? image->columns : image->magick_columns)); 2556 break; 2557 } 2558 case 'x': /* Image horizontal resolution (with units) */ 2559 { 2560 WarnNoImageReturn("\"%%%c\"",letter); 2561 (void) FormatLocaleString(value,MagickPathExtent,"%.20g", 2562 fabs(image->resolution.x) > MagickEpsilon ? image->resolution.x : 72.0); 2563 break; 2564 } 2565 case 'y': /* Image vertical resolution (with units) */ 2566 { 2567 WarnNoImageReturn("\"%%%c\"",letter); 2568 (void) FormatLocaleString(value,MagickPathExtent,"%.20g", 2569 fabs(image->resolution.y) > MagickEpsilon ? image->resolution.y : 72.0); 2570 break; 2571 } 2572 case 'z': /* Image depth as read in */ 2573 { 2574 WarnNoImageReturn("\"%%%c\"",letter); 2575 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2576 image->depth); 2577 break; 2578 } 2579 case 'A': /* Image alpha channel */ 2580 { 2581 WarnNoImageReturn("\"%%%c\"",letter); 2582 string=CommandOptionToMnemonic(MagickPixelTraitOptions,(ssize_t) 2583 image->alpha_trait); 2584 break; 2585 } 2586 case 'C': /* Image compression method. */ 2587 { 2588 WarnNoImageReturn("\"%%%c\"",letter); 2589 string=CommandOptionToMnemonic(MagickCompressOptions,(ssize_t) 2590 image->compression); 2591 break; 2592 } 2593 case 'D': /* Image dispose method. */ 2594 { 2595 WarnNoImageReturn("\"%%%c\"",letter); 2596 string=CommandOptionToMnemonic(MagickDisposeOptions,(ssize_t) 2597 image->dispose); 2598 break; 2599 } 2600 case 'G': /* Image size as geometry = "%wx%h" */ 2601 { 2602 WarnNoImageReturn("\"%%%c\"",letter); 2603 (void) FormatLocaleString(value,MagickPathExtent,"%.20gx%.20g",(double) 2604 image->magick_columns,(double) image->magick_rows); 2605 break; 2606 } 2607 case 'H': /* layer canvas height */ 2608 { 2609 WarnNoImageReturn("\"%%%c\"",letter); 2610 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2611 image->page.height); 2612 break; 2613 } 2614 case 'M': /* Magick filename - filename given incl. coder & read mods */ 2615 { 2616 WarnNoImageReturn("\"%%%c\"",letter); 2617 string=image->magick_filename; 2618 break; 2619 } 2620 case 'O': /* layer canvas offset with sign = "+%X+%Y" */ 2621 { 2622 WarnNoImageReturn("\"%%%c\"",letter); 2623 (void) FormatLocaleString(value,MagickPathExtent,"%+ld%+ld",(long) 2624 image->page.x,(long) image->page.y); 2625 break; 2626 } 2627 case 'P': /* layer canvas page size = "%Wx%H" */ 2628 { 2629 WarnNoImageReturn("\"%%%c\"",letter); 2630 (void) FormatLocaleString(value,MagickPathExtent,"%.20gx%.20g",(double) 2631 image->page.width,(double) image->page.height); 2632 break; 2633 } 2634 case 'Q': /* image compression quality */ 2635 { 2636 WarnNoImageReturn("\"%%%c\"",letter); 2637 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2638 (image->quality == 0 ? 92 : image->quality)); 2639 break; 2640 } 2641 case 'S': /* Number of scenes in image list. */ 2642 { 2643 WarnNoImageInfoReturn("\"%%%c\"",letter); 2644 #if 0 /* What is this number? -- it makes no sense - simplifing */ 2645 if (image_info->number_scenes == 0) 2646 string="2147483647"; 2647 else if ( image != (Image *) NULL ) 2648 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2649 image_info->scene+image_info->number_scenes); 2650 else 2651 string="0"; 2652 #else 2653 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2654 (image_info->number_scenes == 0 ? 2147483647 : 2655 image_info->number_scenes)); 2656 #endif 2657 break; 2658 } 2659 case 'T': /* image time delay for animations */ 2660 { 2661 WarnNoImageReturn("\"%%%c\"",letter); 2662 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2663 image->delay); 2664 break; 2665 } 2666 case 'U': /* Image resolution units. */ 2667 { 2668 WarnNoImageReturn("\"%%%c\"",letter); 2669 string=CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t) 2670 image->units); 2671 break; 2672 } 2673 case 'W': /* layer canvas width */ 2674 { 2675 WarnNoImageReturn("\"%%%c\"",letter); 2676 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2677 image->page.width); 2678 break; 2679 } 2680 case 'X': /* layer canvas X offset */ 2681 { 2682 WarnNoImageReturn("\"%%%c\"",letter); 2683 (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double) 2684 image->page.x); 2685 break; 2686 } 2687 case 'Y': /* layer canvas Y offset */ 2688 { 2689 WarnNoImageReturn("\"%%%c\"",letter); 2690 (void) FormatLocaleString(value,MagickPathExtent,"%+.20g",(double) 2691 image->page.y); 2692 break; 2693 } 2694 case '%': /* percent escaped */ 2695 { 2696 string="%"; 2697 break; 2698 } 2699 case '@': /* Trim bounding box, without actually Trimming! */ 2700 { 2701 RectangleInfo 2702 page; 2703 2704 WarnNoImageReturn("\"%%%c\"",letter); 2705 page=GetImageBoundingBox(image,exception); 2706 (void) FormatLocaleString(value,MagickPathExtent, 2707 "%.20gx%.20g%+.20g%+.20g",(double) page.width,(double) page.height, 2708 (double) page.x,(double)page.y); 2709 break; 2710 } 2711 case '#': 2712 { 2713 /* 2714 Image signature. 2715 */ 2716 WarnNoImageReturn("\"%%%c\"",letter); 2717 (void) SignatureImage(image,exception); 2718 string=GetImageProperty(image,"signature",exception); 2719 break; 2720 } 2721 } 2722 if (string != (char *) NULL) 2723 return(string); 2724 if (*value != '\0') 2725 { 2726 /* 2727 Create a cloned copy of result. 2728 */ 2729 if (image != (Image *) NULL) 2730 { 2731 (void) SetImageArtifact(image,"get-property",value); 2732 return(GetImageArtifact(image,"get-property")); 2733 } 2734 else 2735 { 2736 (void) SetImageOption(image_info,"get-property",value); 2737 return(GetImageOption(image_info,"get-property")); 2738 } 2739 } 2740 return((char *) NULL); 2741 } 2742 2743 MagickExport const char *GetMagickProperty(ImageInfo *image_info, 2744 Image *image,const char *property,ExceptionInfo *exception) 2745 { 2746 char 2747 value[MagickPathExtent]; 2748 2749 const char 2750 *string; 2751 2752 assert(property[0] != '\0'); 2753 assert(image != (Image *) NULL || image_info != (ImageInfo *) NULL ); 2754 if (property[1] == '\0') /* single letter property request */ 2755 return(GetMagickPropertyLetter(image_info,image,*property,exception)); 2756 if ((image != (Image *) NULL) && (image->debug != MagickFalse)) 2757 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2758 else 2759 if ((image_info != (ImageInfo *) NULL) && 2760 (image_info->debug != MagickFalse)) 2761 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images"); 2762 *value='\0'; /* formated string */ 2763 string=(char *) NULL; /* constant string reference */ 2764 switch (*property) 2765 { 2766 case 'b': 2767 { 2768 if (LocaleCompare("basename",property) == 0) 2769 { 2770 WarnNoImageReturn("\"%%[%s]\"",property); 2771 GetPathComponent(image->magick_filename,BasePath,value); 2772 if (*value == '\0') 2773 string=""; 2774 break; 2775 } 2776 if (LocaleCompare("bit-depth",property) == 0) 2777 { 2778 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2779 GetImageDepth(image,exception)); 2780 break; 2781 } 2782 break; 2783 } 2784 case 'c': 2785 { 2786 if (LocaleCompare("channels",property) == 0) 2787 { 2788 WarnNoImageReturn("\"%%[%s]\"",property); 2789 /* FUTURE: return actual image channels */ 2790 (void) FormatLocaleString(value,MagickPathExtent,"%s", 2791 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) 2792 image->colorspace)); 2793 LocaleLower(value); 2794 if( image->alpha_trait != UndefinedPixelTrait ) 2795 (void) ConcatenateMagickString(value,"a",MagickPathExtent); 2796 break; 2797 } 2798 if (LocaleCompare("colorspace",property) == 0) 2799 { 2800 WarnNoImageReturn("\"%%[%s]\"",property); 2801 /* FUTURE: return actual colorspace - no 'gray' stuff */ 2802 string=CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) 2803 image->colorspace); 2804 break; 2805 } 2806 if (LocaleCompare("compose",property) == 0) 2807 { 2808 WarnNoImageReturn("\"%%[%s]\"",property); 2809 string=CommandOptionToMnemonic(MagickComposeOptions,(ssize_t) 2810 image->compose); 2811 break; 2812 } 2813 if (LocaleCompare("copyright",property) == 0) 2814 { 2815 (void) CopyMagickString(value,GetMagickCopyright(),MagickPathExtent); 2816 break; 2817 } 2818 break; 2819 } 2820 case 'd': 2821 { 2822 if (LocaleCompare("depth",property) == 0) 2823 { 2824 WarnNoImageReturn("\"%%[%s]\"",property); 2825 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 2826 image->depth); 2827 break; 2828 } 2829 if (LocaleCompare("directory",property) == 0) 2830 { 2831 WarnNoImageReturn("\"%%[%s]\"",property); 2832 GetPathComponent(image->magick_filename,HeadPath,value); 2833 if (*value == '\0') 2834 string=""; 2835 break; 2836 } 2837 break; 2838 } 2839 case 'e': 2840 { 2841 if (LocaleCompare("entropy",property) == 0) 2842 { 2843 double 2844 entropy; 2845 2846 WarnNoImageReturn("\"%%[%s]\"",property); 2847 (void) GetImageEntropy(image,&entropy,exception); 2848 (void) FormatLocaleString(value,MagickPathExtent,"%.*g", 2849 GetMagickPrecision(),entropy); 2850 break; 2851 } 2852 if (LocaleCompare("extension",property) == 0) 2853 { 2854 WarnNoImageReturn("\"%%[%s]\"",property); 2855 GetPathComponent(image->magick_filename,ExtensionPath,value); 2856 if (*value == '\0') 2857 string=""; 2858 break; 2859 } 2860 break; 2861 } 2862 case 'g': 2863 { 2864 if (LocaleCompare("gamma",property) == 0) 2865 { 2866 WarnNoImageReturn("\"%%[%s]\"",property); 2867 (void) FormatLocaleString(value,MagickPathExtent,"%.*g", 2868 GetMagickPrecision(),image->gamma); 2869 break; 2870 } 2871 break; 2872 } 2873 case 'h': 2874 { 2875 if (LocaleCompare("height",property) == 0) 2876 { 2877 WarnNoImageReturn("\"%%[%s]\"",property); 2878 (void) FormatLocaleString(value,MagickPathExtent,"%.20g", 2879 image->magick_rows != 0 ? (double) image->magick_rows : 256.0); 2880 break; 2881 } 2882 break; 2883 } 2884 case 'i': 2885 { 2886 if (LocaleCompare("input",property) == 0) 2887 { 2888 WarnNoImageReturn("\"%%[%s]\"",property); 2889 string=image->filename; 2890 break; 2891 } 2892 break; 2893 } 2894 case 'k': 2895 { 2896 if (LocaleCompare("kurtosis",property) == 0) 2897 { 2898 double 2899 kurtosis, 2900 skewness; 2901 2902 WarnNoImageReturn("\"%%[%s]\"",property); 2903 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception); 2904 (void) FormatLocaleString(value,MagickPathExtent,"%.*g", 2905 GetMagickPrecision(),kurtosis); 2906 break; 2907 } 2908 break; 2909 } 2910 case 'm': 2911 { 2912 if (LocaleCompare("magick",property) == 0) 2913 { 2914 WarnNoImageReturn("\"%%[%s]\"",property); 2915 string=image->magick; 2916 break; 2917 } 2918 if ((LocaleCompare("maxima",property) == 0) || 2919 (LocaleCompare("max",property) == 0)) 2920 { 2921 double 2922 maximum, 2923 minimum; 2924 2925 WarnNoImageReturn("\"%%[%s]\"",property); 2926 (void) GetImageRange(image,&minimum,&maximum,exception); 2927 (void) FormatLocaleString(value,MagickPathExtent,"%.*g", 2928 GetMagickPrecision(),maximum); 2929 break; 2930 } 2931 if (LocaleCompare("mean",property) == 0) 2932 { 2933 double 2934 mean, 2935 standard_deviation; 2936 2937 WarnNoImageReturn("\"%%[%s]\"",property); 2938 (void) GetImageMean(image,&mean,&standard_deviation,exception); 2939 (void) FormatLocaleString(value,MagickPathExtent,"%.*g", 2940 GetMagickPrecision(),mean); 2941 break; 2942 } 2943 if ((LocaleCompare("minima",property) == 0) || 2944 (LocaleCompare("min",property) == 0)) 2945 { 2946 double 2947 maximum, 2948 minimum; 2949 2950 WarnNoImageReturn("\"%%[%s]\"",property); 2951 (void) GetImageRange(image,&minimum,&maximum,exception); 2952 (void) FormatLocaleString(value,MagickPathExtent,"%.*g", 2953 GetMagickPrecision(),minimum); 2954 break; 2955 } 2956 break; 2957 } 2958 case 'o': 2959 { 2960 if (LocaleCompare("opaque",property) == 0) 2961 { 2962 WarnNoImageReturn("\"%%[%s]\"",property); 2963 string=CommandOptionToMnemonic(MagickBooleanOptions,(ssize_t) 2964 IsImageOpaque(image,exception)); 2965 break; 2966 } 2967 if (LocaleCompare("orientation",property) == 0) 2968 { 2969 WarnNoImageReturn("\"%%[%s]\"",property); 2970 string=CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t) 2971 image->orientation); 2972 break; 2973 } 2974 if (LocaleCompare("output",property) == 0) 2975 { 2976 WarnNoImageInfoReturn("\"%%[%s]\"",property); 2977 (void) CopyMagickString(value,image_info->filename,MagickPathExtent); 2978 break; 2979 } 2980 break; 2981 } 2982 case 'p': 2983 { 2984 #if defined(MAGICKCORE_LCMS_DELEGATE) 2985 if (LocaleCompare("profile:icc",property) == 0 || 2986 LocaleCompare("profile:icm",property) == 0) 2987 { 2988 #if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000) 2989 #define cmsUInt32Number DWORD 2990 #endif 2991 2992 const StringInfo 2993 *profile; 2994 2995 cmsHPROFILE 2996 icc_profile; 2997 2998 profile=GetImageProfile(image,property+8); 2999 if (profile == (StringInfo *) NULL) 3000 break; 3001 icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile), 3002 (cmsUInt32Number) GetStringInfoLength(profile)); 3003 if (icc_profile != (cmsHPROFILE *) NULL) 3004 { 3005 #if defined(LCMS_VERSION) && (LCMS_VERSION < 2000) 3006 string=cmsTakeProductName(icc_profile); 3007 #else 3008 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription, 3009 "en","US",value,MagickPathExtent); 3010 #endif 3011 (void) cmsCloseProfile(icc_profile); 3012 } 3013 } 3014 #endif 3015 if (LocaleCompare("profiles",property) == 0) 3016 { 3017 const char 3018 *name; 3019 3020 ResetImageProfileIterator(image); 3021 name=GetNextImageProfile(image); 3022 if (name != (char *) NULL) 3023 { 3024 (void) CopyMagickString(value,name,MagickPathExtent); 3025 name=GetNextImageProfile(image); 3026 while (name != (char *) NULL) 3027 { 3028 ConcatenateMagickString(value,",",MagickPathExtent); 3029 ConcatenateMagickString(value,name,MagickPathExtent); 3030 name=GetNextImageProfile(image); 3031 } 3032 } 3033 break; 3034 } 3035 break; 3036 } 3037 case 'r': 3038 { 3039 if (LocaleCompare("resolution.x",property) == 0) 3040 { 3041 WarnNoImageReturn("\"%%[%s]\"",property); 3042 (void) FormatLocaleString(value,MagickPathExtent,"%g", 3043 image->resolution.x); 3044 break; 3045 } 3046 if (LocaleCompare("resolution.y",property) == 0) 3047 { 3048 WarnNoImageReturn("\"%%[%s]\"",property); 3049 (void) FormatLocaleString(value,MagickPathExtent,"%g", 3050 image->resolution.y); 3051 break; 3052 } 3053 break; 3054 } 3055 case 's': 3056 { 3057 if (LocaleCompare("scene",property) == 0) 3058 { 3059 WarnNoImageInfoReturn("\"%%[%s]\"",property); 3060 if (image_info->number_scenes != 0) 3061 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 3062 image_info->scene); 3063 else { 3064 WarnNoImageReturn("\"%%[%s]\"",property); 3065 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 3066 image->scene); 3067 } 3068 break; 3069 } 3070 if (LocaleCompare("scenes",property) == 0) 3071 { 3072 /* FUTURE: equivelent to %n? */ 3073 WarnNoImageReturn("\"%%[%s]\"",property); 3074 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 3075 GetImageListLength(image)); 3076 break; 3077 } 3078 if (LocaleCompare("size",property) == 0) 3079 { 3080 WarnNoImageReturn("\"%%[%s]\"",property); 3081 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B", 3082 MagickPathExtent,value); 3083 break; 3084 } 3085 if (LocaleCompare("skewness",property) == 0) 3086 { 3087 double 3088 kurtosis, 3089 skewness; 3090 3091 WarnNoImageReturn("\"%%[%s]\"",property); 3092 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception); 3093 (void) FormatLocaleString(value,MagickPathExtent,"%.*g", 3094 GetMagickPrecision(),skewness); 3095 break; 3096 } 3097 if (LocaleCompare("standard-deviation",property) == 0) 3098 { 3099 double 3100 mean, 3101 standard_deviation; 3102 3103 WarnNoImageReturn("\"%%[%s]\"",property); 3104 (void) GetImageMean(image,&mean,&standard_deviation,exception); 3105 (void) FormatLocaleString(value,MagickPathExtent,"%.*g", 3106 GetMagickPrecision(),standard_deviation); 3107 break; 3108 } 3109 break; 3110 } 3111 case 't': 3112 { 3113 if (LocaleCompare("type",property) == 0) 3114 { 3115 WarnNoImageReturn("\"%%[%s]\"",property); 3116 string=CommandOptionToMnemonic(MagickTypeOptions,(ssize_t) 3117 IdentifyImageType(image,exception)); 3118 break; 3119 } 3120 break; 3121 } 3122 case 'u': 3123 { 3124 if (LocaleCompare("unique",property) == 0) 3125 { 3126 WarnNoImageInfoReturn("\"%%[%s]\"",property); 3127 string=image_info->unique; 3128 break; 3129 } 3130 if (LocaleCompare("units",property) == 0) 3131 { 3132 WarnNoImageReturn("\"%%[%s]\"",property); 3133 string=CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t) 3134 image->units); 3135 break; 3136 } 3137 if (LocaleCompare("copyright",property) == 0) 3138 break; 3139 } 3140 case 'v': 3141 { 3142 if (LocaleCompare("version",property) == 0) 3143 { 3144 string=GetMagickVersion((size_t *) NULL); 3145 break; 3146 } 3147 break; 3148 } 3149 case 'w': 3150 { 3151 if (LocaleCompare("width",property) == 0) 3152 { 3153 WarnNoImageReturn("\"%%[%s]\"",property); 3154 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double) 3155 (image->magick_columns != 0 ? image->magick_columns : 256)); 3156 break; 3157 } 3158 break; 3159 } 3160 } 3161 if (string != (char *) NULL) 3162 return(string); 3163 if (*value != '\0') 3164 { 3165 /* 3166 Create a cloned copy of result, that will get cleaned up, eventually. 3167 */ 3168 if (image != (Image *) NULL) 3169 { 3170 (void) SetImageArtifact(image,"get-property",value); 3171 return(GetImageArtifact(image,"get-property")); 3172 } 3173 else 3174 { 3175 (void) SetImageOption(image_info,"get-property",value); 3176 return(GetImageOption(image_info,"get-property")); 3177 } 3178 } 3179 return((char *) NULL); 3180 } 3181 #undef WarnNoImageReturn 3182 3183 /* 3185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3186 % % 3187 % % 3188 % % 3189 % G e t N e x t I m a g e P r o p e r t y % 3190 % % 3191 % % 3192 % % 3193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3194 % 3195 % GetNextImageProperty() gets the next free-form string property name. 3196 % 3197 % The format of the GetNextImageProperty method is: 3198 % 3199 % char *GetNextImageProperty(const Image *image) 3200 % 3201 % A description of each parameter follows: 3202 % 3203 % o image: the image. 3204 % 3205 */ 3206 MagickExport const char *GetNextImageProperty(const Image *image) 3207 { 3208 assert(image != (Image *) NULL); 3209 assert(image->signature == MagickCoreSignature); 3210 if (image->debug != MagickFalse) 3211 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 3212 image->filename); 3213 if (image->properties == (void *) NULL) 3214 return((const char *) NULL); 3215 return((const char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties)); 3216 } 3217 3218 /* 3220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3221 % % 3222 % % 3223 % % 3224 % I n t e r p r e t I m a g e P r o p e r t i e s % 3225 % % 3226 % % 3227 % % 3228 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3229 % 3230 % InterpretImageProperties() replaces any embedded formatting characters with 3231 % the appropriate image property and returns the interpreted text. 3232 % 3233 % This searches for and replaces 3234 % \n \r \% replaced by newline, return, and percent resp. 3235 % < > & replaced by '<', '>', '&' resp. 3236 % %% replaced by percent 3237 % 3238 % %x %[x] where 'x' is a single letter properity, case sensitive). 3239 % %[type:name] where 'type' a is special and known prefix. 3240 % %[name] where 'name' is a specifically known attribute, calculated 3241 % value, or a per-image property string name, or a per-image 3242 % 'artifact' (as generated from a global option). 3243 % It may contain ':' as long as the prefix is not special. 3244 % 3245 % Single letter % substitutions will only happen if the character before the 3246 % percent is NOT a number. But braced substitutions will always be performed. 3247 % This prevents the typical usage of percent in a interpreted geometry 3248 % argument from being substituted when the percent is a geometry flag. 3249 % 3250 % If 'glob-expresions' ('*' or '?' characters) is used for 'name' it may be 3251 % used as a search pattern to print multiple lines of "name=value\n" pairs of 3252 % the associacted set of properties. 3253 % 3254 % The returned string must be freed using DestoryString() by the caller. 3255 % 3256 % The format of the InterpretImageProperties method is: 3257 % 3258 % char *InterpretImageProperties(ImageInfo *image_info, 3259 % Image *image,const char *embed_text,ExceptionInfo *exception) 3260 % 3261 % A description of each parameter follows: 3262 % 3263 % o image_info: the image info. (required) 3264 % 3265 % o image: the image. (optional) 3266 % 3267 % o embed_text: the address of a character string containing the embedded 3268 % formatting characters. 3269 % 3270 % o exception: return any errors or warnings in this structure. 3271 % 3272 */ 3273 MagickExport char *InterpretImageProperties(ImageInfo *image_info,Image *image, 3274 const char *embed_text,ExceptionInfo *exception) 3275 { 3276 #define ExtendInterpretText(string_length) \ 3277 DisableMSCWarning(4127) \ 3278 { \ 3279 size_t length=(string_length); \ 3280 if ((size_t) (q-interpret_text+length+1) >= extent) \ 3281 { \ 3282 extent+=length; \ 3283 interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \ 3284 MaxTextExtent,sizeof(*interpret_text)); \ 3285 if (interpret_text == (char *) NULL) \ 3286 return((char *) NULL); \ 3287 q=interpret_text+strlen(interpret_text); \ 3288 } \ 3289 } \ 3290 RestoreMSCWarning 3291 3292 #define AppendKeyValue2Text(key,value)\ 3293 DisableMSCWarning(4127) \ 3294 { \ 3295 size_t length=strlen(key)+strlen(value)+2; \ 3296 if ((size_t) (q-interpret_text+length+1) >= extent) \ 3297 { \ 3298 extent+=length; \ 3299 interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \ 3300 MaxTextExtent,sizeof(*interpret_text)); \ 3301 if (interpret_text == (char *) NULL) \ 3302 return((char *) NULL); \ 3303 q=interpret_text+strlen(interpret_text); \ 3304 } \ 3305 q+=FormatLocaleString(q,extent,"%s=%s\n",(key),(value)); \ 3306 } \ 3307 RestoreMSCWarning 3308 3309 #define AppendString2Text(string) \ 3310 DisableMSCWarning(4127) \ 3311 { \ 3312 size_t length=strlen((string)); \ 3313 if ((size_t) (q-interpret_text+length+1) >= extent) \ 3314 { \ 3315 extent+=length; \ 3316 interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+ \ 3317 MaxTextExtent,sizeof(*interpret_text)); \ 3318 if (interpret_text == (char *) NULL) \ 3319 return((char *) NULL); \ 3320 q=interpret_text+strlen(interpret_text); \ 3321 } \ 3322 (void) CopyMagickString(q,(string),extent); \ 3323 q+=length; \ 3324 } \ 3325 RestoreMSCWarning 3326 3327 char 3328 *interpret_text; 3329 3330 MagickBooleanType 3331 number; 3332 3333 register char 3334 *q; /* current position in interpret_text */ 3335 3336 register const char 3337 *p; /* position in embed_text string being expanded */ 3338 3339 size_t 3340 extent; /* allocated length of interpret_text */ 3341 3342 assert(image == NULL || image->signature == MagickCoreSignature); 3343 assert(image_info == NULL || image_info->signature == MagickCoreSignature); 3344 if ((image != (Image *) NULL) && (image->debug != MagickFalse)) 3345 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 3346 else 3347 if ((image_info != (ImageInfo *) NULL) && (image_info->debug != MagickFalse)) 3348 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-image"); 3349 if (embed_text == (const char *) NULL) 3350 return(ConstantString("")); 3351 p=embed_text; 3352 while ((isspace((int) ((unsigned char) *p)) != 0) && (*p != '\0')) 3353 p++; 3354 if (*p == '\0') 3355 return(ConstantString("")); 3356 if ((*p == '@') && (IsPathAccessible(p+1) != MagickFalse)) 3357 { 3358 /* 3359 Handle a '@' replace string from file. 3360 */ 3361 if (IsRightsAuthorized(PathPolicyDomain,ReadPolicyRights,p) == MagickFalse) 3362 { 3363 errno=EPERM; 3364 (void) ThrowMagickException(exception,GetMagickModule(),PolicyError, 3365 "NotAuthorized","`%s'",p); 3366 return(ConstantString("")); 3367 } 3368 interpret_text=FileToString(p+1,~0UL,exception); 3369 if (interpret_text != (char *) NULL) 3370 return(interpret_text); 3371 } 3372 /* 3373 Translate any embedded format characters. 3374 */ 3375 interpret_text=AcquireString(embed_text); /* new string with extra space */ 3376 extent=MagickPathExtent; /* allocated space in string */ 3377 number=MagickFalse; /* is last char a number? */ 3378 for (q=interpret_text; *p!='\0'; number=isdigit(*p) ? MagickTrue : MagickFalse,p++) 3379 { 3380 /* 3381 Look for the various escapes, (and handle other specials) 3382 */ 3383 *q='\0'; 3384 ExtendInterpretText(MagickPathExtent); 3385 switch (*p) 3386 { 3387 case '\\': 3388 { 3389 switch (*(p+1)) 3390 { 3391 case '\0': 3392 continue; 3393 case 'r': /* convert to RETURN */ 3394 { 3395 *q++='\r'; 3396 p++; 3397 continue; 3398 } 3399 case 'n': /* convert to NEWLINE */ 3400 { 3401 *q++='\n'; 3402 p++; 3403 continue; 3404 } 3405 case '\n': /* EOL removal UNIX,MacOSX */ 3406 { 3407 p++; 3408 continue; 3409 } 3410 case '\r': /* EOL removal DOS,Windows */ 3411 { 3412 p++; 3413 if (*p == '\n') /* return-newline EOL */ 3414 p++; 3415 continue; 3416 } 3417 default: 3418 { 3419 p++; 3420 *q++=(*p); 3421 } 3422 } 3423 continue; 3424 } 3425 case '&': 3426 { 3427 if (LocaleNCompare("<",p,4) == 0) 3428 { 3429 *q++='<'; 3430 p+=3; 3431 } 3432 else 3433 if (LocaleNCompare(">",p,4) == 0) 3434 { 3435 *q++='>'; 3436 p+=3; 3437 } 3438 else 3439 if (LocaleNCompare("&",p,5) == 0) 3440 { 3441 *q++='&'; 3442 p+=4; 3443 } 3444 else 3445 *q++=(*p); 3446 continue; 3447 } 3448 case '%': 3449 break; /* continue to next set of handlers */ 3450 default: 3451 { 3452 *q++=(*p); /* any thing else is 'as normal' */ 3453 continue; 3454 } 3455 } 3456 p++; /* advance beyond the percent */ 3457 /* 3458 Doubled Percent - or percent at end of string. 3459 */ 3460 if ((*p == '\0') || (*p == '\'') || (*p == '"')) 3461 p--; 3462 if (*p == '%') 3463 { 3464 *q++='%'; 3465 continue; 3466 } 3467 /* 3468 Single letter escapes %c. 3469 */ 3470 if (*p != '[') 3471 { 3472 const char 3473 *string; 3474 3475 if (number != MagickFalse) 3476 { 3477 /* 3478 But only if not preceeded by a number! 3479 */ 3480 *q++='%'; /* do NOT substitute the percent */ 3481 p--; /* back up one */ 3482 continue; 3483 } 3484 string=GetMagickPropertyLetter(image_info,image,*p, exception); 3485 if (string != (char *) NULL) 3486 { 3487 AppendString2Text(string); 3488 if (image != (Image *) NULL) 3489 (void) DeleteImageArtifact(image,"get-property"); 3490 if (image_info != (ImageInfo *) NULL) 3491 (void) DeleteImageOption(image_info,"get-property"); 3492 continue; 3493 } 3494 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, 3495 "UnknownImageProperty","\"%%%c\"",*p); 3496 continue; 3497 } 3498 { 3499 char 3500 pattern[2*MagickPathExtent]; 3501 3502 const char 3503 *key, 3504 *string; 3505 3506 register ssize_t 3507 len; 3508 3509 ssize_t 3510 depth; 3511 3512 /* 3513 Braced Percent Escape %[...]. 3514 */ 3515 p++; /* advance p to just inside the opening brace */ 3516 depth=1; 3517 if (*p == ']') 3518 { 3519 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, 3520 "UnknownImageProperty","\"%%[]\""); 3521 break; 3522 } 3523 for (len=0; len<(MagickPathExtent-1L) && (*p != '\0');) 3524 { 3525 if ((*p == '\\') && (*(p+1) != '\0')) 3526 { 3527 /* 3528 Skip escaped braces within braced pattern. 3529 */ 3530 pattern[len++]=(*p++); 3531 pattern[len++]=(*p++); 3532 continue; 3533 } 3534 if (*p == '[') 3535 depth++; 3536 if (*p == ']') 3537 depth--; 3538 if (depth <= 0) 3539 break; 3540 pattern[len++]=(*p++); 3541 } 3542 pattern[len]='\0'; 3543 if (depth != 0) 3544 { 3545 /* 3546 Check for unmatched final ']' for "%[...]". 3547 */ 3548 if (len >= 64) 3549 { 3550 pattern[61] = '.'; /* truncate string for error message */ 3551 pattern[62] = '.'; 3552 pattern[63] = '.'; 3553 pattern[64] = '\0'; 3554 } 3555 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 3556 "UnbalancedBraces","\"%%[%s\"",pattern); 3557 interpret_text=DestroyString(interpret_text); 3558 return((char *) NULL); 3559 } 3560 /* 3561 Special Lookup Prefixes %[prefix:...]. 3562 */ 3563 if (LocaleNCompare("fx:",pattern,3) == 0) 3564 { 3565 double 3566 value; 3567 3568 FxInfo 3569 *fx_info; 3570 3571 MagickBooleanType 3572 status; 3573 3574 /* 3575 FX - value calculator. 3576 */ 3577 if (image == (Image *) NULL ) 3578 { 3579 (void) ThrowMagickException(exception,GetMagickModule(), 3580 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern); 3581 continue; /* else no image to retrieve artifact */ 3582 } 3583 fx_info=AcquireFxInfo(image,pattern+3,exception); 3584 status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0, 3585 &value,exception); 3586 fx_info=DestroyFxInfo(fx_info); 3587 if (status != MagickFalse) 3588 { 3589 char 3590 result[MagickPathExtent]; 3591 3592 (void) FormatLocaleString(result,MagickPathExtent,"%.*g", 3593 GetMagickPrecision(),(double) value); 3594 AppendString2Text(result); 3595 } 3596 continue; 3597 } 3598 if (LocaleNCompare("pixel:",pattern,6) == 0) 3599 { 3600 FxInfo 3601 *fx_info; 3602 3603 double 3604 value; 3605 3606 MagickStatusType 3607 status; 3608 3609 PixelInfo 3610 pixel; 3611 3612 /* 3613 Pixel - color value calculator. 3614 */ 3615 if (image == (Image *) NULL) 3616 { 3617 (void) ThrowMagickException(exception,GetMagickModule(), 3618 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern); 3619 continue; /* else no image to retrieve artifact */ 3620 } 3621 GetPixelInfo(image,&pixel); 3622 fx_info=AcquireFxInfo(image,pattern+6,exception); 3623 status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0, 3624 &value,exception); 3625 pixel.red=(double) QuantumRange*value; 3626 status&=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0, 3627 &value,exception); 3628 pixel.green=(double) QuantumRange*value; 3629 status&=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0, 3630 &value,exception); 3631 pixel.blue=(double) QuantumRange*value; 3632 if (image->colorspace == CMYKColorspace) 3633 { 3634 status&=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0, 3635 &value,exception); 3636 pixel.black=(double) QuantumRange*value; 3637 } 3638 status&=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0, 3639 &value,exception); 3640 pixel.alpha=(double) QuantumRange*value; 3641 fx_info=DestroyFxInfo(fx_info); 3642 if (status != MagickFalse) 3643 { 3644 char 3645 name[MagickPathExtent]; 3646 3647 (void) QueryColorname(image,&pixel,SVGCompliance,name, 3648 exception); 3649 AppendString2Text(name); 3650 } 3651 continue; 3652 } 3653 if (LocaleNCompare("option:",pattern,7) == 0) 3654 { 3655 /* 3656 Option - direct global option lookup (with globbing). 3657 */ 3658 if (image_info == (ImageInfo *) NULL ) 3659 { 3660 (void) ThrowMagickException(exception,GetMagickModule(), 3661 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern); 3662 continue; /* else no image to retrieve artifact */ 3663 } 3664 if (IsGlob(pattern+7) != MagickFalse) 3665 { 3666 ResetImageOptionIterator(image_info); 3667 while ((key=GetNextImageOption(image_info)) != (const char *) NULL) 3668 if (GlobExpression(key,pattern+7,MagickTrue) != MagickFalse) 3669 { 3670 string=GetImageOption(image_info,key); 3671 if (string != (const char *) NULL) 3672 AppendKeyValue2Text(key,string); 3673 /* else - assertion failure? key found but no string value! */ 3674 } 3675 continue; 3676 } 3677 string=GetImageOption(image_info,pattern+7); 3678 if (string == (char *) NULL) 3679 goto PropertyLookupFailure; /* no artifact of this specifc name */ 3680 AppendString2Text(string); 3681 continue; 3682 } 3683 if (LocaleNCompare("artifact:",pattern,9) == 0) 3684 { 3685 /* 3686 Artifact - direct image artifact lookup (with glob). 3687 */ 3688 if (image == (Image *) NULL) 3689 { 3690 (void) ThrowMagickException(exception,GetMagickModule(), 3691 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern); 3692 continue; /* else no image to retrieve artifact */ 3693 } 3694 if (IsGlob(pattern+9) != MagickFalse) 3695 { 3696 ResetImageArtifactIterator(image); 3697 while ((key=GetNextImageArtifact(image)) != (const char *) NULL) 3698 if (GlobExpression(key,pattern+9,MagickTrue) != MagickFalse) 3699 { 3700 string=GetImageArtifact(image,key); 3701 if (string != (const char *) NULL) 3702 AppendKeyValue2Text(key,string); 3703 /* else - assertion failure? key found but no string value! */ 3704 } 3705 continue; 3706 } 3707 string=GetImageArtifact(image,pattern+9); 3708 if (string == (char *) NULL) 3709 goto PropertyLookupFailure; /* no artifact of this specifc name */ 3710 AppendString2Text(string); 3711 continue; 3712 } 3713 if (LocaleNCompare("property:",pattern,9) == 0) 3714 { 3715 /* 3716 Property - direct image property lookup (with glob). 3717 */ 3718 if (image == (Image *) NULL) 3719 { 3720 (void) ThrowMagickException(exception,GetMagickModule(), 3721 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern); 3722 continue; /* else no image to retrieve artifact */ 3723 } 3724 if (IsGlob(pattern+9) != MagickFalse) 3725 { 3726 ResetImagePropertyIterator(image); 3727 while ((key=GetNextImageProperty(image)) != (const char *) NULL) 3728 if (GlobExpression(key,pattern,MagickTrue) != MagickFalse) 3729 { 3730 string=GetImageProperty(image,key,exception); 3731 if (string != (const char *) NULL) 3732 AppendKeyValue2Text(key,string); 3733 /* else - assertion failure? */ 3734 } 3735 continue; 3736 } 3737 string=GetImageProperty(image,pattern+9,exception); 3738 if (string == (char *) NULL) 3739 goto PropertyLookupFailure; /* no artifact of this specifc name */ 3740 AppendString2Text(string); 3741 continue; 3742 } 3743 if (image != (Image *) NULL) 3744 { 3745 /* 3746 Properties without special prefix. This handles attributes, 3747 properties, and profiles such as %[exif:...]. Note the profile 3748 properties may also include a glob expansion pattern. 3749 */ 3750 string=GetImageProperty(image,pattern,exception); 3751 if (string != (const char *) NULL) 3752 { 3753 AppendString2Text(string); 3754 if (image != (Image *) NULL) 3755 (void)DeleteImageArtifact(image,"get-property"); 3756 if (image_info != (ImageInfo *) NULL) 3757 (void)DeleteImageOption(image_info,"get-property"); 3758 continue; 3759 } 3760 } 3761 if (IsGlob(pattern) != MagickFalse) 3762 { 3763 /* 3764 Handle property 'glob' patterns such as: 3765 %[*] %[user:array_??] %[filename:e*]> 3766 */ 3767 if (image == (Image *) NULL) 3768 continue; /* else no image to retrieve proprty - no list */ 3769 ResetImagePropertyIterator(image); 3770 while ((key=GetNextImageProperty(image)) != (const char *) NULL) 3771 if (GlobExpression(key,pattern,MagickTrue) != MagickFalse) 3772 { 3773 string=GetImageProperty(image,key,exception); 3774 if (string != (const char *) NULL) 3775 AppendKeyValue2Text(key,string); 3776 /* else - assertion failure? */ 3777 } 3778 continue; 3779 } 3780 /* 3781 Look for a known property or image attribute such as 3782 %[basename] %[denisty] %[delay]. Also handles a braced single 3783 letter: %[b] %[G] %[g]. 3784 */ 3785 string=GetMagickProperty(image_info,image,pattern,exception); 3786 if (string != (const char *) NULL) 3787 { 3788 AppendString2Text(string); 3789 continue; 3790 } 3791 /* 3792 Look for a per-image artifact. This includes option lookup 3793 (FUTURE: interpreted according to image). 3794 */ 3795 if (image != (Image *) NULL) 3796 { 3797 string=GetImageArtifact(image,pattern); 3798 if (string != (char *) NULL) 3799 { 3800 AppendString2Text(string); 3801 continue; 3802 } 3803 } 3804 else 3805 if (image_info != (ImageInfo *) NULL) 3806 { 3807 /* 3808 No image, so direct 'option' lookup (no delayed percent escapes). 3809 */ 3810 string=GetImageOption(image_info,pattern); 3811 if (string != (char *) NULL) 3812 { 3813 AppendString2Text(string); 3814 continue; 3815 } 3816 } 3817 PropertyLookupFailure: 3818 /* 3819 Failed to find any match anywhere! 3820 */ 3821 if (len >= 64) 3822 { 3823 pattern[61] = '.'; /* truncate string for error message */ 3824 pattern[62] = '.'; 3825 pattern[63] = '.'; 3826 pattern[64] = '\0'; 3827 } 3828 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, 3829 "UnknownImageProperty","\"%%[%s]\"",pattern); 3830 } 3831 } 3832 *q='\0'; 3833 return(interpret_text); 3834 } 3835 3836 /* 3838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3839 % % 3840 % % 3841 % % 3842 % R e m o v e I m a g e P r o p e r t y % 3843 % % 3844 % % 3845 % % 3846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3847 % 3848 % RemoveImageProperty() removes a property from the image and returns its 3849 % value. 3850 % 3851 % In this case the ConstantString() value returned should be freed by the 3852 % caller when finished. 3853 % 3854 % The format of the RemoveImageProperty method is: 3855 % 3856 % char *RemoveImageProperty(Image *image,const char *property) 3857 % 3858 % A description of each parameter follows: 3859 % 3860 % o image: the image. 3861 % 3862 % o property: the image property. 3863 % 3864 */ 3865 MagickExport char *RemoveImageProperty(Image *image,const char *property) 3866 { 3867 char 3868 *value; 3869 3870 assert(image != (Image *) NULL); 3871 assert(image->signature == MagickCoreSignature); 3872 if (image->debug != MagickFalse) 3873 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 3874 if (image->properties == (void *) NULL) 3875 return((char *) NULL); 3876 value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties, 3877 property); 3878 return(value); 3879 } 3880 3881 /* 3883 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3884 % % 3885 % % 3886 % % 3887 % 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 % 3888 % % 3889 % % 3890 % % 3891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3892 % 3893 % ResetImagePropertyIterator() resets the image properties iterator. Use it 3894 % in conjunction with GetNextImageProperty() to iterate over all the values 3895 % associated with an image property. 3896 % 3897 % The format of the ResetImagePropertyIterator method is: 3898 % 3899 % ResetImagePropertyIterator(Image *image) 3900 % 3901 % A description of each parameter follows: 3902 % 3903 % o image: the image. 3904 % 3905 */ 3906 MagickExport void ResetImagePropertyIterator(const Image *image) 3907 { 3908 assert(image != (Image *) NULL); 3909 assert(image->signature == MagickCoreSignature); 3910 if (image->debug != MagickFalse) 3911 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 3912 if (image->properties == (void *) NULL) 3913 return; 3914 ResetSplayTreeIterator((SplayTreeInfo *) image->properties); 3915 } 3916 3917 /* 3919 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3920 % % 3921 % % 3922 % % 3923 % S e t I m a g e P r o p e r t y % 3924 % % 3925 % % 3926 % % 3927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3928 % 3929 % SetImageProperty() saves the given string value either to specific known 3930 % attribute or to a freeform property string. 3931 % 3932 % Attempting to set a property that is normally calculated will produce 3933 % an exception. 3934 % 3935 % The format of the SetImageProperty method is: 3936 % 3937 % MagickBooleanType SetImageProperty(Image *image,const char *property, 3938 % const char *value,ExceptionInfo *exception) 3939 % 3940 % A description of each parameter follows: 3941 % 3942 % o image: the image. 3943 % 3944 % o property: the image property. 3945 % 3946 % o values: the image property values. 3947 % 3948 % o exception: return any errors or warnings in this structure. 3949 % 3950 */ 3951 MagickExport MagickBooleanType SetImageProperty(Image *image, 3952 const char *property,const char *value,ExceptionInfo *exception) 3953 { 3954 MagickBooleanType 3955 status; 3956 3957 MagickStatusType 3958 flags; 3959 3960 assert(image != (Image *) NULL); 3961 assert(image->signature == MagickCoreSignature); 3962 if (image->debug != MagickFalse) 3963 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 3964 if (image->properties == (void *) NULL) 3965 image->properties=NewSplayTree(CompareSplayTreeString, 3966 RelinquishMagickMemory,RelinquishMagickMemory); /* create splay-tree */ 3967 if (value == (const char *) NULL) 3968 return(DeleteImageProperty(image,property)); /* delete if NULL */ 3969 status=MagickTrue; 3970 if (strlen(property) <= 1) 3971 { 3972 /* 3973 Do not 'set' single letter properties - read only shorthand. 3974 */ 3975 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 3976 "SetReadOnlyProperty","`%s'",property); 3977 return(MagickFalse); 3978 } 3979 3980 /* FUTURE: binary chars or quotes in key should produce a error */ 3981 /* Set attributes with known names or special prefixes 3982 return result is found, or break to set a free form properity 3983 */ 3984 switch (*property) 3985 { 3986 #if 0 /* Percent escape's sets values with this prefix: for later use 3987 Throwing an exception causes this setting to fail */ 3988 case '8': 3989 { 3990 if (LocaleNCompare("8bim:",property,5) == 0) 3991 { 3992 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 3993 "SetReadOnlyProperty","`%s'",property); 3994 return(MagickFalse); 3995 } 3996 break; 3997 } 3998 #endif 3999 case 'B': 4000 case 'b': 4001 { 4002 if (LocaleCompare("background",property) == 0) 4003 { 4004 (void) QueryColorCompliance(value,AllCompliance, 4005 &image->background_color,exception); 4006 /* check for FUTURE: value exception?? */ 4007 /* also add user input to splay tree */ 4008 } 4009 break; /* not an attribute, add as a property */ 4010 } 4011 case 'C': 4012 case 'c': 4013 { 4014 if (LocaleCompare("channels",property) == 0) 4015 { 4016 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4017 "SetReadOnlyProperty","`%s'",property); 4018 return(MagickFalse); 4019 } 4020 if (LocaleCompare("colorspace",property) == 0) 4021 { 4022 ssize_t 4023 colorspace; 4024 4025 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse, 4026 value); 4027 if (colorspace < 0) 4028 return(MagickFalse); /* FUTURE: value exception?? */ 4029 return(SetImageColorspace(image,(ColorspaceType) colorspace,exception)); 4030 } 4031 if (LocaleCompare("compose",property) == 0) 4032 { 4033 ssize_t 4034 compose; 4035 4036 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value); 4037 if (compose < 0) 4038 return(MagickFalse); /* FUTURE: value exception?? */ 4039 image->compose=(CompositeOperator) compose; 4040 return(MagickTrue); 4041 } 4042 if (LocaleCompare("compress",property) == 0) 4043 { 4044 ssize_t 4045 compression; 4046 4047 compression=ParseCommandOption(MagickCompressOptions,MagickFalse, 4048 value); 4049 if (compression < 0) 4050 return(MagickFalse); /* FUTURE: value exception?? */ 4051 image->compression=(CompressionType) compression; 4052 return(MagickTrue); 4053 } 4054 break; /* not an attribute, add as a property */ 4055 } 4056 case 'D': 4057 case 'd': 4058 { 4059 if (LocaleCompare("delay",property) == 0) 4060 { 4061 GeometryInfo 4062 geometry_info; 4063 4064 flags=ParseGeometry(value,&geometry_info); 4065 if ((flags & GreaterValue) != 0) 4066 { 4067 if (image->delay > (size_t) floor(geometry_info.rho+0.5)) 4068 image->delay=(size_t) floor(geometry_info.rho+0.5); 4069 } 4070 else 4071 if ((flags & LessValue) != 0) 4072 { 4073 if (image->delay < (size_t) floor(geometry_info.rho+0.5)) 4074 image->delay=(ssize_t) 4075 floor(geometry_info.sigma+0.5); 4076 } 4077 else 4078 image->delay=(size_t) floor(geometry_info.rho+0.5); 4079 if ((flags & SigmaValue) != 0) 4080 image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5); 4081 return(MagickTrue); 4082 } 4083 if (LocaleCompare("delay_units",property) == 0) 4084 { 4085 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4086 "SetReadOnlyProperty","`%s'",property); 4087 return(MagickFalse); 4088 } 4089 if (LocaleCompare("density",property) == 0) 4090 { 4091 GeometryInfo 4092 geometry_info; 4093 4094 flags=ParseGeometry(value,&geometry_info); 4095 image->resolution.x=geometry_info.rho; 4096 image->resolution.y=geometry_info.sigma; 4097 if ((flags & SigmaValue) == 0) 4098 image->resolution.y=image->resolution.x; 4099 return(MagickTrue); 4100 } 4101 if (LocaleCompare("depth",property) == 0) 4102 { 4103 image->depth=StringToUnsignedLong(value); 4104 return(MagickTrue); 4105 } 4106 if (LocaleCompare("dispose",property) == 0) 4107 { 4108 ssize_t 4109 dispose; 4110 4111 dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value); 4112 if (dispose < 0) 4113 return(MagickFalse); /* FUTURE: value exception?? */ 4114 image->dispose=(DisposeType) dispose; 4115 return(MagickTrue); 4116 } 4117 break; /* not an attribute, add as a property */ 4118 } 4119 #if 0 /* Percent escape's sets values with this prefix: for later use 4120 Throwing an exception causes this setting to fail */ 4121 case 'E': 4122 case 'e': 4123 { 4124 if (LocaleNCompare("exif:",property,5) == 0) 4125 { 4126 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4127 "SetReadOnlyProperty","`%s'",property); 4128 return(MagickFalse); 4129 } 4130 break; /* not an attribute, add as a property */ 4131 } 4132 case 'F': 4133 case 'f': 4134 { 4135 if (LocaleNCompare("fx:",property,3) == 0) 4136 { 4137 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4138 "SetReadOnlyProperty","`%s'",property); 4139 return(MagickFalse); 4140 } 4141 break; /* not an attribute, add as a property */ 4142 } 4143 #endif 4144 case 'G': 4145 case 'g': 4146 { 4147 if (LocaleCompare("gamma",property) == 0) 4148 { 4149 image->gamma=StringToDouble(value,(char **) NULL); 4150 return(MagickTrue); 4151 } 4152 if (LocaleCompare("gravity",property) == 0) 4153 { 4154 ssize_t 4155 gravity; 4156 4157 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value); 4158 if (gravity < 0) 4159 return(MagickFalse); /* FUTURE: value exception?? */ 4160 image->gravity=(GravityType) gravity; 4161 return(MagickTrue); 4162 } 4163 break; /* not an attribute, add as a property */ 4164 } 4165 case 'H': 4166 case 'h': 4167 { 4168 if (LocaleCompare("height",property) == 0) 4169 { 4170 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4171 "SetReadOnlyProperty","`%s'",property); 4172 return(MagickFalse); 4173 } 4174 break; /* not an attribute, add as a property */ 4175 } 4176 case 'I': 4177 case 'i': 4178 { 4179 if (LocaleCompare("intensity",property) == 0) 4180 { 4181 ssize_t 4182 intensity; 4183 4184 intensity=ParseCommandOption(MagickIntentOptions,MagickFalse,value); 4185 if (intensity < 0) 4186 return(MagickFalse); 4187 image->intensity=(PixelIntensityMethod) intensity; 4188 return(MagickTrue); 4189 } 4190 if (LocaleCompare("intent",property) == 0) 4191 { 4192 ssize_t 4193 rendering_intent; 4194 4195 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse, 4196 value); 4197 if (rendering_intent < 0) 4198 return(MagickFalse); /* FUTURE: value exception?? */ 4199 image->rendering_intent=(RenderingIntent) rendering_intent; 4200 return(MagickTrue); 4201 } 4202 if (LocaleCompare("interpolate",property) == 0) 4203 { 4204 ssize_t 4205 interpolate; 4206 4207 interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse, 4208 value); 4209 if (interpolate < 0) 4210 return(MagickFalse); /* FUTURE: value exception?? */ 4211 image->interpolate=(PixelInterpolateMethod) interpolate; 4212 return(MagickTrue); 4213 } 4214 #if 0 /* Percent escape's sets values with this prefix: for later use 4215 Throwing an exception causes this setting to fail */ 4216 if (LocaleNCompare("iptc:",property,5) == 0) 4217 { 4218 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4219 "SetReadOnlyProperty","`%s'",property); 4220 return(MagickFalse); 4221 } 4222 #endif 4223 break; /* not an attribute, add as a property */ 4224 } 4225 case 'K': 4226 case 'k': 4227 if (LocaleCompare("kurtosis",property) == 0) 4228 { 4229 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4230 "SetReadOnlyProperty","`%s'",property); 4231 return(MagickFalse); 4232 } 4233 break; /* not an attribute, add as a property */ 4234 case 'L': 4235 case 'l': 4236 { 4237 if (LocaleCompare("loop",property) == 0) 4238 { 4239 image->iterations=StringToUnsignedLong(value); 4240 return(MagickTrue); 4241 } 4242 break; /* not an attribute, add as a property */ 4243 } 4244 case 'M': 4245 case 'm': 4246 if ((LocaleCompare("magick",property) == 0) || 4247 (LocaleCompare("max",property) == 0) || 4248 (LocaleCompare("mean",property) == 0) || 4249 (LocaleCompare("min",property) == 0) || 4250 (LocaleCompare("min",property) == 0)) 4251 { 4252 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4253 "SetReadOnlyProperty","`%s'",property); 4254 return(MagickFalse); 4255 } 4256 break; /* not an attribute, add as a property */ 4257 case 'O': 4258 case 'o': 4259 if (LocaleCompare("opaque",property) == 0) 4260 { 4261 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4262 "SetReadOnlyProperty","`%s'",property); 4263 return(MagickFalse); 4264 } 4265 break; /* not an attribute, add as a property */ 4266 case 'P': 4267 case 'p': 4268 { 4269 if (LocaleCompare("page",property) == 0) 4270 { 4271 char 4272 *geometry; 4273 4274 geometry=GetPageGeometry(value); 4275 flags=ParseAbsoluteGeometry(geometry,&image->page); 4276 geometry=DestroyString(geometry); 4277 return(MagickTrue); 4278 } 4279 #if 0 /* Percent escape's sets values with this prefix: for later use 4280 Throwing an exception causes this setting to fail */ 4281 if (LocaleNCompare("pixel:",property,6) == 0) 4282 { 4283 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4284 "SetReadOnlyProperty","`%s'",property); 4285 return(MagickFalse); 4286 } 4287 #endif 4288 if (LocaleCompare("profile",property) == 0) 4289 { 4290 ImageInfo 4291 *image_info; 4292 4293 StringInfo 4294 *profile; 4295 4296 image_info=AcquireImageInfo(); 4297 (void) CopyMagickString(image_info->filename,value,MagickPathExtent); 4298 (void) SetImageInfo(image_info,1,exception); 4299 profile=FileToStringInfo(image_info->filename,~0UL,exception); 4300 if (profile != (StringInfo *) NULL) 4301 status=SetImageProfile(image,image_info->magick,profile,exception); 4302 image_info=DestroyImageInfo(image_info); 4303 return(MagickTrue); 4304 } 4305 break; /* not an attribute, add as a property */ 4306 } 4307 case 'R': 4308 case 'r': 4309 { 4310 if (LocaleCompare("rendering-intent",property) == 0) 4311 { 4312 ssize_t 4313 rendering_intent; 4314 4315 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse, 4316 value); 4317 if (rendering_intent < 0) 4318 return(MagickFalse); /* FUTURE: value exception?? */ 4319 image->rendering_intent=(RenderingIntent) rendering_intent; 4320 return(MagickTrue); 4321 } 4322 break; /* not an attribute, add as a property */ 4323 } 4324 case 'S': 4325 case 's': 4326 if ((LocaleCompare("size",property) == 0) || 4327 (LocaleCompare("skewness",property) == 0) || 4328 (LocaleCompare("scenes",property) == 0) || 4329 (LocaleCompare("standard-deviation",property) == 0)) 4330 { 4331 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4332 "SetReadOnlyProperty","`%s'",property); 4333 return(MagickFalse); 4334 } 4335 break; /* not an attribute, add as a property */ 4336 case 'T': 4337 case 't': 4338 { 4339 if (LocaleCompare("tile-offset",property) == 0) 4340 { 4341 char 4342 *geometry; 4343 4344 geometry=GetPageGeometry(value); 4345 flags=ParseAbsoluteGeometry(geometry,&image->tile_offset); 4346 geometry=DestroyString(geometry); 4347 return(MagickTrue); 4348 } 4349 break; /* not an attribute, add as a property */ 4350 } 4351 case 'U': 4352 case 'u': 4353 { 4354 if (LocaleCompare("units",property) == 0) 4355 { 4356 ssize_t 4357 units; 4358 4359 units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value); 4360 if (units < 0) 4361 return(MagickFalse); /* FUTURE: value exception?? */ 4362 image->units=(ResolutionType) units; 4363 return(MagickTrue); 4364 } 4365 break; /* not an attribute, add as a property */ 4366 } 4367 case 'V': 4368 case 'v': 4369 { 4370 if (LocaleCompare("version",property) == 0) 4371 { 4372 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4373 "SetReadOnlyProperty","`%s'",property); 4374 return(MagickFalse); 4375 } 4376 break; /* not an attribute, add as a property */ 4377 } 4378 case 'W': 4379 case 'w': 4380 { 4381 if (LocaleCompare("width",property) == 0) 4382 { 4383 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4384 "SetReadOnlyProperty","`%s'",property); 4385 return(MagickFalse); 4386 } 4387 break; /* not an attribute, add as a property */ 4388 } 4389 #if 0 /* Percent escape's sets values with this prefix: for later use 4390 Throwing an exception causes this setting to fail */ 4391 case 'X': 4392 case 'x': 4393 { 4394 if (LocaleNCompare("xmp:",property,4) == 0) 4395 { 4396 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4397 "SetReadOnlyProperty","`%s'",property); 4398 return(MagickFalse); 4399 } 4400 break; /* not an attribute, add as a property */ 4401 } 4402 #endif 4403 } 4404 /* Default: not an attribute, add as a property */ 4405 status=AddValueToSplayTree((SplayTreeInfo *) image->properties, 4406 ConstantString(property),ConstantString(value)); 4407 /* FUTURE: error if status is bad? */ 4408 return(status); 4409 } 4410