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