property.c revision e2f5dbfc51e9e170aef5cc94826c266f191b68ec
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-2015 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_LCMS_LCMS2_H) 89#include <lcms/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 == MagickSignature); 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 == MagickSignature); 142 if( IfMagickTrue(clone_image->debug) ) 143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 144 clone_image->filename); 145 (void) CopyMagickString(image->filename,clone_image->filename,MaxTextExtent); 146 (void) CopyMagickString(image->magick_filename,clone_image->magick_filename, 147 MaxTextExtent); 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[MaxTextExtent], 235 value[MaxTextExtent]; 236 237 register char 238 *p; 239 240 assert(image != (Image *) NULL); 241 assert(property != (const char *) NULL); 242 (void) CopyMagickString(key,property,MaxTextExtent-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,MaxTextExtent); 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 == MagickSignature); 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 == MagickSignature); 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[MaxTextExtent]; 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,MaxTextExtent,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[MaxTextExtent], 566 name[MaxTextExtent], 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:%[^\n]\n%[^\n]",&start,&stop,name, 600 format); 601 if ((count != 2) && (count != 3) && (count != 4)) 602 return(MagickFalse); 603 if (count < 4) 604 (void) CopyMagickString(format,"SVG",MaxTextExtent); 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) >= (MaxTextExtent-1)) 637 resource=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent, 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) >= (MaxTextExtent-1)) 674 attribute=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent, 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,MaxTextExtent-length, \ 780 format", ",arg); \ 781 if (length >= (MaxTextExtent-1)) \ 782 length=MaxTextExtent-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,MaxTextExtent-length, \ 806 format", ",(arg1),(arg2)); \ 807 if (length >= (MaxTextExtent-1)) \ 808 length=MaxTextExtent-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 == (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 all=0; 1163 tag=(~0UL); 1164 switch (*(property+5)) 1165 { 1166 case '*': 1167 { 1168 /* 1169 Caller has asked for all the tags in the EXIF data. 1170 */ 1171 tag=0; 1172 all=1; /* return the data in description=value format */ 1173 break; 1174 } 1175 case '!': 1176 { 1177 tag=0; 1178 all=2; /* return the data in tagid=value format */ 1179 break; 1180 } 1181 case '#': 1182 case '@': 1183 { 1184 int 1185 c; 1186 1187 size_t 1188 n; 1189 1190 /* 1191 Check for a hex based tag specification first. 1192 */ 1193 tag=(*(property+5) == '@') ? 1UL : 0UL; 1194 property+=6; 1195 n=strlen(property); 1196 if (n != 4) 1197 return(MagickFalse); 1198 /* 1199 Parse tag specification as a hex number. 1200 */ 1201 n/=4; 1202 do 1203 { 1204 for (i=(ssize_t) n-1L; i >= 0; i--) 1205 { 1206 c=(*property++); 1207 tag<<=4; 1208 if ((c >= '0') && (c <= '9')) 1209 tag|=(c-'0'); 1210 else 1211 if ((c >= 'A') && (c <= 'F')) 1212 tag|=(c-('A'-10)); 1213 else 1214 if ((c >= 'a') && (c <= 'f')) 1215 tag|=(c-('a'-10)); 1216 else 1217 return(MagickFalse); 1218 } 1219 } while (*property != '\0'); 1220 break; 1221 } 1222 default: 1223 { 1224 /* 1225 Try to match the text with a tag name instead. 1226 */ 1227 for (i=0; ; i++) 1228 { 1229 if (EXIFTag[i].tag == 0) 1230 break; 1231 if (LocaleCompare(EXIFTag[i].description,property) == 0) 1232 { 1233 tag=(size_t) EXIFTag[i].tag; 1234 break; 1235 } 1236 } 1237 break; 1238 } 1239 } 1240 if (tag == (~0UL)) 1241 return(MagickFalse); 1242 length=GetStringInfoLength(profile); 1243 exif=GetStringInfoDatum(profile); 1244 while (length != 0) 1245 { 1246 if (ReadPropertyByte(&exif,&length) != 0x45) 1247 continue; 1248 if (ReadPropertyByte(&exif,&length) != 0x78) 1249 continue; 1250 if (ReadPropertyByte(&exif,&length) != 0x69) 1251 continue; 1252 if (ReadPropertyByte(&exif,&length) != 0x66) 1253 continue; 1254 if (ReadPropertyByte(&exif,&length) != 0x00) 1255 continue; 1256 if (ReadPropertyByte(&exif,&length) != 0x00) 1257 continue; 1258 break; 1259 } 1260 if (length < 16) 1261 return(MagickFalse); 1262 id=(ssize_t) ((int) ReadPropertyShort(LSBEndian,exif)); 1263 endian=LSBEndian; 1264 if (id == 0x4949) 1265 endian=LSBEndian; 1266 else 1267 if (id == 0x4D4D) 1268 endian=MSBEndian; 1269 else 1270 return(MagickFalse); 1271 if (ReadPropertyShort(endian,exif+2) != 0x002a) 1272 return(MagickFalse); 1273 /* 1274 This the offset to the first IFD. 1275 */ 1276 offset=(ssize_t) ((int) ReadPropertyLong(endian,exif+4)); 1277 if ((offset < 0) || (size_t) offset >= length) 1278 return(MagickFalse); 1279 /* 1280 Set the pointer to the first IFD and follow it were it leads. 1281 */ 1282 status=MagickFalse; 1283 directory=exif+offset; 1284 level=0; 1285 entry=0; 1286 tag_offset=0; 1287 exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL, 1288 (void *(*)(void *)) NULL,(void *(*)(void *)) NULL); 1289 do 1290 { 1291 /* 1292 If there is anything on the stack then pop it off. 1293 */ 1294 if (level > 0) 1295 { 1296 level--; 1297 directory=directory_stack[level].directory; 1298 entry=directory_stack[level].entry; 1299 tag_offset=directory_stack[level].offset; 1300 } 1301 /* 1302 Determine how many entries there are in the current IFD. 1303 */ 1304 number_entries=(size_t) ((int) ReadPropertyShort(endian,directory)); 1305 for ( ; entry < number_entries; entry++) 1306 { 1307 register unsigned char 1308 *p, 1309 *q; 1310 1311 size_t 1312 format; 1313 1314 ssize_t 1315 number_bytes, 1316 components; 1317 1318 q=(unsigned char *) (directory+(12*entry)+2); 1319 if (GetValueFromSplayTree(exif_resources,q) == q) 1320 break; 1321 (void) AddValueToSplayTree(exif_resources,q,q); 1322 tag_value=(ssize_t) ((int) ReadPropertyShort(endian,q)+tag_offset); 1323 format=(size_t) ((int) ReadPropertyShort(endian,q+2)); 1324 if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes))) 1325 break; 1326 components=(ssize_t) ((int) ReadPropertyLong(endian,q+4)); 1327 number_bytes=(size_t) components*tag_bytes[format]; 1328 if (number_bytes < components) 1329 break; /* prevent overflow */ 1330 if (number_bytes <= 4) 1331 p=q+8; 1332 else 1333 { 1334 ssize_t 1335 offset; 1336 1337 /* 1338 The directory entry contains an offset. 1339 */ 1340 offset=(ssize_t) ((int) ReadPropertyLong(endian,q+8)); 1341 if ((offset < 0) || (size_t) offset >= length) 1342 continue; 1343 if ((ssize_t) (offset+number_bytes) < offset) 1344 continue; /* prevent overflow */ 1345 if ((size_t) (offset+number_bytes) > length) 1346 continue; 1347 p=(unsigned char *) (exif+offset); 1348 } 1349 if ((all != 0) || (tag == (size_t) tag_value)) 1350 { 1351 char 1352 buffer[MaxTextExtent], 1353 *value; 1354 1355 value=(char *) NULL; 1356 *buffer='\0'; 1357 switch (format) 1358 { 1359 case EXIF_FMT_BYTE: 1360 case EXIF_FMT_UNDEFINED: 1361 { 1362 EXIFMultipleValues(1,"%.20g",(double) (*(unsigned char *) p1)); 1363 break; 1364 } 1365 case EXIF_FMT_SBYTE: 1366 { 1367 EXIFMultipleValues(1,"%.20g",(double) (*(signed char *) p1)); 1368 break; 1369 } 1370 case EXIF_FMT_SSHORT: 1371 { 1372 EXIFMultipleValues(2,"%hd",ReadPropertyShort(endian,p1)); 1373 break; 1374 } 1375 case EXIF_FMT_USHORT: 1376 { 1377 EXIFMultipleValues(2,"%hu",ReadPropertyShort(endian,p1)); 1378 break; 1379 } 1380 case EXIF_FMT_ULONG: 1381 { 1382 EXIFMultipleValues(4,"%.20g",(double) 1383 ((int) ReadPropertyLong(endian,p1))); 1384 break; 1385 } 1386 case EXIF_FMT_SLONG: 1387 { 1388 EXIFMultipleValues(4,"%.20g",(double) 1389 ((int) ReadPropertyLong(endian,p1))); 1390 break; 1391 } 1392 case EXIF_FMT_URATIONAL: 1393 { 1394 EXIFMultipleFractions(8,"%.20g/%.20g",(double) 1395 ((int) ReadPropertyLong(endian,p1)),(double) 1396 ((int) ReadPropertyLong(endian,p1+4))); 1397 break; 1398 } 1399 case EXIF_FMT_SRATIONAL: 1400 { 1401 EXIFMultipleFractions(8,"%.20g/%.20g",(double) 1402 ((int) ReadPropertyLong(endian,p1)),(double) 1403 ((int) ReadPropertyLong(endian,p1+4))); 1404 break; 1405 } 1406 case EXIF_FMT_SINGLE: 1407 { 1408 EXIFMultipleValues(4,"%f",(double) *(float *) p1); 1409 break; 1410 } 1411 case EXIF_FMT_DOUBLE: 1412 { 1413 EXIFMultipleValues(8,"%f",*(double *) p1); 1414 break; 1415 } 1416 default: 1417 case EXIF_FMT_STRING: 1418 { 1419 value=(char *) NULL; 1420 if (~((size_t) number_bytes) >= 1) 1421 value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL, 1422 sizeof(*value)); 1423 if (value != (char *) NULL) 1424 { 1425 register ssize_t 1426 i; 1427 1428 for (i=0; i < (ssize_t) number_bytes; i++) 1429 { 1430 value[i]='.'; 1431 if ((isprint((int) p[i]) != 0) || (p[i] == '\0')) 1432 value[i]=(char) p[i]; 1433 } 1434 value[i]='\0'; 1435 } 1436 break; 1437 } 1438 } 1439 if (value != (char *) NULL) 1440 { 1441 char 1442 *key; 1443 1444 register const char 1445 *p; 1446 1447 key=AcquireString(property); 1448 switch (all) 1449 { 1450 case 1: 1451 { 1452 const char 1453 *description; 1454 1455 register ssize_t 1456 i; 1457 1458 description="unknown"; 1459 for (i=0; ; i++) 1460 { 1461 if (EXIFTag[i].tag == 0) 1462 break; 1463 if ((ssize_t) EXIFTag[i].tag == tag_value) 1464 { 1465 description=EXIFTag[i].description; 1466 break; 1467 } 1468 } 1469 (void) FormatLocaleString(key,MaxTextExtent,"%s",description); 1470 if (level == 2) 1471 (void) SubstituteString(&key,"exif:","exif:thumbnail:"); 1472 break; 1473 } 1474 case 2: 1475 { 1476 if (tag_value < 0x10000) 1477 (void) FormatLocaleString(key,MaxTextExtent,"#%04lx", 1478 (unsigned long) tag_value); 1479 else 1480 if (tag_value < 0x20000) 1481 (void) FormatLocaleString(key,MaxTextExtent,"@%04lx", 1482 (unsigned long) (tag_value & 0xffff)); 1483 else 1484 (void) FormatLocaleString(key,MaxTextExtent,"unknown"); 1485 break; 1486 } 1487 default: 1488 { 1489 if (level == 2) 1490 (void) SubstituteString(&key,"exif:","exif:thumbnail:"); 1491 } 1492 } 1493 p=(const char *) NULL; 1494 if (image->properties != (void *) NULL) 1495 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *) 1496 image->properties,key); 1497 if (p == (const char *) NULL) 1498 (void) SetImageProperty((Image *) image,key,value,exception); 1499 value=DestroyString(value); 1500 key=DestroyString(key); 1501 status=MagickTrue; 1502 } 1503 } 1504 if ((tag_value == TAG_EXIF_OFFSET) || 1505 (tag_value == TAG_INTEROP_OFFSET) || (tag_value == TAG_GPS_OFFSET)) 1506 { 1507 ssize_t 1508 offset; 1509 1510 offset=(ssize_t) ((int) ReadPropertyLong(endian,p)); 1511 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2))) 1512 { 1513 ssize_t 1514 tag_offset1; 1515 1516 tag_offset1=(ssize_t) ((tag_value == TAG_GPS_OFFSET) ? 0x10000 : 1517 0); 1518 directory_stack[level].directory=directory; 1519 entry++; 1520 directory_stack[level].entry=entry; 1521 directory_stack[level].offset=tag_offset; 1522 level++; 1523 directory_stack[level].directory=exif+offset; 1524 directory_stack[level].offset=tag_offset1; 1525 directory_stack[level].entry=0; 1526 level++; 1527 if ((directory+2+(12*number_entries)) > (exif+length)) 1528 break; 1529 offset=(ssize_t) ((int) ReadPropertyLong(endian,directory+2+(12* 1530 number_entries))); 1531 if ((offset != 0) && ((size_t) offset < length) && 1532 (level < (MaxDirectoryStack-2))) 1533 { 1534 directory_stack[level].directory=exif+offset; 1535 directory_stack[level].entry=0; 1536 directory_stack[level].offset=tag_offset1; 1537 level++; 1538 } 1539 } 1540 break; 1541 } 1542 } 1543 } while (level > 0); 1544 exif_resources=DestroySplayTree(exif_resources); 1545 return(status); 1546} 1547 1548static MagickBooleanType GetICCProperty(const Image *image,const char *property, 1549 ExceptionInfo *exception) 1550{ 1551 const StringInfo 1552 *profile; 1553 1554 magick_unreferenced(property); 1555 1556 profile=GetImageProfile(image,"icc"); 1557 if (profile == (StringInfo *) NULL) 1558 profile=GetImageProfile(image,"icm"); 1559 if (profile == (StringInfo *) NULL) 1560 return(MagickFalse); 1561 if (GetStringInfoLength(profile) < 128) 1562 return(MagickFalse); /* minimum ICC profile length */ 1563#if defined(MAGICKCORE_LCMS_DELEGATE) 1564 { 1565 cmsHPROFILE 1566 icc_profile; 1567 1568 icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile), 1569 (cmsUInt32Number) GetStringInfoLength(profile)); 1570 if (icc_profile != (cmsHPROFILE *) NULL) 1571 { 1572#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000) 1573 const char 1574 *name; 1575 1576 name=cmsTakeProductName(icc_profile); 1577 if (name != (const char *) NULL) 1578 (void) SetImageProperty((Image *) image,"icc:name",name,exception); 1579#else 1580 char 1581 info[MaxTextExtent]; 1582 1583 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription, 1584 "en","US",info,MaxTextExtent); 1585 (void) SetImageProperty((Image *) image,"icc:description",info, 1586 exception); 1587 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoManufacturer, 1588 "en","US",info,MaxTextExtent); 1589 (void) SetImageProperty((Image *) image,"icc:manufacturer",info, 1590 exception); 1591 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoModel,"en", 1592 "US",info,MaxTextExtent); 1593 (void) SetImageProperty((Image *) image,"icc:model",info,exception); 1594 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoCopyright, 1595 "en","US",info,MaxTextExtent); 1596 (void) SetImageProperty((Image *) image,"icc:copyright",info,exception); 1597#endif 1598 (void) cmsCloseProfile(icc_profile); 1599 } 1600 } 1601#endif 1602 return(MagickTrue); 1603} 1604 1605static MagickBooleanType SkipXMPValue(const char *value) 1606{ 1607 if (value == (const char*) NULL) 1608 return(MagickTrue); 1609 while (*value != '\0') 1610 { 1611 if (isspace((int) ((unsigned char) *value)) == 0) 1612 return(MagickFalse); 1613 value++; 1614 } 1615 return(MagickTrue); 1616} 1617 1618static MagickBooleanType GetXMPProperty(const Image *image,const char *property) 1619{ 1620 char 1621 *xmp_profile; 1622 1623 const char 1624 *content; 1625 1626 const StringInfo 1627 *profile; 1628 1629 ExceptionInfo 1630 *exception; 1631 1632 MagickBooleanType 1633 status; 1634 1635 register const char 1636 *p; 1637 1638 XMLTreeInfo 1639 *child, 1640 *description, 1641 *node, 1642 *rdf, 1643 *xmp; 1644 1645 profile=GetImageProfile(image,"xmp"); 1646 if (profile == (StringInfo *) NULL) 1647 return(MagickFalse); 1648 if ((property == (const char *) NULL) || (*property == '\0')) 1649 return(MagickFalse); 1650 xmp_profile=StringInfoToString(profile); 1651 if (xmp_profile == (char *) NULL) 1652 return(MagickFalse); 1653 for (p=xmp_profile; *p != '\0'; p++) 1654 if ((*p == '<') && (*(p+1) == 'x')) 1655 break; 1656 exception=AcquireExceptionInfo(); 1657 xmp=NewXMLTree((char *) p,exception); 1658 xmp_profile=DestroyString(xmp_profile); 1659 exception=DestroyExceptionInfo(exception); 1660 if (xmp == (XMLTreeInfo *) NULL) 1661 return(MagickFalse); 1662 status=MagickFalse; 1663 rdf=GetXMLTreeChild(xmp,"rdf:RDF"); 1664 if (rdf != (XMLTreeInfo *) NULL) 1665 { 1666 if (image->properties == (void *) NULL) 1667 ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString, 1668 RelinquishMagickMemory,RelinquishMagickMemory); 1669 description=GetXMLTreeChild(rdf,"rdf:Description"); 1670 while (description != (XMLTreeInfo *) NULL) 1671 { 1672 node=GetXMLTreeChild(description,(const char *) NULL); 1673 while (node != (XMLTreeInfo *) NULL) 1674 { 1675 child=GetXMLTreeChild(node,(const char *) NULL); 1676 content=GetXMLTreeContent(node); 1677 if ((child == (XMLTreeInfo *) NULL) && 1678 (SkipXMPValue(content) == MagickFalse)) 1679 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties, 1680 ConstantString(GetXMLTreeTag(node)),ConstantString(content)); 1681 while (child != (XMLTreeInfo *) NULL) 1682 { 1683 content=GetXMLTreeContent(child); 1684 if (SkipXMPValue(content) == MagickFalse) 1685 (void) AddValueToSplayTree((SplayTreeInfo *) image->properties, 1686 ConstantString(GetXMLTreeTag(child)),ConstantString(content)); 1687 child=GetXMLTreeSibling(child); 1688 } 1689 node=GetXMLTreeSibling(node); 1690 } 1691 description=GetNextXMLTreeTag(description); 1692 } 1693 } 1694 xmp=DestroyXMLTree(xmp); 1695 return(status); 1696} 1697 1698static char *TracePSClippath(const unsigned char *blob,size_t length, 1699 const size_t magick_unused(columns),const size_t magick_unused(rows)) 1700{ 1701 char 1702 *path, 1703 *message; 1704 1705 MagickBooleanType 1706 in_subpath; 1707 1708 PointInfo 1709 first[3], 1710 last[3], 1711 point[3]; 1712 1713 register ssize_t 1714 i, 1715 x; 1716 1717 ssize_t 1718 knot_count, 1719 selector, 1720 y; 1721 1722 path=AcquireString((char *) NULL); 1723 if (path == (char *) NULL) 1724 return((char *) NULL); 1725 message=AcquireString((char *) NULL); 1726 (void) FormatLocaleString(message,MaxTextExtent,"/ClipImage\n"); 1727 (void) ConcatenateString(&path,message); 1728 (void) FormatLocaleString(message,MaxTextExtent,"{\n"); 1729 (void) ConcatenateString(&path,message); 1730 (void) FormatLocaleString(message,MaxTextExtent," /c {curveto} bind def\n"); 1731 (void) ConcatenateString(&path,message); 1732 (void) FormatLocaleString(message,MaxTextExtent," /l {lineto} bind def\n"); 1733 (void) ConcatenateString(&path,message); 1734 (void) FormatLocaleString(message,MaxTextExtent," /m {moveto} bind def\n"); 1735 (void) ConcatenateString(&path,message); 1736 (void) FormatLocaleString(message,MaxTextExtent, 1737 " /v {currentpoint 6 2 roll curveto} bind def\n"); 1738 (void) ConcatenateString(&path,message); 1739 (void) FormatLocaleString(message,MaxTextExtent, 1740 " /y {2 copy curveto} bind def\n"); 1741 (void) ConcatenateString(&path,message); 1742 (void) FormatLocaleString(message,MaxTextExtent, 1743 " /z {closepath} bind def\n"); 1744 (void) ConcatenateString(&path,message); 1745 (void) FormatLocaleString(message,MaxTextExtent," newpath\n"); 1746 (void) ConcatenateString(&path,message); 1747 /* 1748 The clipping path format is defined in "Adobe Photoshop File 1749 Formats Specification" version 6.0 downloadable from adobe.com. 1750 */ 1751 (void) ResetMagickMemory(point,0,sizeof(point)); 1752 (void) ResetMagickMemory(first,0,sizeof(first)); 1753 (void) ResetMagickMemory(last,0,sizeof(last)); 1754 knot_count=0; 1755 in_subpath=MagickFalse; 1756 while (length > 0) 1757 { 1758 selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length)); 1759 switch (selector) 1760 { 1761 case 0: 1762 case 3: 1763 { 1764 if (knot_count != 0) 1765 { 1766 blob+=24; 1767 length-=MagickMin(24,(ssize_t) length); 1768 break; 1769 } 1770 /* 1771 Expected subpath length record. 1772 */ 1773 knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length)); 1774 blob+=22; 1775 length-=MagickMin(22,(ssize_t) length); 1776 break; 1777 } 1778 case 1: 1779 case 2: 1780 case 4: 1781 case 5: 1782 { 1783 if (knot_count == 0) 1784 { 1785 /* 1786 Unexpected subpath knot 1787 */ 1788 blob+=24; 1789 length-=MagickMin(24,(ssize_t) length); 1790 break; 1791 } 1792 /* 1793 Add sub-path knot 1794 */ 1795 for (i=0; i < 3; i++) 1796 { 1797 size_t 1798 xx, 1799 yy; 1800 1801 yy=(size_t) ((int) ReadPropertyMSBLong(&blob,&length)); 1802 xx=(size_t) ((int) ReadPropertyMSBLong(&blob,&length)); 1803 x=(ssize_t) xx; 1804 if (xx > 2147483647) 1805 x=(ssize_t) xx-4294967295U-1; 1806 y=(ssize_t) yy; 1807 if (yy > 2147483647) 1808 y=(ssize_t) yy-4294967295U-1; 1809 point[i].x=(double) x/4096/4096; 1810 point[i].y=1.0-(double) y/4096/4096; 1811 } 1812 if( IfMagickFalse(in_subpath) ) 1813 { 1814 (void) FormatLocaleString(message,MaxTextExtent," %g %g m\n", 1815 point[1].x,point[1].y); 1816 for (i=0; i < 3; i++) 1817 { 1818 first[i]=point[i]; 1819 last[i]=point[i]; 1820 } 1821 } 1822 else 1823 { 1824 /* 1825 Handle special cases when Bezier curves are used to describe 1826 corners and straight lines. 1827 */ 1828 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) && 1829 (point[0].x == point[1].x) && (point[0].y == point[1].y)) 1830 (void) FormatLocaleString(message,MaxTextExtent, 1831 " %g %g l\n",point[1].x,point[1].y); 1832 else 1833 if ((last[1].x == last[2].x) && (last[1].y == last[2].y)) 1834 (void) FormatLocaleString(message,MaxTextExtent, 1835 " %g %g %g %g v\n",point[0].x,point[0].y, 1836 point[1].x,point[1].y); 1837 else 1838 if ((point[0].x == point[1].x) && (point[0].y == point[1].y)) 1839 (void) FormatLocaleString(message,MaxTextExtent, 1840 " %g %g %g %g y\n",last[2].x,last[2].y, 1841 point[1].x,point[1].y); 1842 else 1843 (void) FormatLocaleString(message,MaxTextExtent, 1844 " %g %g %g %g %g %g c\n",last[2].x, 1845 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y); 1846 for (i=0; i < 3; i++) 1847 last[i]=point[i]; 1848 } 1849 (void) ConcatenateString(&path,message); 1850 in_subpath=MagickTrue; 1851 knot_count--; 1852 /* 1853 Close the subpath if there are no more knots. 1854 */ 1855 if (knot_count == 0) 1856 { 1857 /* 1858 Same special handling as above except we compare to the 1859 first point in the path and close the path. 1860 */ 1861 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) && 1862 (first[0].x == first[1].x) && (first[0].y == first[1].y)) 1863 (void) FormatLocaleString(message,MaxTextExtent, 1864 " %g %g l z\n",first[1].x,first[1].y); 1865 else 1866 if ((last[1].x == last[2].x) && (last[1].y == last[2].y)) 1867 (void) FormatLocaleString(message,MaxTextExtent, 1868 " %g %g %g %g v z\n",first[0].x,first[0].y, 1869 first[1].x,first[1].y); 1870 else 1871 if ((first[0].x == first[1].x) && (first[0].y == first[1].y)) 1872 (void) FormatLocaleString(message,MaxTextExtent, 1873 " %g %g %g %g y z\n",last[2].x,last[2].y, 1874 first[1].x,first[1].y); 1875 else 1876 (void) FormatLocaleString(message,MaxTextExtent, 1877 " %g %g %g %g %g %g c z\n",last[2].x, 1878 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y); 1879 (void) ConcatenateString(&path,message); 1880 in_subpath=MagickFalse; 1881 } 1882 break; 1883 } 1884 case 6: 1885 case 7: 1886 case 8: 1887 default: 1888 { 1889 blob+=24; 1890 length-=MagickMin(24,(ssize_t) length); 1891 break; 1892 } 1893 } 1894 } 1895 /* 1896 Returns an empty PS path if the path has no knots. 1897 */ 1898 (void) FormatLocaleString(message,MaxTextExtent," eoclip\n"); 1899 (void) ConcatenateString(&path,message); 1900 (void) FormatLocaleString(message,MaxTextExtent,"} bind def"); 1901 (void) ConcatenateString(&path,message); 1902 message=DestroyString(message); 1903 return(path); 1904} 1905 1906static char *TraceSVGClippath(const unsigned char *blob,size_t length, 1907 const size_t columns,const size_t rows) 1908{ 1909 char 1910 *path, 1911 *message; 1912 1913 MagickBooleanType 1914 in_subpath; 1915 1916 PointInfo 1917 first[3], 1918 last[3], 1919 point[3]; 1920 1921 register ssize_t 1922 i; 1923 1924 ssize_t 1925 knot_count, 1926 selector, 1927 x, 1928 y; 1929 1930 path=AcquireString((char *) NULL); 1931 if (path == (char *) NULL) 1932 return((char *) NULL); 1933 message=AcquireString((char *) NULL); 1934 (void) FormatLocaleString(message,MaxTextExtent, 1935 ("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" 1936 "<svg xmlns=\"http://www.w3.org/2000/svg\"" 1937 " width=\"%.20g\" height=\"%.20g\">\n" 1938 "<g>\n" 1939 "<path fill-rule=\"evenodd\" style=\"fill:#00000000;stroke:#00000000;" 1940 "stroke-width:0;stroke-antialiasing:false\" d=\"\n"), 1941 (double) columns,(double) rows); 1942 (void) ConcatenateString(&path,message); 1943 (void) ResetMagickMemory(point,0,sizeof(point)); 1944 (void) ResetMagickMemory(first,0,sizeof(first)); 1945 (void) ResetMagickMemory(last,0,sizeof(last)); 1946 knot_count=0; 1947 in_subpath=MagickFalse; 1948 while (length != 0) 1949 { 1950 selector=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length)); 1951 switch (selector) 1952 { 1953 case 0: 1954 case 3: 1955 { 1956 if (knot_count != 0) 1957 { 1958 blob+=24; 1959 length-=MagickMin(24,(ssize_t) length); 1960 break; 1961 } 1962 /* 1963 Expected subpath length record. 1964 */ 1965 knot_count=(ssize_t) ((int) ReadPropertyMSBShort(&blob,&length)); 1966 blob+=22; 1967 length-=MagickMin(22,(ssize_t) length); 1968 break; 1969 } 1970 case 1: 1971 case 2: 1972 case 4: 1973 case 5: 1974 { 1975 if (knot_count == 0) 1976 { 1977 /* 1978 Unexpected subpath knot. 1979 */ 1980 blob+=24; 1981 length-=MagickMin(24,(ssize_t) length); 1982 break; 1983 } 1984 /* 1985 Add sub-path knot 1986 */ 1987 for (i=0; i < 3; i++) 1988 { 1989 unsigned int 1990 xx, 1991 yy; 1992 1993 yy=(unsigned int) ((int) ReadPropertyMSBLong(&blob,&length)); 1994 xx=(unsigned int) ((int) ReadPropertyMSBLong(&blob,&length)); 1995 x=(ssize_t) xx; 1996 if (xx > 2147483647) 1997 x=(ssize_t) xx-4294967295U-1; 1998 y=(ssize_t) yy; 1999 if (yy > 2147483647) 2000 y=(ssize_t) yy-4294967295U-1; 2001 point[i].x=(double) x*columns/4096/4096; 2002 point[i].y=(double) y*rows/4096/4096; 2003 } 2004 if (in_subpath == MagickFalse) 2005 { 2006 (void) FormatLocaleString(message,MaxTextExtent,"M %g %g\n", 2007 point[1].x,point[1].y); 2008 for (i=0; i < 3; i++) 2009 { 2010 first[i]=point[i]; 2011 last[i]=point[i]; 2012 } 2013 } 2014 else 2015 { 2016 /* 2017 Handle special cases when Bezier curves are used to describe 2018 corners and straight lines. 2019 */ 2020 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) && 2021 (point[0].x == point[1].x) && (point[0].y == point[1].y)) 2022 (void) FormatLocaleString(message,MaxTextExtent, 2023 "L %g %g\n",point[1].x,point[1].y); 2024 else 2025 (void) FormatLocaleString(message,MaxTextExtent, 2026 "C %g %g %g %g %g %g\n",last[2].x, 2027 last[2].y,point[0].x,point[0].y,point[1].x,point[1].y); 2028 for (i=0; i < 3; i++) 2029 last[i]=point[i]; 2030 } 2031 (void) ConcatenateString(&path,message); 2032 in_subpath=MagickTrue; 2033 knot_count--; 2034 /* 2035 Close the subpath if there are no more knots. 2036 */ 2037 if (knot_count == 0) 2038 { 2039 /* 2040 Same special handling as above except we compare to the 2041 first point in the path and close the path. 2042 */ 2043 if ((last[1].x == last[2].x) && (last[1].y == last[2].y) && 2044 (first[0].x == first[1].x) && (first[0].y == first[1].y)) 2045 (void) FormatLocaleString(message,MaxTextExtent, 2046 "L %g %g Z\n",first[1].x,first[1].y); 2047 else 2048 (void) FormatLocaleString(message,MaxTextExtent, 2049 "C %g %g %g %g %g %g Z\n",last[2].x, 2050 last[2].y,first[0].x,first[0].y,first[1].x,first[1].y); 2051 (void) ConcatenateString(&path,message); 2052 in_subpath=MagickFalse; 2053 } 2054 break; 2055 } 2056 case 6: 2057 case 7: 2058 case 8: 2059 default: 2060 { 2061 blob+=24; 2062 length-=MagickMin(24,(ssize_t) length); 2063 break; 2064 } 2065 } 2066 } 2067 /* 2068 Return an empty SVG image if the path does not have knots. 2069 */ 2070 (void) ConcatenateString(&path,"\"/>\n</g>\n</svg>\n"); 2071 message=DestroyString(message); 2072 return(path); 2073} 2074 2075MagickExport const char *GetImageProperty(const Image *image, 2076 const char *property,ExceptionInfo *exception) 2077{ 2078 register const char 2079 *p; 2080 2081 assert(image != (Image *) NULL); 2082 assert(image->signature == MagickSignature); 2083 if (IfMagickTrue(image->debug)) 2084 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2085 p=(const char *) NULL; 2086 if (image->properties != (void *) NULL) 2087 { 2088 if (property == (const char *) NULL) 2089 { 2090 ResetSplayTreeIterator((SplayTreeInfo *) image->properties); 2091 p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *) 2092 image->properties); 2093 return(p); 2094 } 2095 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *) 2096 image->properties,property); 2097 if (p != (const char *) NULL) 2098 return(p); 2099 } 2100 if ((property == (const char *) NULL) || 2101 (strchr(property,':') == (char *) NULL)) 2102 return(p); 2103 switch (*property) 2104 { 2105 case '8': 2106 { 2107 if (LocaleNCompare("8bim:",property,5) == 0) 2108 { 2109 (void) Get8BIMProperty(image,property,exception); 2110 break; 2111 } 2112 break; 2113 } 2114 case 'E': 2115 case 'e': 2116 { 2117 if (LocaleNCompare("exif:",property,5) == 0) 2118 { 2119 (void) GetEXIFProperty(image,property,exception); 2120 break; 2121 } 2122 break; 2123 } 2124 case 'I': 2125 case 'i': 2126 { 2127 if ((LocaleNCompare("icc:",property,4) == 0) || 2128 (LocaleNCompare("icm:",property,4) == 0)) 2129 { 2130 (void) GetICCProperty(image,property,exception); 2131 break; 2132 } 2133 if (LocaleNCompare("iptc:",property,5) == 0) 2134 { 2135 (void) GetIPTCProperty(image,property,exception); 2136 break; 2137 } 2138 break; 2139 } 2140 case 'X': 2141 case 'x': 2142 { 2143 if (LocaleNCompare("xmp:",property,4) == 0) 2144 { 2145 (void) GetXMPProperty(image,property); 2146 break; 2147 } 2148 break; 2149 } 2150 default: 2151 break; 2152 } 2153 if (image->properties != (void *) NULL) 2154 { 2155 p=(const char *) GetValueFromSplayTree((SplayTreeInfo *) 2156 image->properties,property); 2157 return(p); 2158 } 2159 return((const char *) NULL); 2160} 2161 2162/* 2163%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2164% % 2165% % 2166% % 2167+ G e t M a g i c k P r o p e r t y % 2168% % 2169% % 2170% % 2171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2172% 2173% GetMagickProperty() gets attributes or calculated values that is associated 2174% with a fixed known property name, or single letter property. It may be 2175% called if no image is defined (IMv7), in which case only global image_info 2176% values are available: 2177% 2178% \n newline 2179% \r carriage return 2180% < less-than character. 2181% > greater-than character. 2182% & ampersand character. 2183% %% a percent sign 2184% %b file size of image read in 2185% %c comment meta-data property 2186% %d directory component of path 2187% %e filename extension or suffix 2188% %f filename (including suffix) 2189% %g layer canvas page geometry (equivalent to "%Wx%H%X%Y") 2190% %h current image height in pixels 2191% %i image filename (note: becomes output filename for "info:") 2192% %k CALCULATED: number of unique colors 2193% %l label meta-data property 2194% %m image file format (file magic) 2195% %n number of images in current image sequence 2196% %o output filename (used for delegates) 2197% %p index of image in current image list 2198% %q quantum depth (compile-time constant) 2199% %r image class and colorspace 2200% %s scene number (from input unless re-assigned) 2201% %t filename without directory or extension (suffix) 2202% %u unique temporary filename (used for delegates) 2203% %w current width in pixels 2204% %x x resolution (density) 2205% %y y resolution (density) 2206% %z image depth (as read in unless modified, image save depth) 2207% %A image transparency channel enabled (true/false) 2208% %C image compression type 2209% %D image GIF dispose method 2210% %G original image size (%wx%h; before any resizes) 2211% %H page (canvas) height 2212% %M Magick filename (original file exactly as given, including read mods) 2213% %O page (canvas) offset ( = %X%Y ) 2214% %P page (canvas) size ( = %Wx%H ) 2215% %Q image compression quality ( 0 = default ) 2216% %S ?? scenes ?? 2217% %T image time delay (in centi-seconds) 2218% %U image resolution units 2219% %W page (canvas) width 2220% %X page (canvas) x offset (including sign) 2221% %Y page (canvas) y offset (including sign) 2222% %Z unique filename (used for delegates) 2223% %@ CALCULATED: trim bounding box (without actually trimming) 2224% %# CALCULATED: 'signature' hash of image values 2225% 2226% This routine only handles specifically known properties. It does not 2227% handle special prefixed properties, profiles, or expressions. Nor does 2228% it return any free-form property strings. 2229% 2230% The returned string is stored in a structure somewhere, and should not be 2231% directly freed. If the string was generated (common) the string will be 2232% stored as as either as artifact or option 'get-property'. These may be 2233% deleted (cleaned up) when no longer required, but neither artifact or 2234% option is guranteed to exist. 2235% 2236% The format of the GetMagickProperty method is: 2237% 2238% const char *GetMagickProperty(ImageInfo *image_info,Image *image, 2239% const char *property,ExceptionInfo *exception) 2240% 2241% A description of each parameter follows: 2242% 2243% o image_info: the image info (optional) 2244% 2245% o image: the image (optional) 2246% 2247% o key: the key. 2248% 2249% o exception: return any errors or warnings in this structure. 2250% 2251*/ 2252#define WarnNoImageReturn(format,arg) \ 2253 if (image == (Image *) NULL ) { \ 2254 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \ 2255 "NoImageForProperty",format,arg); \ 2256 return((const char *) NULL); \ 2257 } 2258#define WarnNoImageInfoReturn(format,arg) \ 2259 if (image_info == (ImageInfo *) NULL ) { \ 2260 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, \ 2261 "NoImageInfoForProperty",format,arg); \ 2262 return((const char *) NULL); \ 2263 } 2264 2265static const char *GetMagickPropertyLetter(ImageInfo *image_info, 2266 Image *image,const char letter,ExceptionInfo *exception) 2267{ 2268 char 2269 value[MaxTextExtent]; /* formated string to store as a returned artifact */ 2270 2271 const char 2272 *string; /* return a string already stored somewher */ 2273 2274 if (image != (Image *) NULL && IfMagickTrue(image->debug)) 2275 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2276 else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug)) 2277 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images"); 2278 2279 *value='\0'; /* formatted string */ 2280 string=(char *) NULL; /* constant string reference */ 2281 2282 /* Get properities that are directly defined by images */ 2283 switch (letter) 2284 { 2285 case 'b': /* image size read in - in bytes */ 2286 { 2287 WarnNoImageReturn("\"%%%c\"",letter); 2288 (void) FormatMagickSize(image->extent,MagickFalse,"B",value); 2289 break; 2290 } 2291 case 'c': /* image comment property - empty string by default */ 2292 { 2293 WarnNoImageReturn("\"%%%c\"",letter); 2294 string=GetImageProperty(image,"comment",exception); 2295 if ( string == (const char *) NULL ) 2296 string=""; 2297 break; 2298 } 2299 case 'd': /* Directory component of filename */ 2300 { 2301 WarnNoImageReturn("\"%%%c\"",letter); 2302 GetPathComponent(image->magick_filename,HeadPath,value); 2303 if (*value == '\0') string=""; 2304 break; 2305 } 2306 case 'e': /* Filename extension (suffix) of image file */ 2307 { 2308 WarnNoImageReturn("\"%%%c\"",letter); 2309 GetPathComponent(image->magick_filename,ExtensionPath,value); 2310 if (*value == '\0') string=""; 2311 break; 2312 } 2313 case 'f': /* Filename without directory component */ 2314 { 2315 WarnNoImageReturn("\"%%%c\"",letter); 2316 GetPathComponent(image->magick_filename,TailPath,value); 2317 if (*value == '\0') string=""; 2318 break; 2319 } 2320 case 'g': /* Image geometry, canvas and offset %Wx%H+%X+%Y */ 2321 { 2322 WarnNoImageReturn("\"%%%c\"",letter); 2323 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g", 2324 (double) image->page.width,(double) image->page.height, 2325 (double) image->page.x,(double) image->page.y); 2326 break; 2327 } 2328 case 'h': /* Image height (current) */ 2329 { 2330 WarnNoImageReturn("\"%%%c\"",letter); 2331 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2332 (image->rows != 0 ? image->rows : image->magick_rows)); 2333 break; 2334 } 2335 case 'i': /* Filename last used for an image (read or write) */ 2336 { 2337 WarnNoImageReturn("\"%%%c\"",letter); 2338 string=image->filename; 2339 break; 2340 } 2341 case 'k': /* Number of unique colors */ 2342 { 2343 /* 2344 FUTURE: ensure this does not generate the formatted comment! 2345 */ 2346 WarnNoImageReturn("\"%%%c\"",letter); 2347 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2348 GetNumberColors(image,(FILE *) NULL,exception)); 2349 break; 2350 } 2351 case 'l': /* Image label property - empty string by default */ 2352 { 2353 WarnNoImageReturn("\"%%%c\"",letter); 2354 string=GetImageProperty(image,"label",exception); 2355 if ( string == (const char *) NULL) 2356 string=""; 2357 break; 2358 } 2359 case 'm': /* Image format (file magick) */ 2360 { 2361 WarnNoImageReturn("\"%%%c\"",letter); 2362 string=image->magick; 2363 break; 2364 } 2365 case 'n': /* Number of images in the list. */ 2366 { 2367 if ( image != (Image *) NULL ) 2368 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2369 GetImageListLength(image)); 2370 else 2371 string="0"; /* no images or scenes */ 2372 break; 2373 } 2374 case 'o': /* Output Filename - for delegate use only */ 2375 WarnNoImageInfoReturn("\"%%%c\"",letter); 2376 string=image_info->filename; 2377 break; 2378 case 'p': /* Image index in current image list */ 2379 { 2380 WarnNoImageReturn("\"%%%c\"",letter); 2381 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2382 GetImageIndexInList(image)); 2383 break; 2384 } 2385 case 'q': /* Quantum depth of image in memory */ 2386 { 2387 WarnNoImageReturn("\"%%%c\"",letter); 2388 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2389 MAGICKCORE_QUANTUM_DEPTH); 2390 break; 2391 } 2392 case 'r': /* Image storage class, colorspace, and alpha enabled. */ 2393 { 2394 ColorspaceType 2395 colorspace; 2396 2397 WarnNoImageReturn("\"%%%c\"",letter); 2398 colorspace=image->colorspace; 2399 if (IfMagickTrue(IsImageGray(image,exception))) 2400 colorspace=GRAYColorspace; /* FUTURE: this is IMv6 not IMv7 */ 2401 (void) FormatLocaleString(value,MaxTextExtent,"%s %s %s", 2402 CommandOptionToMnemonic(MagickClassOptions,(ssize_t) image->storage_class), 2403 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) colorspace), 2404 image->alpha_trait != UndefinedPixelTrait ? "Alpha" : ""); 2405 break; 2406 } 2407 case 's': /* Image scene number */ 2408 { 2409#if 0 /* this seems non-sensical -- simplifing */ 2410 if (image_info->number_scenes != 0) 2411 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2412 image_info->scene); 2413 else if (image != (Image *) NULL) 2414 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2415 image->scene); 2416 else 2417 string="0"; 2418#else 2419 WarnNoImageReturn("\"%%%c\"",letter); 2420 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2421 image->scene); 2422#endif 2423 break; 2424 } 2425 case 't': /* Base filename without directory or extention */ 2426 { 2427 WarnNoImageReturn("\"%%%c\"",letter); 2428 GetPathComponent(image->magick_filename,BasePath,value); 2429 if (*value == '\0') string=""; 2430 break; 2431 } 2432 case 'u': /* Unique filename */ 2433 WarnNoImageInfoReturn("\"%%%c\"",letter); 2434 string=image_info->unique; 2435 break; 2436 case 'w': /* Image width (current) */ 2437 { 2438 WarnNoImageReturn("\"%%%c\"",letter); 2439 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2440 (image->columns != 0 ? image->columns : image->magick_columns)); 2441 break; 2442 } 2443 case 'x': /* Image horizontal resolution (with units) */ 2444 { 2445 WarnNoImageReturn("\"%%%c\"",letter); 2446 (void) FormatLocaleString(value,MaxTextExtent,"%.20g", 2447 fabs(image->resolution.x) > MagickEpsilon ? image->resolution.x : 72.0); 2448 break; 2449 } 2450 case 'y': /* Image vertical resolution (with units) */ 2451 { 2452 WarnNoImageReturn("\"%%%c\"",letter); 2453 (void) FormatLocaleString(value,MaxTextExtent,"%.20g", 2454 fabs(image->resolution.y) > MagickEpsilon ? image->resolution.y : 72.0); 2455 break; 2456 } 2457 case 'z': /* Image depth as read in */ 2458 { 2459 WarnNoImageReturn("\"%%%c\"",letter); 2460 (void) FormatLocaleString(value,MaxTextExtent,"%.20g", 2461 (double) image->depth); 2462 break; 2463 } 2464 case 'A': /* Image alpha channel */ 2465 { 2466 WarnNoImageReturn("\"%%%c\"",letter); 2467 string=CommandOptionToMnemonic(MagickBooleanOptions, 2468 (ssize_t) image->alpha_trait); 2469 break; 2470 } 2471 case 'C': /* Image compression method. */ 2472 { 2473 WarnNoImageReturn("\"%%%c\"",letter); 2474 string=CommandOptionToMnemonic(MagickCompressOptions, 2475 (ssize_t) image->compression); 2476 break; 2477 } 2478 case 'D': /* Image dispose method. */ 2479 { 2480 WarnNoImageReturn("\"%%%c\"",letter); 2481 string=CommandOptionToMnemonic(MagickDisposeOptions, 2482 (ssize_t) image->dispose); 2483 break; 2484 } 2485 case 'G': /* Image size as geometry = "%wx%h" */ 2486 { 2487 WarnNoImageReturn("\"%%%c\"",letter); 2488 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g", 2489 (double)image->magick_columns,(double) image->magick_rows); 2490 break; 2491 } 2492 case 'H': /* layer canvas height */ 2493 { 2494 WarnNoImageReturn("\"%%%c\"",letter); 2495 (void) FormatLocaleString(value,MaxTextExtent,"%.20g", 2496 (double) image->page.height); 2497 break; 2498 } 2499 case 'M': /* Magick filename - filename given incl. coder & read mods */ 2500 { 2501 WarnNoImageReturn("\"%%%c\"",letter); 2502 string=image->magick_filename; 2503 break; 2504 } 2505 case 'O': /* layer canvas offset with sign = "+%X+%Y" */ 2506 { 2507 WarnNoImageReturn("\"%%%c\"",letter); 2508 (void) FormatLocaleString(value,MaxTextExtent,"%+ld%+ld",(long) 2509 image->page.x,(long) image->page.y); 2510 break; 2511 } 2512 case 'P': /* layer canvas page size = "%Wx%H" */ 2513 { 2514 WarnNoImageReturn("\"%%%c\"",letter); 2515 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g", 2516 (double) image->page.width,(double) image->page.height); 2517 break; 2518 } 2519 case 'Q': /* image compression quality */ 2520 { 2521 WarnNoImageReturn("\"%%%c\"",letter); 2522 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2523 (image->quality == 0 ? 92 : image->quality)); 2524 break; 2525 } 2526 case 'S': /* Number of scenes in image list. */ 2527 { 2528 WarnNoImageInfoReturn("\"%%%c\"",letter); 2529#if 0 /* What is this number? -- it makes no sense - simplifing */ 2530 if (image_info->number_scenes == 0) 2531 string="2147483647"; 2532 else if ( image != (Image *) NULL ) 2533 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2534 image_info->scene+image_info->number_scenes); 2535 else 2536 string="0"; 2537#else 2538 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2539 (image_info->number_scenes == 0 ? 2147483647 : 2540 image_info->number_scenes)); 2541#endif 2542 break; 2543 } 2544 case 'T': /* image time delay for animations */ 2545 { 2546 WarnNoImageReturn("\"%%%c\"",letter); 2547 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2548 image->delay); 2549 break; 2550 } 2551 case 'U': /* Image resolution units. */ 2552 { 2553 WarnNoImageReturn("\"%%%c\"",letter); 2554 string=CommandOptionToMnemonic(MagickResolutionOptions, 2555 (ssize_t) image->units); 2556 break; 2557 } 2558 case 'W': /* layer canvas width */ 2559 { 2560 WarnNoImageReturn("\"%%%c\"",letter); 2561 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2562 image->page.width); 2563 break; 2564 } 2565 case 'X': /* layer canvas X offset */ 2566 { 2567 WarnNoImageReturn("\"%%%c\"",letter); 2568 (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double) 2569 image->page.x); 2570 break; 2571 } 2572 case 'Y': /* layer canvas Y offset */ 2573 { 2574 WarnNoImageReturn("\"%%%c\"",letter); 2575 (void) FormatLocaleString(value,MaxTextExtent,"%+.20g",(double) 2576 image->page.y); 2577 break; 2578 } 2579 case 'Z': /* Zero filename ??? */ 2580 WarnNoImageInfoReturn("\"%%%c\"",letter); 2581 string=image_info->zero; 2582 break; 2583 case '%': /* percent escaped */ 2584 string="%"; 2585 break; 2586 case '@': /* Trim bounding box, without actually Trimming! */ 2587 { 2588 RectangleInfo 2589 page; 2590 2591 WarnNoImageReturn("\"%%%c\"",letter); 2592 page=GetImageBoundingBox(image,exception); 2593 (void) FormatLocaleString(value,MaxTextExtent,"%.20gx%.20g%+.20g%+.20g", 2594 (double) page.width,(double) page.height,(double) page.x,(double) 2595 page.y); 2596 break; 2597 } 2598 case '#': 2599 { 2600 /* 2601 Image signature. 2602 */ 2603 WarnNoImageReturn("\"%%%c\"",letter); 2604 (void) SignatureImage(image,exception); 2605 string=GetImageProperty(image,"signature",exception); 2606 break; 2607 } 2608 } 2609 if (string != (char *) NULL) 2610 return(string); 2611 if (*value != '\0') 2612 { 2613 /* 2614 Create a cloned copy of result. 2615 */ 2616 if (image != (Image *) NULL) 2617 { 2618 (void) SetImageArtifact(image,"get-property",value); 2619 return(GetImageArtifact(image,"get-property")); 2620 } 2621 else 2622 { 2623 (void) SetImageOption(image_info,"get-property",value); 2624 return(GetImageOption(image_info,"get-property")); 2625 } 2626 } 2627 return((char *) NULL); 2628} 2629 2630MagickExport const char *GetMagickProperty(ImageInfo *image_info, 2631 Image *image,const char *property,ExceptionInfo *exception) 2632{ 2633 char 2634 value[MaxTextExtent]; 2635 2636 const char 2637 *string; 2638 2639 assert(property[0] != '\0'); 2640 assert(image != (Image *) NULL || image_info != (ImageInfo *) NULL ); 2641 2642 if (property[1] == '\0') /* single letter property request */ 2643 return(GetMagickPropertyLetter(image_info,image,*property,exception)); 2644 2645 if (image != (Image *) NULL && IfMagickTrue(image->debug)) 2646 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2647 else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug)) 2648 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-images"); 2649 2650 *value='\0'; /* formated string */ 2651 string=(char *) NULL; /* constant string reference */ 2652 switch (*property) 2653 { 2654 case 'b': 2655 { 2656 if (LocaleCompare("basename",property) == 0) 2657 { 2658 WarnNoImageReturn("\"%%[%s]\"",property); 2659 GetPathComponent(image->magick_filename,BasePath,value); 2660 if (*value == '\0') string=""; 2661 break; 2662 } 2663 if (LocaleCompare("bit-depth",property) == 0) 2664 { 2665 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2666 GetImageDepth(image, exception)); 2667 break; 2668 } 2669 break; 2670 } 2671 case 'c': 2672 { 2673 if (LocaleCompare("channels",property) == 0) 2674 { 2675 WarnNoImageReturn("\"%%[%s]\"",property); 2676 /* FUTURE: return actual image channels */ 2677 (void) FormatLocaleString(value,MaxTextExtent,"%s", 2678 CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) 2679 image->colorspace)); 2680 LocaleLower(value); 2681 if( image->alpha_trait != UndefinedPixelTrait ) 2682 (void) ConcatenateMagickString(value,"a",MaxTextExtent); 2683 break; 2684 } 2685 if (LocaleCompare("colorspace",property) == 0) 2686 { 2687 WarnNoImageReturn("\"%%[%s]\"",property); 2688 /* FUTURE: return actual colorspace - no 'gray' stuff */ 2689 string=CommandOptionToMnemonic(MagickColorspaceOptions,(ssize_t) 2690 image->colorspace); 2691 break; 2692 } 2693 if (LocaleCompare("copyright",property) == 0) 2694 { 2695 (void) CopyMagickString(value,GetMagickCopyright(),MaxTextExtent); 2696 break; 2697 } 2698 break; 2699 } 2700 case 'd': 2701 { 2702 if (LocaleCompare("depth",property) == 0) 2703 { 2704 WarnNoImageReturn("\"%%[%s]\"",property); 2705 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2706 image->depth); 2707 break; 2708 } 2709 if (LocaleCompare("directory",property) == 0) 2710 { 2711 WarnNoImageReturn("\"%%[%s]\"",property); 2712 GetPathComponent(image->magick_filename,HeadPath,value); 2713 if (*value == '\0') string=""; 2714 break; 2715 } 2716 break; 2717 } 2718 case 'e': 2719 { 2720 if (LocaleCompare("entropy",property) == 0) 2721 { 2722 double 2723 entropy; 2724 2725 WarnNoImageReturn("\"%%[%s]\"",property); 2726 (void) GetImageEntropy(image,&entropy,exception); 2727 (void) FormatLocaleString(value,MaxTextExtent,"%.*g", 2728 GetMagickPrecision(),entropy); 2729 break; 2730 } 2731 if (LocaleCompare("extension",property) == 0) 2732 { 2733 WarnNoImageReturn("\"%%[%s]\"",property); 2734 GetPathComponent(image->magick_filename,ExtensionPath,value); 2735 if (*value == '\0') string=""; 2736 break; 2737 } 2738 break; 2739 } 2740 case 'g': 2741 { 2742 if (LocaleCompare("gamma",property) == 0) 2743 { 2744 WarnNoImageReturn("\"%%[%s]\"",property); 2745 (void) FormatLocaleString(value,MaxTextExtent,"%.*g", 2746 GetMagickPrecision(),image->gamma); 2747 break; 2748 } 2749 if (LocaleCompare("group",property) == 0) 2750 { 2751 WarnNoImageInfoReturn("\"%%[%s]\"",property); 2752 (void) FormatLocaleString(value,MaxTextExtent,"0x%lx",(unsigned long) 2753 image_info->group); 2754 break; 2755 } 2756 break; 2757 } 2758 case 'h': 2759 { 2760 if (LocaleCompare("height",property) == 0) 2761 { 2762 WarnNoImageReturn("\"%%[%s]\"",property); 2763 (void) FormatLocaleString(value,MaxTextExtent,"%.20g", 2764 image->magick_rows != 0 ? (double) image->magick_rows : 256.0); 2765 break; 2766 } 2767 break; 2768 } 2769 case 'i': 2770 { 2771 if (LocaleCompare("input",property) == 0) 2772 { 2773 WarnNoImageReturn("\"%%[%s]\"",property); 2774 string=image->filename; 2775 break; 2776 } 2777 break; 2778 } 2779 case 'k': 2780 { 2781 if (LocaleCompare("kurtosis",property) == 0) 2782 { 2783 double 2784 kurtosis, 2785 skewness; 2786 2787 WarnNoImageReturn("\"%%[%s]\"",property); 2788 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception); 2789 (void) FormatLocaleString(value,MaxTextExtent,"%.*g", 2790 GetMagickPrecision(),kurtosis); 2791 break; 2792 } 2793 break; 2794 } 2795 case 'm': 2796 { 2797 if (LocaleCompare("magick",property) == 0) 2798 { 2799 WarnNoImageReturn("\"%%[%s]\"",property); 2800 string=image->magick; 2801 break; 2802 } 2803 if (LocaleCompare("maxima",property) == 0) 2804 { 2805 double 2806 maximum, 2807 minimum; 2808 2809 WarnNoImageReturn("\"%%[%s]\"",property); 2810 (void) GetImageRange(image,&minimum,&maximum,exception); 2811 (void) FormatLocaleString(value,MaxTextExtent,"%.*g", 2812 GetMagickPrecision(),maximum); 2813 break; 2814 } 2815 if (LocaleCompare("mean",property) == 0) 2816 { 2817 double 2818 mean, 2819 standard_deviation; 2820 2821 WarnNoImageReturn("\"%%[%s]\"",property); 2822 (void) GetImageMean(image,&mean,&standard_deviation,exception); 2823 (void) FormatLocaleString(value,MaxTextExtent,"%.*g", 2824 GetMagickPrecision(),mean); 2825 break; 2826 } 2827 if (LocaleCompare("minima",property) == 0) 2828 { 2829 double 2830 maximum, 2831 minimum; 2832 2833 WarnNoImageReturn("\"%%[%s]\"",property); 2834 (void) GetImageRange(image,&minimum,&maximum,exception); 2835 (void) FormatLocaleString(value,MaxTextExtent,"%.*g", 2836 GetMagickPrecision(),minimum); 2837 break; 2838 } 2839 break; 2840 } 2841 case 'o': 2842 { 2843 if (LocaleCompare("opaque",property) == 0) 2844 { 2845 WarnNoImageReturn("\"%%[%s]\"",property); 2846 string=CommandOptionToMnemonic(MagickBooleanOptions,(ssize_t) 2847 IsImageOpaque(image,exception)); 2848 break; 2849 } 2850 if (LocaleCompare("orientation",property) == 0) 2851 { 2852 WarnNoImageReturn("\"%%[%s]\"",property); 2853 string=CommandOptionToMnemonic(MagickOrientationOptions,(ssize_t) 2854 image->orientation); 2855 break; 2856 } 2857 if (LocaleCompare("output",property) == 0) 2858 { 2859 WarnNoImageInfoReturn("\"%%[%s]\"",property); 2860 (void) CopyMagickString(value,image_info->filename,MaxTextExtent); 2861 break; 2862 } 2863 break; 2864 } 2865 case 'p': 2866 { 2867#if defined(MAGICKCORE_LCMS_DELEGATE) 2868 if (LocaleCompare("profile:icc",property) == 0 || 2869 LocaleCompare("profile:icm",property) == 0) 2870 { 2871#if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000) 2872#define cmsUInt32Number DWORD 2873#endif 2874 2875 const StringInfo 2876 *profile; 2877 2878 cmsHPROFILE 2879 icc_profile; 2880 2881 profile=GetImageProfile(image,property+8); 2882 if (profile == (StringInfo *) NULL) 2883 break; 2884 icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile), 2885 (cmsUInt32Number) GetStringInfoLength(profile)); 2886 if (icc_profile != (cmsHPROFILE *) NULL) 2887 { 2888#if defined(LCMS_VERSION) && (LCMS_VERSION < 2000) 2889 string=cmsTakeProductName(icc_profile); 2890#else 2891 (void) cmsGetProfileInfoASCII(icc_profile,cmsInfoDescription, 2892 "en","US",value,MaxTextExtent); 2893#endif 2894 (void) cmsCloseProfile(icc_profile); 2895 } 2896 } 2897#endif 2898 if (LocaleCompare("profiles",property) == 0) 2899 { 2900 const char 2901 *name; 2902 2903 ResetImageProfileIterator(image); 2904 name=GetNextImageProfile(image); 2905 if (name != (char *) NULL) 2906 { 2907 (void) CopyMagickString(value,name,MaxTextExtent); 2908 name=GetNextImageProfile(image); 2909 while (name != (char *) NULL) 2910 { 2911 ConcatenateMagickString(value,",",MaxTextExtent); 2912 ConcatenateMagickString(value,name,MaxTextExtent); 2913 name=GetNextImageProfile(image); 2914 } 2915 } 2916 break; 2917 } 2918 break; 2919 } 2920 case 'r': 2921 { 2922 if (LocaleCompare("resolution.x",property) == 0) 2923 { 2924 WarnNoImageReturn("\"%%[%s]\"",property); 2925 (void) FormatLocaleString(value,MaxTextExtent,"%g", 2926 image->resolution.x); 2927 break; 2928 } 2929 if (LocaleCompare("resolution.y",property) == 0) 2930 { 2931 WarnNoImageReturn("\"%%[%s]\"",property); 2932 (void) FormatLocaleString(value,MaxTextExtent,"%g", 2933 image->resolution.y); 2934 break; 2935 } 2936 break; 2937 } 2938 case 's': 2939 { 2940 if (LocaleCompare("scene",property) == 0) 2941 { 2942 WarnNoImageInfoReturn("\"%%[%s]\"",property); 2943 if (image_info->number_scenes != 0) 2944 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2945 image_info->scene); 2946 else { 2947 WarnNoImageReturn("\"%%[%s]\"",property); 2948 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2949 image->scene); 2950 } 2951 break; 2952 } 2953 if (LocaleCompare("scenes",property) == 0) 2954 { 2955 /* FUTURE: equivelent to %n? */ 2956 WarnNoImageReturn("\"%%[%s]\"",property); 2957 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 2958 GetImageListLength(image)); 2959 break; 2960 } 2961 if (LocaleCompare("size",property) == 0) 2962 { 2963 WarnNoImageReturn("\"%%[%s]\"",property); 2964 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B",value); 2965 break; 2966 } 2967 if (LocaleCompare("skewness",property) == 0) 2968 { 2969 double 2970 kurtosis, 2971 skewness; 2972 2973 WarnNoImageReturn("\"%%[%s]\"",property); 2974 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception); 2975 (void) FormatLocaleString(value,MaxTextExtent,"%.*g", 2976 GetMagickPrecision(),skewness); 2977 break; 2978 } 2979 if (LocaleCompare("standard-deviation",property) == 0) 2980 { 2981 double 2982 mean, 2983 standard_deviation; 2984 2985 WarnNoImageReturn("\"%%[%s]\"",property); 2986 (void) GetImageMean(image,&mean,&standard_deviation,exception); 2987 (void) FormatLocaleString(value,MaxTextExtent,"%.*g", 2988 GetMagickPrecision(),standard_deviation); 2989 break; 2990 } 2991 break; 2992 } 2993 case 't': 2994 { 2995 if (LocaleCompare("type",property) == 0) 2996 { 2997 WarnNoImageReturn("\"%%[%s]\"",property); 2998 string=CommandOptionToMnemonic(MagickTypeOptions,(ssize_t) 2999 GetImageType(image,exception)); 3000 break; 3001 } 3002 break; 3003 } 3004 case 'u': 3005 { 3006 if (LocaleCompare("unique",property) == 0) 3007 { 3008 WarnNoImageInfoReturn("\"%%[%s]\"",property); 3009 string=image_info->unique; 3010 break; 3011 } 3012 if (LocaleCompare("units",property) == 0) 3013 { 3014 WarnNoImageReturn("\"%%[%s]\"",property); 3015 string=CommandOptionToMnemonic(MagickResolutionOptions,(ssize_t) 3016 image->units); 3017 break; 3018 } 3019 if (LocaleCompare("copyright",property) == 0) 3020 break; 3021 } 3022 case 'v': 3023 { 3024 if (LocaleCompare("version",property) == 0) 3025 { 3026 string=GetMagickVersion((size_t *) NULL); 3027 break; 3028 } 3029 break; 3030 } 3031 case 'w': 3032 { 3033 if (LocaleCompare("width",property) == 0) 3034 { 3035 WarnNoImageReturn("\"%%[%s]\"",property); 3036 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 3037 (image->magick_columns != 0 ? image->magick_columns : 256)); 3038 break; 3039 } 3040 break; 3041 } 3042 case 'z': 3043 { 3044 if (LocaleCompare("zero",property) == 0) 3045 { 3046 WarnNoImageInfoReturn("\"%%[%s]\"",property); 3047 string=image_info->zero; 3048 break; 3049 } 3050 break; 3051 } 3052 } 3053 if (string != (char *) NULL) 3054 return(string); 3055 if (*value != '\0') 3056 { 3057 /* create a cloned copy of result, that will get cleaned up, eventually */ 3058 if (image != (Image *) NULL) 3059 { 3060 (void) SetImageArtifact(image,"get-property",value); 3061 return(GetImageArtifact(image,"get-property")); 3062 } 3063 else 3064 { 3065 (void) SetImageOption(image_info,"get-property",value); 3066 return(GetImageOption(image_info,"get-property")); 3067 } 3068 } 3069 return((char *) NULL); 3070} 3071#undef WarnNoImageReturn 3072 3073/* 3074%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3075% % 3076% % 3077% % 3078% G e t N e x t I m a g e P r o p e r t y % 3079% % 3080% % 3081% % 3082%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3083% 3084% GetNextImageProperty() gets the next free-form string property name. 3085% 3086% The format of the GetNextImageProperty method is: 3087% 3088% char *GetNextImageProperty(const Image *image) 3089% 3090% A description of each parameter follows: 3091% 3092% o image: the image. 3093% 3094*/ 3095MagickExport char *GetNextImageProperty(const Image *image) 3096{ 3097 assert(image != (Image *) NULL); 3098 assert(image->signature == MagickSignature); 3099 if (image->debug != MagickFalse) 3100 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 3101 image->filename); 3102 if (image->properties == (void *) NULL) 3103 return((char *) NULL); 3104 return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties)); 3105} 3106 3107/* 3108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3109% % 3110% % 3111% % 3112% I n t e r p r e t I m a g e P r o p e r t i e s % 3113% % 3114% % 3115% % 3116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3117% 3118% InterpretImageProperties() replaces any embedded formatting characters with 3119% the appropriate image property and returns the interpreted text. 3120% 3121% This searches for and replaces 3122% \n \r \% replaced by newline, return, and percent resp. 3123% < > & replaced by '<', '>', '&' resp. 3124% %% replaced by percent 3125% 3126% %x %[x] where 'x' is a single letter properity, case sensitive). 3127% %[type:name] where 'type' a is special and known prefix. 3128% %[name] where 'name' is a specifically known attribute, calculated 3129% value, or a per-image property string name, or a per-image 3130% 'artifact' (as generated from a global option). 3131% It may contain ':' as long as the prefix is not special. 3132% 3133% Single letter % substitutions will only happen if the character before the 3134% percent is NOT a number. But braced substitutions will always be performed. 3135% This prevents the typical usage of percent in a interpreted geometry 3136% argument from being substituted when the percent is a geometry flag. 3137% 3138% If 'glob-expresions' ('*' or '?' characters) is used for 'name' it may be 3139% used as a search pattern to print multiple lines of "name=value\n" pairs of 3140% the associacted set of properties. 3141% 3142% The returned string must be freed using DestoryString() by the caller. 3143% 3144% The format of the InterpretImageProperties method is: 3145% 3146% char *InterpretImageProperties(ImageInfo *image_info, 3147% Image *image,const char *embed_text,ExceptionInfo *exception) 3148% 3149% A description of each parameter follows: 3150% 3151% o image_info: the image info. (required) 3152% 3153% o image: the image. (optional) 3154% 3155% o embed_text: the address of a character string containing the embedded 3156% formatting characters. 3157% 3158% o exception: return any errors or warnings in this structure. 3159% 3160*/ 3161 3162/* common inline code to expand the interpreted text string */ 3163#define ExtendInterpretText(string_length) do { \ 3164DisableMSCWarning(4127) \ 3165 size_t length=(string_length); \ 3166 if ((size_t) (q-interpret_text+length+1) >= extent) \ 3167 { extent+=length; \ 3168 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \ 3169 extent+MaxTextExtent,sizeof(*interpret_text)); \ 3170 if (interpret_text == (char *) NULL) \ 3171 return((char *) NULL); \ 3172 q=interpret_text+strlen(interpret_text); \ 3173 } } while (0) /* no trailing ; */ \ 3174RestoreMSCWarning 3175 3176/* same but append the given string */ 3177#define AppendString2Text(string) do { \ 3178DisableMSCWarning(4127) \ 3179 size_t length=strlen((string)); \ 3180 if ((size_t) (q-interpret_text+length+1) >= extent) \ 3181 { extent+=length; \ 3182 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \ 3183 extent+MaxTextExtent,sizeof(*interpret_text)); \ 3184 if (interpret_text == (char *) NULL) \ 3185 return((char *) NULL); \ 3186 q=interpret_text+strlen(interpret_text); \ 3187 } \ 3188 (void) CopyMagickString(q,(string),extent); \ 3189 q+=length; \ 3190 } while (0) /* no trailing ; */ \ 3191RestoreMSCWarning 3192 3193/* same but append a 'key' and 'string' pair */ 3194#define AppendKeyValue2Text(key,string) do { \ 3195DisableMSCWarning(4127) \ 3196 size_t length=strlen(key)+strlen(string)+2; \ 3197 if ((size_t) (q-interpret_text+length+1) >= extent) \ 3198 { extent+=length; \ 3199 interpret_text=(char *) ResizeQuantumMemory(interpret_text, \ 3200 extent+MaxTextExtent,sizeof(*interpret_text)); \ 3201 if (interpret_text == (char *) NULL) \ 3202 return((char *) NULL); \ 3203 q=interpret_text+strlen(interpret_text); \ 3204 } \ 3205 q+=FormatLocaleString(q,extent,"%s=%s\n",(key),(string)); \ 3206 } while (0) /* no trailing ; */ \ 3207RestoreMSCWarning 3208 3209MagickExport char *InterpretImageProperties(ImageInfo *image_info, 3210 Image *image,const char *embed_text,ExceptionInfo *exception) 3211{ 3212 char 3213 *interpret_text; 3214 3215 register char 3216 *q; /* current position in interpret_text */ 3217 3218 register const char 3219 *p; /* position in embed_text string being expanded */ 3220 3221 size_t 3222 extent; /* allocated length of interpret_text */ 3223 3224 MagickBooleanType 3225 number; 3226 3227 assert(image == NULL || image->signature == MagickSignature); 3228 assert(image_info == NULL || image_info->signature == MagickSignature); 3229 3230 if (image != (Image *) NULL && IfMagickTrue(image->debug)) 3231 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 3232 else if( image_info != (ImageInfo *) NULL && IfMagickTrue(image_info->debug)) 3233 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s","no-image"); 3234 3235 if (embed_text == (const char *) NULL) 3236 return((char *) NULL); 3237 p=embed_text; 3238 3239 if (*p == '\0') 3240 return(ConstantString("")); 3241 3242 /* handle a '@' replace string from file */ 3243 if (*p == '@') { 3244 p++; 3245 if (*p != '-' && IfMagickFalse(IsPathAccessible(p)) ) { 3246 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 3247 "UnableToAccessPath","%s",p); 3248 return((char *) NULL); 3249 } 3250 return(FileToString(p,~0UL,exception)); 3251 } 3252 3253 /* 3254 Translate any embedded format characters. 3255 */ 3256 interpret_text=AcquireString(embed_text); /* new string with extra space */ 3257 extent=MaxTextExtent; /* allocated space in string */ 3258 number=MagickFalse; /* is last char a number? */ 3259 for (q=interpret_text; *p!='\0'; number=IsMagickTrue(isdigit(*p)),p++) 3260 { 3261 *q='\0'; 3262 ExtendInterpretText(MaxTextExtent); 3263 /* 3264 Look for the various escapes, (and handle other specials) 3265 */ 3266 switch (*p) { 3267 case '\\': 3268 switch (*(p+1)) { 3269 case '\0': 3270 continue; 3271 case 'r': /* convert to RETURN */ 3272 *q++='\r'; 3273 p++; 3274 continue; 3275 case 'n': /* convert to NEWLINE */ 3276 *q++='\n'; 3277 p++; 3278 continue; 3279 case '\n': /* EOL removal UNIX,MacOSX */ 3280 p++; 3281 continue; 3282 case '\r': /* EOL removal DOS,Windows */ 3283 p++; 3284 if (*p == '\n') /* return-newline EOL */ 3285 p++; 3286 continue; 3287 default: 3288 p++; 3289 *q++=(*p); 3290 } 3291 continue; 3292 case '&': 3293 if (LocaleNCompare("<",p,4) == 0) 3294 *q++='<', p+=3; 3295 else if (LocaleNCompare(">",p,4) == 0) 3296 *q++='>', p+=3; 3297 else if (LocaleNCompare("&",p,5) == 0) 3298 *q++='&', p+=4; 3299 else 3300 *q++=(*p); 3301 continue; 3302 case '%': 3303 break; /* continue to next set of handlers */ 3304 default: 3305 *q++=(*p); /* any thing else is 'as normal' */ 3306 continue; 3307 } 3308 p++; /* advance beyond the percent */ 3309 3310 /* 3311 Doubled Percent - or percent at end of string 3312 */ 3313 if ((*p == '\0') || (*p == '\'') || (*p == '"')) 3314 p--; 3315 if (*p == '%') { 3316 *q++='%'; 3317 continue; 3318 } 3319 /* 3320 Single letter escapes %c 3321 */ 3322 if ( *p != '[' ) { 3323 const char 3324 *string; 3325 3326 /* But only if not preceeded by a number! */ 3327 if ( IfMagickTrue(number) ) { 3328 *q++='%'; /* do NOT substitute the percent */ 3329 p--; /* back up one */ 3330 continue; 3331 } 3332 string=GetMagickPropertyLetter(image_info,image,*p, exception); 3333 if (string != (char *) NULL) 3334 { 3335 AppendString2Text(string); 3336 if (image != (Image *) NULL) 3337 (void)DeleteImageArtifact(image,"get-property"); 3338 if (image_info != (ImageInfo *) NULL) 3339 (void)DeleteImageOption(image_info,"get-property"); 3340 continue; 3341 } 3342 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, 3343 "UnknownImageProperty","\"%%%c\"",*p); 3344 continue; 3345 } 3346 3347 /* 3348 Braced Percent Escape %[...] 3349 */ 3350 { 3351 char 3352 pattern[2*MaxTextExtent]; 3353 3354 const char 3355 *key, 3356 *string; 3357 3358 register ssize_t 3359 len; 3360 3361 ssize_t 3362 depth; 3363 3364 /* get the property name framed by the %[...] */ 3365 p++; /* advance p to just inside the opening brace */ 3366 depth=1; 3367 if ( *p == ']' ) { 3368 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, 3369 "UnknownImageProperty","\"%%[]\""); 3370 break; 3371 } 3372 for (len=0; len<(MaxTextExtent-1L) && (*p != '\0');) 3373 { 3374 /* skip escaped braces within braced pattern */ 3375 if ( (*p == '\\') && (*(p+1) != '\0') ) { 3376 pattern[len++]=(*p++); 3377 pattern[len++]=(*p++); 3378 continue; 3379 } 3380 if (*p == '[') 3381 depth++; 3382 if (*p == ']') 3383 depth--; 3384 if (depth <= 0) 3385 break; 3386 pattern[len++]=(*p++); 3387 } 3388 pattern[len]='\0'; 3389 /* Check for unmatched final ']' for "%[...]" */ 3390 if ( depth != 0 ) { 3391 if (len >= 64) { /* truncate string for error message */ 3392 pattern[61] = '.'; 3393 pattern[62] = '.'; 3394 pattern[63] = '.'; 3395 pattern[64] = '\0'; 3396 } 3397 (void) ThrowMagickException(exception,GetMagickModule(), 3398 OptionError,"UnbalancedBraces","\"%%[%s\"",pattern); 3399 interpret_text=DestroyString(interpret_text); 3400 return((char *) NULL); 3401 } 3402 3403 /* 3404 Special Lookup Prefixes %[prefix:...] 3405 */ 3406 /* fx - value calculator */ 3407 if (LocaleNCompare("fx:",pattern,3) == 0) 3408 { 3409 FxInfo 3410 *fx_info; 3411 3412 double 3413 value; 3414 3415 MagickBooleanType 3416 status; 3417 3418 if (image == (Image *) NULL ) { 3419 (void) ThrowMagickException(exception,GetMagickModule(), 3420 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern); 3421 continue; /* else no image to retrieve artifact */ 3422 } 3423 fx_info=AcquireFxInfo(image,pattern+3,exception); 3424 status=FxEvaluateChannelExpression(fx_info,IntensityPixelChannel,0,0, 3425 &value,exception); 3426 fx_info=DestroyFxInfo(fx_info); 3427 if( IfMagickTrue(status) ) 3428 { 3429 char 3430 result[MaxTextExtent]; 3431 3432 (void) FormatLocaleString(result,MaxTextExtent,"%.*g", 3433 GetMagickPrecision(),(double) value); 3434 AppendString2Text(result); 3435 } 3436 continue; 3437 } 3438 /* pixel - color value calculator */ 3439 if (LocaleNCompare("pixel:",pattern,6) == 0) 3440 { 3441 FxInfo 3442 *fx_info; 3443 3444 double 3445 value; 3446 3447 MagickStatusType 3448 status; 3449 3450 PixelInfo 3451 pixel; 3452 3453 if (image == (Image *) NULL ) { 3454 (void) ThrowMagickException(exception,GetMagickModule(), 3455 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern); 3456 continue; /* else no image to retrieve artifact */ 3457 } 3458 GetPixelInfo(image,&pixel); 3459 fx_info=AcquireFxInfo(image,pattern+6,exception); 3460 status=FxEvaluateChannelExpression(fx_info,RedPixelChannel,0,0, 3461 &value,exception); 3462 pixel.red=(double) QuantumRange*value; 3463 status&=FxEvaluateChannelExpression(fx_info,GreenPixelChannel,0,0, 3464 &value,exception); 3465 pixel.green=(double) QuantumRange*value; 3466 status&=FxEvaluateChannelExpression(fx_info,BluePixelChannel,0,0, 3467 &value,exception); 3468 pixel.blue=(double) QuantumRange*value; 3469 if (image->colorspace == CMYKColorspace) 3470 { 3471 status&=FxEvaluateChannelExpression(fx_info,BlackPixelChannel,0,0, 3472 &value,exception); 3473 pixel.black=(double) QuantumRange*value; 3474 } 3475 status&=FxEvaluateChannelExpression(fx_info,AlphaPixelChannel,0,0, 3476 &value,exception); 3477 pixel.alpha=(double) QuantumRange*value; 3478 fx_info=DestroyFxInfo(fx_info); 3479 if( IfMagickTrue(status) ) 3480 { 3481 char 3482 name[MaxTextExtent]; 3483 3484 (void) QueryColorname(image,&pixel,SVGCompliance,name, 3485 exception); 3486 AppendString2Text(name); 3487 } 3488 continue; 3489 } 3490 /* option - direct global option lookup (with globbing) */ 3491 if (LocaleNCompare("option:",pattern,7) == 0) 3492 { 3493 if (image_info == (ImageInfo *) NULL ) { 3494 (void) ThrowMagickException(exception,GetMagickModule(), 3495 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern); 3496 continue; /* else no image to retrieve artifact */ 3497 } 3498 if( IfMagickTrue(IsGlob(pattern+7)) ) 3499 { 3500 ResetImageOptionIterator(image_info); 3501 while ((key=GetNextImageOption(image_info)) != (const char *) NULL) 3502 if( IfMagickTrue(GlobExpression(key,pattern+7,MagickTrue)) ) 3503 { 3504 string=GetImageOption(image_info,key); 3505 if (string != (const char *) NULL) 3506 AppendKeyValue2Text(key,string); 3507 /* else - assertion failure? key found but no string value! */ 3508 } 3509 continue; 3510 } 3511 string=GetImageOption(image_info,pattern+7); 3512 if (string == (char *) NULL) 3513 goto PropertyLookupFailure; /* no artifact of this specifc name */ 3514 AppendString2Text(string); 3515 continue; 3516 } 3517 /* artifact - direct image artifact lookup (with glob) */ 3518 if (LocaleNCompare("artifact:",pattern,9) == 0) 3519 { 3520 if (image == (Image *) NULL ) { 3521 (void) ThrowMagickException(exception,GetMagickModule(), 3522 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern); 3523 continue; /* else no image to retrieve artifact */ 3524 } 3525 if( IfMagickTrue(IsGlob(pattern+9)) ) 3526 { 3527 ResetImageArtifactIterator(image); 3528 while ((key=GetNextImageArtifact(image)) != (const char *) NULL) 3529 if( IfMagickTrue(GlobExpression(key,pattern+9,MagickTrue)) ) 3530 { 3531 string=GetImageArtifact(image,key); 3532 if (string != (const char *) NULL) 3533 AppendKeyValue2Text(key,string); 3534 /* else - assertion failure? key found but no string value! */ 3535 } 3536 continue; 3537 } 3538 string=GetImageArtifact(image,pattern+9); 3539 if (string == (char *) NULL) 3540 goto PropertyLookupFailure; /* no artifact of this specifc name */ 3541 AppendString2Text(string); 3542 continue; 3543 } 3544 /* property - direct image property lookup (with glob) */ 3545 if (LocaleNCompare("property:",pattern,9) == 0) 3546 { 3547 if (image == (Image *) NULL ) { 3548 (void) ThrowMagickException(exception,GetMagickModule(), 3549 OptionWarning,"NoImageForProperty","\"%%[%s]\"",pattern); 3550 continue; /* else no image to retrieve artifact */ 3551 } 3552 if( IfMagickTrue(IsGlob(pattern+9)) ) 3553 { 3554 ResetImagePropertyIterator(image); 3555 while ((key=GetNextImageProperty(image)) != (const char *) NULL) 3556 if( IfMagickTrue(GlobExpression(key,pattern,MagickTrue)) ) 3557 { 3558 string=GetImageProperty(image,key,exception); 3559 if (string != (const char *) NULL) 3560 AppendKeyValue2Text(key,string); 3561 /* else - assertion failure? */ 3562 } 3563 continue; 3564 } 3565 string=GetImageProperty(image,pattern+9,exception); 3566 if (string == (char *) NULL) 3567 goto PropertyLookupFailure; /* no artifact of this specifc name */ 3568 AppendString2Text(string); 3569 continue; 3570 } 3571 /* Properties without special prefix. 3572 This handles attributes, properties, and profiles such as %[exif:...] 3573 Note the profile properties may also include a glob expansion pattern. 3574 */ 3575 if ( image != (Image *) NULL ) 3576 { 3577 string=GetImageProperty(image,pattern,exception); 3578 if (string != (const char *) NULL) 3579 { 3580 AppendString2Text(string); 3581 if (image != (Image *) NULL) 3582 (void)DeleteImageArtifact(image,"get-property"); 3583 if (image_info != (ImageInfo *) NULL) 3584 (void)DeleteImageOption(image_info,"get-property"); 3585 continue; 3586 } 3587 } 3588 /* 3589 Handle property 'glob' patterns 3590 Such as: %[*] %[user:array_??] %[filename:e*] 3591 */ 3592 if( IfMagickTrue(IsGlob(pattern)) ) 3593 { 3594 if (image == (Image *) NULL) 3595 continue; /* else no image to retrieve proprty - no list */ 3596 ResetImagePropertyIterator(image); 3597 while ((key=GetNextImageProperty(image)) != (const char *) NULL) 3598 if( IfMagickTrue(GlobExpression(key,pattern,MagickTrue)) ) 3599 { 3600 string=GetImageProperty(image,key,exception); 3601 if (string != (const char *) NULL) 3602 AppendKeyValue2Text(key,string); 3603 /* else - assertion failure? */ 3604 } 3605 continue; 3606 } 3607 /* 3608 Look for a known property or image attribute 3609 Such as %[basename] %[denisty] %[delay] 3610 Also handles a braced single letter: %[b] %[G] %[g] 3611 */ 3612 string=GetMagickProperty(image_info,image,pattern,exception); 3613 if (string != (const char *) NULL) 3614 { 3615 AppendString2Text(string); 3616 continue; 3617 } 3618 /* 3619 Look for a per-image Artifact 3620 This includes option lookup (FUTURE: interpreted according to image) 3621 */ 3622 if (image != (Image *) NULL) 3623 { 3624 string=GetImageArtifact(image,pattern); 3625 if (string != (char *) NULL) 3626 { 3627 AppendString2Text(string); 3628 continue; 3629 } 3630 } 3631 else 3632 /* no image, so direct 'option' lookup (no delayed percent escapes) */ 3633 if (image_info != (ImageInfo *) NULL) 3634 { 3635 string=GetImageOption(image_info,pattern); 3636 if (string != (char *) NULL) 3637 { 3638 AppendString2Text(string); 3639 continue; 3640 } 3641 } 3642PropertyLookupFailure: 3643 /* 3644 Failed to find any match anywhere! 3645 */ 3646 if (len >= 64) { /* truncate string for error message */ 3647 pattern[61] = '.'; 3648 pattern[62] = '.'; 3649 pattern[63] = '.'; 3650 pattern[64] = '\0'; 3651 } 3652 (void) ThrowMagickException(exception,GetMagickModule(), 3653 OptionWarning,"UnknownImageProperty","\"%%[%s]\"",pattern); 3654 /* continue */ 3655 } /* Braced Percent Escape */ 3656 3657 } /* for each char in 'embed_text' */ 3658 *q='\0'; 3659 return(interpret_text); 3660} 3661 3662/* 3663%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3664% % 3665% % 3666% % 3667% R e m o v e I m a g e P r o p e r t y % 3668% % 3669% % 3670% % 3671%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3672% 3673% RemoveImageProperty() removes a property from the image and returns its 3674% value. 3675% 3676% In this case the ConstantString() value returned should be freed by the 3677% caller when finished. 3678% 3679% The format of the RemoveImageProperty method is: 3680% 3681% char *RemoveImageProperty(Image *image,const char *property) 3682% 3683% A description of each parameter follows: 3684% 3685% o image: the image. 3686% 3687% o property: the image property. 3688% 3689*/ 3690MagickExport char *RemoveImageProperty(Image *image, 3691 const char *property) 3692{ 3693 char 3694 *value; 3695 3696 assert(image != (Image *) NULL); 3697 assert(image->signature == MagickSignature); 3698 if (image->debug != MagickFalse) 3699 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 3700 image->filename); 3701 if (image->properties == (void *) NULL) 3702 return((char *) NULL); 3703 value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties, 3704 property); 3705 return(value); 3706} 3707 3708/* 3709%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3710% % 3711% % 3712% % 3713% 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 % 3714% % 3715% % 3716% % 3717%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3718% 3719% ResetImagePropertyIterator() resets the image properties iterator. Use it 3720% in conjunction with GetNextImageProperty() to iterate over all the values 3721% associated with an image property. 3722% 3723% The format of the ResetImagePropertyIterator method is: 3724% 3725% ResetImagePropertyIterator(Image *image) 3726% 3727% A description of each parameter follows: 3728% 3729% o image: the image. 3730% 3731*/ 3732MagickExport void ResetImagePropertyIterator(const Image *image) 3733{ 3734 assert(image != (Image *) NULL); 3735 assert(image->signature == MagickSignature); 3736 if (image->debug != MagickFalse) 3737 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 3738 image->filename); 3739 if (image->properties == (void *) NULL) 3740 return; 3741 ResetSplayTreeIterator((SplayTreeInfo *) image->properties); 3742} 3743 3744/* 3745%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3746% % 3747% % 3748% % 3749% S e t I m a g e P r o p e r t y % 3750% % 3751% % 3752% % 3753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3754% 3755% SetImageProperty() saves the given string value either to specific known 3756% attribute or to a freeform property string. 3757% 3758% Attempting to set a property that is normally calculated will produce 3759% an exception. 3760% 3761% The format of the SetImageProperty method is: 3762% 3763% MagickBooleanType SetImageProperty(Image *image,const char *property, 3764% const char *value,ExceptionInfo *exception) 3765% 3766% A description of each parameter follows: 3767% 3768% o image: the image. 3769% 3770% o property: the image property. 3771% 3772% o values: the image property values. 3773% 3774% o exception: return any errors or warnings in this structure. 3775% 3776*/ 3777MagickExport MagickBooleanType SetImageProperty(Image *image, 3778 const char *property,const char *value,ExceptionInfo *exception) 3779{ 3780 MagickBooleanType 3781 status; 3782 3783 MagickStatusType 3784 flags; 3785 3786 assert(image != (Image *) NULL); 3787 assert(image->signature == MagickSignature); 3788 if (image->debug != MagickFalse) 3789 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 3790 if (image->properties == (void *) NULL) 3791 image->properties=NewSplayTree(CompareSplayTreeString, 3792 RelinquishMagickMemory,RelinquishMagickMemory); /* create splay-tree */ 3793 if (value == (const char *) NULL) 3794 return(DeleteImageProperty(image,property)); /* delete if NULL */ 3795 status=MagickTrue; 3796 if (strlen(property) <= 1) 3797 { 3798 /* 3799 Do not 'set' single letter properties - read only shorthand. 3800 */ 3801 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 3802 "SetReadOnlyProperty","`%s'",property); 3803 return(MagickFalse); 3804 } 3805 3806 /* FUTURE: binary chars or quotes in key should produce a error */ 3807 /* Set attributes with known names or special prefixes 3808 return result is found, or break to set a free form properity 3809 */ 3810 switch (*property) 3811 { 3812#if 0 /* Percent escape's sets values with this prefix: for later use 3813 Throwing an exception causes this setting to fail */ 3814 case '8': 3815 { 3816 if (LocaleNCompare("8bim:",property,5) == 0) 3817 { 3818 (void) ThrowMagickException(exception,GetMagickModule(), 3819 OptionError,"SetReadOnlyProperty","`%s'",property); 3820 return(MagickFalse); 3821 } 3822 break; 3823 } 3824#endif 3825 case 'B': 3826 case 'b': 3827 { 3828 if (LocaleCompare("background",property) == 0) 3829 { 3830 (void) QueryColorCompliance(value,AllCompliance, 3831 &image->background_color,exception); 3832 /* check for FUTURE: value exception?? */ 3833 /* also add user input to splay tree */ 3834 } 3835 break; /* not an attribute, add as a property */ 3836 } 3837 case 'C': 3838 case 'c': 3839 { 3840 if (LocaleCompare("channels",property) == 0) 3841 { 3842 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 3843 "SetReadOnlyProperty","`%s'",property); 3844 return(MagickFalse); 3845 } 3846 if (LocaleCompare("colorspace",property) == 0) 3847 { 3848 ssize_t 3849 colorspace; 3850 3851 colorspace=ParseCommandOption(MagickColorspaceOptions,MagickFalse, 3852 value); 3853 if (colorspace < 0) 3854 return(MagickFalse); /* FUTURE: value exception?? */ 3855 return(SetImageColorspace(image,(ColorspaceType) colorspace,exception)); 3856 } 3857 if (LocaleCompare("compose",property) == 0) 3858 { 3859 ssize_t 3860 compose; 3861 3862 compose=ParseCommandOption(MagickComposeOptions,MagickFalse,value); 3863 if (compose < 0) 3864 return(MagickFalse); /* FUTURE: value exception?? */ 3865 image->compose=(CompositeOperator) compose; 3866 return(MagickTrue); 3867 } 3868 if (LocaleCompare("compress",property) == 0) 3869 { 3870 ssize_t 3871 compression; 3872 3873 compression=ParseCommandOption(MagickCompressOptions,MagickFalse, 3874 value); 3875 if (compression < 0) 3876 return(MagickFalse); /* FUTURE: value exception?? */ 3877 image->compression=(CompressionType) compression; 3878 return(MagickTrue); 3879 } 3880 break; /* not an attribute, add as a property */ 3881 } 3882 case 'D': 3883 case 'd': 3884 { 3885 if (LocaleCompare("delay",property) == 0) 3886 { 3887 GeometryInfo 3888 geometry_info; 3889 3890 flags=ParseGeometry(value,&geometry_info); 3891 if ((flags & GreaterValue) != 0) 3892 { 3893 if (image->delay > (size_t) floor(geometry_info.rho+0.5)) 3894 image->delay=(size_t) floor(geometry_info.rho+0.5); 3895 } 3896 else 3897 if ((flags & LessValue) != 0) 3898 { 3899 if (image->delay < (size_t) floor(geometry_info.rho+0.5)) 3900 image->delay=(ssize_t) 3901 floor(geometry_info.sigma+0.5); 3902 } 3903 else 3904 image->delay=(size_t) floor(geometry_info.rho+0.5); 3905 if ((flags & SigmaValue) != 0) 3906 image->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5); 3907 return(MagickTrue); 3908 } 3909 if (LocaleCompare("delay_units",property) == 0) 3910 { 3911 (void) ThrowMagickException(exception,GetMagickModule(), 3912 OptionError,"SetReadOnlyProperty","`%s'",property); 3913 return(MagickFalse); 3914 } 3915 if (LocaleCompare("density",property) == 0) 3916 { 3917 GeometryInfo 3918 geometry_info; 3919 3920 flags=ParseGeometry(value,&geometry_info); 3921 image->resolution.x=geometry_info.rho; 3922 image->resolution.y=geometry_info.sigma; 3923 if ((flags & SigmaValue) == 0) 3924 image->resolution.y=image->resolution.x; 3925 return(MagickTrue); 3926 } 3927 if (LocaleCompare("depth",property) == 0) 3928 { 3929 image->depth=StringToUnsignedLong(value); 3930 return(MagickTrue); 3931 } 3932 if (LocaleCompare("dispose",property) == 0) 3933 { 3934 ssize_t 3935 dispose; 3936 3937 dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,value); 3938 if (dispose < 0) 3939 return(MagickFalse); /* FUTURE: value exception?? */ 3940 image->dispose=(DisposeType) dispose; 3941 return(MagickTrue); 3942 } 3943 break; /* not an attribute, add as a property */ 3944 } 3945#if 0 /* Percent escape's sets values with this prefix: for later use 3946 Throwing an exception causes this setting to fail */ 3947 case 'E': 3948 case 'e': 3949 { 3950 if (LocaleNCompare("exif:",property,5) == 0) 3951 { 3952 (void) ThrowMagickException(exception,GetMagickModule(), 3953 OptionError,"SetReadOnlyProperty","`%s'",property); 3954 return(MagickFalse); 3955 } 3956 break; /* not an attribute, add as a property */ 3957 } 3958 case 'F': 3959 case 'f': 3960 { 3961 if (LocaleNCompare("fx:",property,3) == 0) 3962 { 3963 (void) ThrowMagickException(exception,GetMagickModule(), 3964 OptionError,"SetReadOnlyProperty","`%s'",property); 3965 return(MagickFalse); 3966 } 3967 break; /* not an attribute, add as a property */ 3968 } 3969#endif 3970 case 'G': 3971 case 'g': 3972 { 3973 if (LocaleCompare("gamma",property) == 0) 3974 { 3975 image->gamma=StringToDouble(value,(char **) NULL); 3976 return(MagickTrue); 3977 } 3978 if (LocaleCompare("gravity",property) == 0) 3979 { 3980 ssize_t 3981 gravity; 3982 3983 gravity=ParseCommandOption(MagickGravityOptions,MagickFalse,value); 3984 if (gravity < 0) 3985 return(MagickFalse); /* FUTURE: value exception?? */ 3986 image->gravity=(GravityType) gravity; 3987 return(MagickTrue); 3988 } 3989 break; /* not an attribute, add as a property */ 3990 } 3991 case 'H': 3992 case 'h': 3993 { 3994 if (LocaleCompare("height",property) == 0) 3995 { 3996 (void) ThrowMagickException(exception,GetMagickModule(), 3997 OptionError,"SetReadOnlyProperty","`%s'",property); 3998 return(MagickFalse); 3999 } 4000 break; /* not an attribute, add as a property */ 4001 } 4002 case 'I': 4003 case 'i': 4004 { 4005 if (LocaleCompare("intensity",property) == 0) 4006 { 4007 ssize_t 4008 intensity; 4009 4010 intensity=ParseCommandOption(MagickIntentOptions,MagickFalse, 4011 value); 4012 if (intensity < 0) 4013 return(MagickFalse); 4014 image->intensity=(PixelIntensityMethod) intensity; 4015 return(MagickTrue); 4016 } 4017 if (LocaleCompare("intent",property) == 0) 4018 { 4019 ssize_t 4020 rendering_intent; 4021 4022 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse, 4023 value); 4024 if (rendering_intent < 0) 4025 return(MagickFalse); /* FUTURE: value exception?? */ 4026 image->rendering_intent=(RenderingIntent) rendering_intent; 4027 return(MagickTrue); 4028 } 4029 if (LocaleCompare("interpolate",property) == 0) 4030 { 4031 ssize_t 4032 interpolate; 4033 4034 interpolate=ParseCommandOption(MagickInterpolateOptions,MagickFalse, 4035 value); 4036 if (interpolate < 0) 4037 return(MagickFalse); /* FUTURE: value exception?? */ 4038 image->interpolate=(PixelInterpolateMethod) interpolate; 4039 return(MagickTrue); 4040 } 4041#if 0 /* Percent escape's sets values with this prefix: for later use 4042 Throwing an exception causes this setting to fail */ 4043 if (LocaleNCompare("iptc:",property,5) == 0) 4044 { 4045 (void) ThrowMagickException(exception,GetMagickModule(), 4046 OptionError,"SetReadOnlyProperty","`%s'",property); 4047 return(MagickFalse); 4048 } 4049#endif 4050 break; /* not an attribute, add as a property */ 4051 } 4052 case 'K': 4053 case 'k': 4054 if (LocaleCompare("kurtosis",property) == 0) 4055 { 4056 (void) ThrowMagickException(exception,GetMagickModule(), 4057 OptionError,"SetReadOnlyProperty","`%s'",property); 4058 return(MagickFalse); 4059 } 4060 break; /* not an attribute, add as a property */ 4061 case 'L': 4062 case 'l': 4063 { 4064 if (LocaleCompare("loop",property) == 0) 4065 { 4066 image->iterations=StringToUnsignedLong(value); 4067 return(MagickTrue); 4068 } 4069 break; /* not an attribute, add as a property */ 4070 } 4071 case 'M': 4072 case 'm': 4073 if ( (LocaleCompare("magick",property) == 0) || 4074 (LocaleCompare("max",property) == 0) || 4075 (LocaleCompare("mean",property) == 0) || 4076 (LocaleCompare("min",property) == 0) || 4077 (LocaleCompare("min",property) == 0) ) 4078 { 4079 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 4080 "SetReadOnlyProperty","`%s'",property); 4081 return(MagickFalse); 4082 } 4083 break; /* not an attribute, add as a property */ 4084 case 'O': 4085 case 'o': 4086 if (LocaleCompare("opaque",property) == 0) 4087 { 4088 (void) ThrowMagickException(exception,GetMagickModule(), 4089 OptionError,"SetReadOnlyProperty","`%s'",property); 4090 return(MagickFalse); 4091 } 4092 break; /* not an attribute, add as a property */ 4093 case 'P': 4094 case 'p': 4095 { 4096 if (LocaleCompare("page",property) == 0) 4097 { 4098 char 4099 *geometry; 4100 4101 geometry=GetPageGeometry(value); 4102 flags=ParseAbsoluteGeometry(geometry,&image->page); 4103 geometry=DestroyString(geometry); 4104 return(MagickTrue); 4105 } 4106#if 0 /* Percent escape's sets values with this prefix: for later use 4107 Throwing an exception causes this setting to fail */ 4108 if (LocaleNCompare("pixel:",property,6) == 0) 4109 { 4110 (void) ThrowMagickException(exception,GetMagickModule(), 4111 OptionError,"SetReadOnlyProperty","`%s'",property); 4112 return(MagickFalse); 4113 } 4114#endif 4115 if (LocaleCompare("profile",property) == 0) 4116 { 4117 ImageInfo 4118 *image_info; 4119 4120 StringInfo 4121 *profile; 4122 4123 image_info=AcquireImageInfo(); 4124 (void) CopyMagickString(image_info->filename,value,MaxTextExtent); 4125 (void) SetImageInfo(image_info,1,exception); 4126 profile=FileToStringInfo(image_info->filename,~0UL,exception); 4127 if (profile != (StringInfo *) NULL) 4128 status=SetImageProfile(image,image_info->magick,profile,exception); 4129 image_info=DestroyImageInfo(image_info); 4130 return(MagickTrue); 4131 } 4132 break; /* not an attribute, add as a property */ 4133 } 4134 case 'R': 4135 case 'r': 4136 { 4137 if (LocaleCompare("rendering-intent",property) == 0) 4138 { 4139 ssize_t 4140 rendering_intent; 4141 4142 rendering_intent=ParseCommandOption(MagickIntentOptions,MagickFalse, 4143 value); 4144 if (rendering_intent < 0) 4145 return(MagickFalse); /* FUTURE: value exception?? */ 4146 image->rendering_intent=(RenderingIntent) rendering_intent; 4147 return(MagickTrue); 4148 } 4149 break; /* not an attribute, add as a property */ 4150 } 4151 case 'S': 4152 case 's': 4153 if ( (LocaleCompare("size",property) == 0) || 4154 (LocaleCompare("skewness",property) == 0) || 4155 (LocaleCompare("scenes",property) == 0) || 4156 (LocaleCompare("standard-deviation",property) == 0) ) 4157 { 4158 (void) ThrowMagickException(exception,GetMagickModule(), 4159 OptionError,"SetReadOnlyProperty","`%s'",property); 4160 return(MagickFalse); 4161 } 4162 break; /* not an attribute, add as a property */ 4163 case 'T': 4164 case 't': 4165 { 4166 if (LocaleCompare("tile-offset",property) == 0) 4167 { 4168 char 4169 *geometry; 4170 4171 geometry=GetPageGeometry(value); 4172 flags=ParseAbsoluteGeometry(geometry,&image->tile_offset); 4173 geometry=DestroyString(geometry); 4174 return(MagickTrue); 4175 } 4176 break; /* not an attribute, add as a property */ 4177 } 4178 case 'U': 4179 case 'u': 4180 { 4181 if (LocaleCompare("units",property) == 0) 4182 { 4183 ssize_t 4184 units; 4185 4186 units=ParseCommandOption(MagickResolutionOptions,MagickFalse,value); 4187 if (units < 0) 4188 return(MagickFalse); /* FUTURE: value exception?? */ 4189 image->units=(ResolutionType) units; 4190 return(MagickTrue); 4191 } 4192 break; /* not an attribute, add as a property */ 4193 } 4194 case 'V': 4195 case 'v': 4196 { 4197 if (LocaleCompare("version",property) == 0) 4198 { 4199 (void) ThrowMagickException(exception,GetMagickModule(), 4200 OptionError,"SetReadOnlyProperty","`%s'",property); 4201 return(MagickFalse); 4202 } 4203 break; /* not an attribute, add as a property */ 4204 } 4205 case 'W': 4206 case 'w': 4207 { 4208 if (LocaleCompare("width",property) == 0) 4209 { 4210 (void) ThrowMagickException(exception,GetMagickModule(), 4211 OptionError,"SetReadOnlyProperty","`%s'",property); 4212 return(MagickFalse); 4213 } 4214 break; /* not an attribute, add as a property */ 4215 } 4216#if 0 /* Percent escape's sets values with this prefix: for later use 4217 Throwing an exception causes this setting to fail */ 4218 case 'X': 4219 case 'x': 4220 { 4221 if (LocaleNCompare("xmp:",property,4) == 0) 4222 { 4223 (void) ThrowMagickException(exception,GetMagickModule(), 4224 OptionError,"SetReadOnlyProperty","`%s'",property); 4225 return(MagickFalse); 4226 } 4227 break; /* not an attribute, add as a property */ 4228 } 4229#endif 4230 } 4231 /* Default: not an attribute, add as a property */ 4232 status=AddValueToSplayTree((SplayTreeInfo *) image->properties, 4233 ConstantString(property),ConstantString(value)); 4234 /* FUTURE: error if status is bad? */ 4235 return(status); 4236} 4237