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