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