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