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