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