1/* 2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3% % 4% % 5% % 6% TTTTT Y Y PPPP EEEEE % 7% T Y Y P P E % 8% T Y PPPP EEE % 9% T Y P E % 10% T Y P EEEEE % 11% % 12% % 13% MagickCore Image Type Methods % 14% % 15% Software Design % 16% Cristy % 17% May 2001 % 18% % 19% % 20% Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization % 21% dedicated to making software imaging solutions freely available. % 22% % 23% You may not use this file except in compliance with the License. You may % 24% obtain a copy of the License at % 25% % 26% http://www.imagemagick.org/script/license.php % 27% % 28% Unless required by applicable law or agreed to in writing, software % 29% distributed under the License is distributed on an "AS IS" BASIS, % 30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 31% See the License for the specific language governing permissions and % 32% limitations under the License. % 33% % 34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35% 36% 37*/ 38 39/* 40 Include declarations. 41*/ 42#include "MagickCore/studio.h" 43#include "MagickCore/blob.h" 44#include "MagickCore/client.h" 45#include "MagickCore/configure.h" 46#include "MagickCore/draw.h" 47#include "MagickCore/exception.h" 48#include "MagickCore/exception-private.h" 49#include "MagickCore/image-private.h" 50#include "MagickCore/linked-list.h" 51#include "MagickCore/log.h" 52#include "MagickCore/memory_.h" 53#include "MagickCore/nt-feature.h" 54#include "MagickCore/nt-base-private.h" 55#include "MagickCore/option.h" 56#include "MagickCore/semaphore.h" 57#include "MagickCore/splay-tree.h" 58#include "MagickCore/string_.h" 59#include "MagickCore/string-private.h" 60#include "MagickCore/type.h" 61#include "MagickCore/type-private.h" 62#include "MagickCore/token.h" 63#include "MagickCore/utility.h" 64#include "MagickCore/utility-private.h" 65#include "MagickCore/xml-tree.h" 66#if defined(MAGICKCORE_FONTCONFIG_DELEGATE) 67# include "fontconfig/fontconfig.h" 68#if (FC_VERSION < 20209) 69#undef FC_WEIGHT_LIGHT 70#define FC_WIDTH "width" /* Int */ 71#define FC_WIDTH_ULTRACONDENSED 50 72#define FC_WIDTH_EXTRACONDENSED 63 73#define FC_WIDTH_CONDENSED 75 74#define FC_WIDTH_SEMICONDENSED 87 75#define FC_WIDTH_NORMAL 100 76#define FC_WIDTH_SEMIEXPANDED 113 77#define FC_WIDTH_EXPANDED 125 78#define FC_WIDTH_EXTRAEXPANDED 150 79#define FC_WIDTH_ULTRAEXPANDED 200 80 81#define FC_WEIGHT_THIN 0 82#define FC_WEIGHT_EXTRALIGHT 40 83#define FC_WEIGHT_ULTRALIGHT FC_WEIGHT_EXTRALIGHT 84#define FC_WEIGHT_LIGHT 50 85#define FC_WEIGHT_BOOK 75 86#define FC_WEIGHT_REGULAR 80 87#define FC_WEIGHT_NORMAL FC_WEIGHT_REGULAR 88#define FC_WEIGHT_MEDIUM 100 89#define FC_WEIGHT_DEMIBOLD 180 90#define FC_WEIGHT_SEMIBOLD FC_WEIGHT_DEMIBOLD 91#define FC_WEIGHT_BOLD 200 92#define FC_WEIGHT_EXTRABOLD 205 93#define FC_WEIGHT_ULTRABOLD FC_WEIGHT_EXTRABOLD 94#define FC_WEIGHT_BLACK 210 95#define FC_WEIGHT_HEAVY FC_WEIGHT_BLACK 96#endif 97#endif 98#if defined(MAGICKCORE_WINDOWS_SUPPORT) 99# include "MagickCore/nt-feature.h" 100#endif 101 102/* 103 Define declarations. 104*/ 105#define MagickTypeFilename "type.xml" 106 107/* 108 Declare type map. 109*/ 110static const char 111 *TypeMap = (const char *) 112 "<?xml version=\"1.0\"?>" 113 "<typemap>" 114 " <type stealth=\"True\" name=\"fixed\" family=\"helvetica\"/>" 115 " <type stealth=\"True\" name=\"helvetica\" family=\"helvetica\"/>" 116 "</typemap>"; 117 118/* 119 Static declarations. 120*/ 121static SemaphoreInfo 122 *type_semaphore = (SemaphoreInfo *) NULL; 123 124static SplayTreeInfo 125 *type_cache = (SplayTreeInfo *) NULL; 126 127/* 128 Forward declarations. 129*/ 130static MagickBooleanType 131 IsTypeTreeInstantiated(ExceptionInfo *), 132 LoadTypeCache(SplayTreeInfo *,const char *,const char *,const size_t, 133 ExceptionInfo *); 134 135/* 136%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 137% % 138% % 139% % 140% A c q u i r e T y p e S p l a y T r e e % 141% % 142% % 143% % 144%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 145% 146% AcquireTypeCache() caches one or more type configuration files which 147% provides a mapping between type attributes and a type name. 148% 149% The format of the AcquireTypeCache method is: 150% 151% SplayTreeInfo *AcquireTypeCache(const char *filename, 152% ExceptionInfo *exception) 153% 154% A description of each parameter follows: 155% 156% o filename: the font file name. 157% 158% o exception: return any errors or warnings in this structure. 159% 160*/ 161 162static void *DestroyTypeNode(void *type_info) 163{ 164 register TypeInfo 165 *p; 166 167 p=(TypeInfo *) type_info; 168 if (p->path != (char *) NULL) 169 p->path=DestroyString(p->path); 170 if (p->name != (char *) NULL) 171 p->name=DestroyString(p->name); 172 if (p->description != (char *) NULL) 173 p->description=DestroyString(p->description); 174 if (p->family != (char *) NULL) 175 p->family=DestroyString(p->family); 176 if (p->encoding != (char *) NULL) 177 p->encoding=DestroyString(p->encoding); 178 if (p->foundry != (char *) NULL) 179 p->foundry=DestroyString(p->foundry); 180 if (p->format != (char *) NULL) 181 p->format=DestroyString(p->format); 182 if (p->metrics != (char *) NULL) 183 p->metrics=DestroyString(p->metrics); 184 if (p->glyphs != (char *) NULL) 185 p->glyphs=DestroyString(p->glyphs); 186 return(RelinquishMagickMemory(p)); 187} 188 189static SplayTreeInfo *AcquireTypeCache(const char *filename, 190 ExceptionInfo *exception) 191{ 192 MagickStatusType 193 status; 194 195 SplayTreeInfo 196 *cache; 197 198 cache=NewSplayTree(CompareSplayTreeString,(void *(*)(void *)) NULL, 199 DestroyTypeNode); 200 if (cache == (SplayTreeInfo *) NULL) 201 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 202 status=MagickTrue; 203#if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT) 204 { 205 char 206 *font_path, 207 path[MagickPathExtent]; 208 209 const StringInfo 210 *option; 211 212 LinkedListInfo 213 *options; 214 215 *path='\0'; 216 options=GetConfigureOptions(filename,exception); 217 option=(const StringInfo *) GetNextValueInLinkedList(options); 218 while (option != (const StringInfo *) NULL) 219 { 220 (void) CopyMagickString(path,GetStringInfoPath(option),MagickPathExtent); 221 status&=LoadTypeCache(cache,(const char *) 222 GetStringInfoDatum(option),GetStringInfoPath(option),0,exception); 223 option=(const StringInfo *) GetNextValueInLinkedList(options); 224 } 225 options=DestroyConfigureOptions(options); 226 font_path=GetEnvironmentValue("MAGICK_FONT_PATH"); 227 if (font_path != (char *) NULL) 228 { 229 char 230 *option; 231 232 /* 233 Search MAGICK_FONT_PATH. 234 */ 235 (void) FormatLocaleString(path,MagickPathExtent,"%s%s%s",font_path, 236 DirectorySeparator,filename); 237 option=FileToString(path,~0UL,exception); 238 if (option != (void *) NULL) 239 { 240 status&=LoadTypeCache(cache,option,path,0,exception); 241 option=DestroyString(option); 242 } 243 font_path=DestroyString(font_path); 244 } 245 } 246#endif 247 if (GetNumberOfNodesInSplayTree(cache) == 0) 248 status&=LoadTypeCache(cache,TypeMap,"built-in",0,exception); 249 return(cache); 250} 251 252/* 253%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 254% % 255% % 256% % 257+ G e t T y p e I n f o % 258% % 259% % 260% % 261%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 262% 263% GetTypeInfo searches the type list for the specified name and if found 264% returns attributes for that type. 265% 266% The format of the GetTypeInfo method is: 267% 268% const TypeInfo *GetTypeInfo(const char *name,ExceptionInfo *exception) 269% 270% A description of each parameter follows: 271% 272% o name: the type name. 273% 274% o exception: return any errors or warnings in this structure. 275% 276*/ 277MagickExport const TypeInfo *GetTypeInfo(const char *name, 278 ExceptionInfo *exception) 279{ 280 const TypeInfo 281 *type_info; 282 283 assert(exception != (ExceptionInfo *) NULL); 284 if (IsTypeTreeInstantiated(exception) == MagickFalse) 285 return((const TypeInfo *) NULL); 286 LockSemaphoreInfo(type_semaphore); 287 if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0)) 288 { 289 ResetSplayTreeIterator(type_cache); 290 type_info=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 291 UnlockSemaphoreInfo(type_semaphore); 292 return(type_info); 293 } 294 type_info=(const TypeInfo *) GetValueFromSplayTree(type_cache,name); 295 UnlockSemaphoreInfo(type_semaphore); 296 return(type_info); 297} 298 299/* 300%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 301% % 302% % 303% % 304+ G e t T y p e I n f o B y F a m i l y % 305% % 306% % 307% % 308%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 309% 310% GetTypeInfoByFamily() searches the type list for the specified family and if 311% found returns attributes for that type. 312% 313% Type substitution and scoring algorithm contributed by Bob Friesenhahn. 314% 315% The format of the GetTypeInfoByFamily method is: 316% 317% const TypeInfo *GetTypeInfoByFamily(const char *family, 318% const StyleType style,const StretchType stretch, 319% const size_t weight,ExceptionInfo *exception) 320% 321% A description of each parameter follows: 322% 323% o family: the type family. 324% 325% o style: the type style. 326% 327% o stretch: the type stretch. 328% 329% o weight: the type weight. 330% 331% o exception: return any errors or warnings in this structure. 332% 333*/ 334 335MagickExport const TypeInfo *GetTypeInfoByFamily(const char *family, 336 const StyleType style,const StretchType stretch,const size_t weight, 337 ExceptionInfo *exception) 338{ 339 typedef struct _Fontmap 340 { 341 const char 342 *name, 343 *substitute; 344 } Fontmap; 345 346 const TypeInfo 347 *type_info; 348 349 register const TypeInfo 350 *p; 351 352 register ssize_t 353 i; 354 355 ssize_t 356 range; 357 358 static const Fontmap 359 fontmap[] = 360 { 361 { "fixed", "courier" }, 362 { "modern","courier" }, 363 { "monotype corsiva", "courier" }, 364 { "news gothic", "helvetica" }, 365 { "system", "courier" }, 366 { "terminal", "courier" }, 367 { "wingdings", "symbol" }, 368 { NULL, NULL } 369 }; 370 371 size_t 372 font_weight, 373 max_score, 374 score; 375 376 /* 377 Check for an exact type match. 378 */ 379 (void) GetTypeInfo("*",exception); 380 if (type_cache == (SplayTreeInfo *) NULL) 381 return((TypeInfo *) NULL); 382 font_weight=weight == 0 ? 400 : weight; 383 LockSemaphoreInfo(type_semaphore); 384 ResetSplayTreeIterator(type_cache); 385 type_info=(const TypeInfo *) NULL; 386 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 387 while (p != (const TypeInfo *) NULL) 388 { 389 if (p->family == (char *) NULL) 390 { 391 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 392 continue; 393 } 394 if (family == (const char *) NULL) 395 { 396 if ((LocaleCompare(p->family,"arial") != 0) && 397 (LocaleCompare(p->family,"helvetica") != 0)) 398 { 399 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 400 continue; 401 } 402 } 403 else 404 if (LocaleCompare(p->family,family) != 0) 405 { 406 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 407 continue; 408 } 409 if ((style != UndefinedStyle) && (style != AnyStyle) && (p->style != style)) 410 { 411 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 412 continue; 413 } 414 if ((stretch != UndefinedStretch) && (stretch != AnyStretch) && 415 (p->stretch != stretch)) 416 { 417 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 418 continue; 419 } 420 if (p->weight != font_weight) 421 { 422 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 423 continue; 424 } 425 type_info=p; 426 break; 427 } 428 UnlockSemaphoreInfo(type_semaphore); 429 if (type_info != (const TypeInfo *) NULL) 430 return(type_info); 431 /* 432 Check for types in the same family. 433 */ 434 max_score=0; 435 LockSemaphoreInfo(type_semaphore); 436 ResetSplayTreeIterator(type_cache); 437 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 438 while (p != (const TypeInfo *) NULL) 439 { 440 if (p->family == (char *) NULL) 441 { 442 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 443 continue; 444 } 445 if (family == (const char *) NULL) 446 { 447 if ((LocaleCompare(p->family,"arial") != 0) && 448 (LocaleCompare(p->family,"helvetica") != 0)) 449 { 450 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 451 continue; 452 } 453 } 454 else 455 if (LocaleCompare(p->family,family) != 0) 456 { 457 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 458 continue; 459 } 460 score=0; 461 if ((style == UndefinedStyle) || (style == AnyStyle) || (p->style == style)) 462 score+=32; 463 else 464 if (((style == ItalicStyle) || (style == ObliqueStyle)) && 465 ((p->style == ItalicStyle) || (p->style == ObliqueStyle))) 466 score+=25; 467 score+=(16*(800-((ssize_t) MagickMax(MagickMin(font_weight,900),p->weight)- 468 (ssize_t) MagickMin(MagickMin(font_weight,900),p->weight))))/800; 469 if ((stretch == UndefinedStretch) || (stretch == AnyStretch)) 470 score+=8; 471 else 472 { 473 range=(ssize_t) UltraExpandedStretch-(ssize_t) NormalStretch; 474 score+=(8*(range-((ssize_t) MagickMax(stretch,p->stretch)- 475 (ssize_t) MagickMin(stretch,p->stretch))))/range; 476 } 477 if (score > max_score) 478 { 479 max_score=score; 480 type_info=p; 481 } 482 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 483 } 484 UnlockSemaphoreInfo(type_semaphore); 485 if (type_info != (const TypeInfo *) NULL) 486 return(type_info); 487 /* 488 Check for table-based substitution match. 489 */ 490 for (i=0; fontmap[i].name != (char *) NULL; i++) 491 { 492 if (family == (const char *) NULL) 493 { 494 if ((LocaleCompare(fontmap[i].name,"arial") != 0) && 495 (LocaleCompare(fontmap[i].name,"helvetica") != 0)) 496 continue; 497 } 498 else 499 if (LocaleCompare(fontmap[i].name,family) != 0) 500 continue; 501 type_info=GetTypeInfoByFamily(fontmap[i].substitute,style,stretch,weight, 502 exception); 503 break; 504 } 505 if (type_info != (const TypeInfo *) NULL) 506 { 507 (void) ThrowMagickException(exception,GetMagickModule(),TypeError, 508 "FontSubstitutionRequired","`%s'",type_info->family); 509 return(type_info); 510 } 511 if (family != (const char *) NULL) 512 type_info=GetTypeInfoByFamily((const char *) NULL,style,stretch,weight, 513 exception); 514 return(type_info); 515} 516 517/* 518%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 519% % 520% % 521% % 522% G e t T y p e I n f o L i s t % 523% % 524% % 525% % 526%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 527% 528% GetTypeInfoList() returns any fonts that match the specified pattern. 529% 530% The format of the GetTypeInfoList function is: 531% 532% const TypeInfo **GetTypeInfoList(const char *pattern, 533% size_t *number_fonts,ExceptionInfo *exception) 534% 535% A description of each parameter follows: 536% 537% o pattern: Specifies a pointer to a text string containing a pattern. 538% 539% o number_fonts: This integer returns the number of types in the list. 540% 541% o exception: return any errors or warnings in this structure. 542% 543*/ 544 545#if defined(__cplusplus) || defined(c_plusplus) 546extern "C" { 547#endif 548 549static int TypeInfoCompare(const void *x,const void *y) 550{ 551 const TypeInfo 552 **p, 553 **q; 554 555 p=(const TypeInfo **) x, 556 q=(const TypeInfo **) y; 557 if (LocaleCompare((*p)->path,(*q)->path) == 0) 558 return(LocaleCompare((*p)->name,(*q)->name)); 559 return(LocaleCompare((*p)->path,(*q)->path)); 560} 561 562#if defined(__cplusplus) || defined(c_plusplus) 563} 564#endif 565 566MagickExport const TypeInfo **GetTypeInfoList(const char *pattern, 567 size_t *number_fonts,ExceptionInfo *exception) 568{ 569 const TypeInfo 570 **fonts; 571 572 register const TypeInfo 573 *p; 574 575 register ssize_t 576 i; 577 578 /* 579 Allocate type list. 580 */ 581 assert(pattern != (char *) NULL); 582 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern); 583 assert(number_fonts != (size_t *) NULL); 584 *number_fonts=0; 585 p=GetTypeInfo("*",exception); 586 if (p == (const TypeInfo *) NULL) 587 return((const TypeInfo **) NULL); 588 fonts=(const TypeInfo **) AcquireQuantumMemory((size_t) 589 GetNumberOfNodesInSplayTree(type_cache)+1UL,sizeof(*fonts)); 590 if (fonts == (const TypeInfo **) NULL) 591 return((const TypeInfo **) NULL); 592 /* 593 Generate type list. 594 */ 595 LockSemaphoreInfo(type_semaphore); 596 ResetSplayTreeIterator(type_cache); 597 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 598 for (i=0; p != (const TypeInfo *) NULL; ) 599 { 600 if ((p->stealth == MagickFalse) && 601 (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse)) 602 fonts[i++]=p; 603 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 604 } 605 UnlockSemaphoreInfo(type_semaphore); 606 qsort((void *) fonts,(size_t) i,sizeof(*fonts),TypeInfoCompare); 607 fonts[i]=(TypeInfo *) NULL; 608 *number_fonts=(size_t) i; 609 return(fonts); 610} 611 612/* 613%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 614% % 615% % 616% % 617% G e t T y p e L i s t % 618% % 619% % 620% % 621%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 622% 623% GetTypeList() returns any fonts that match the specified pattern. 624% 625% The format of the GetTypeList function is: 626% 627% char **GetTypeList(const char *pattern,size_t *number_fonts, 628% ExceptionInfo *exception) 629% 630% A description of each parameter follows: 631% 632% o pattern: Specifies a pointer to a text string containing a pattern. 633% 634% o number_fonts: This integer returns the number of fonts in the list. 635% 636% o exception: return any errors or warnings in this structure. 637% 638*/ 639 640#if defined(__cplusplus) || defined(c_plusplus) 641extern "C" { 642#endif 643 644static int TypeCompare(const void *x,const void *y) 645{ 646 register const char 647 **p, 648 **q; 649 650 p=(const char **) x; 651 q=(const char **) y; 652 return(LocaleCompare(*p,*q)); 653} 654 655#if defined(__cplusplus) || defined(c_plusplus) 656} 657#endif 658 659MagickExport char **GetTypeList(const char *pattern,size_t *number_fonts, 660 ExceptionInfo *exception) 661{ 662 char 663 **fonts; 664 665 register const TypeInfo 666 *p; 667 668 register ssize_t 669 i; 670 671 /* 672 Allocate type list. 673 */ 674 assert(pattern != (char *) NULL); 675 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern); 676 assert(number_fonts != (size_t *) NULL); 677 *number_fonts=0; 678 p=GetTypeInfo("*",exception); 679 if (p == (const TypeInfo *) NULL) 680 return((char **) NULL); 681 fonts=(char **) AcquireQuantumMemory((size_t) 682 GetNumberOfNodesInSplayTree(type_cache)+1UL,sizeof(*fonts)); 683 if (fonts == (char **) NULL) 684 return((char **) NULL); 685 /* 686 Generate type list. 687 */ 688 LockSemaphoreInfo(type_semaphore); 689 ResetSplayTreeIterator(type_cache); 690 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 691 for (i=0; p != (const TypeInfo *) NULL; ) 692 { 693 if ((p->stealth == MagickFalse) && 694 (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse)) 695 fonts[i++]=ConstantString(p->name); 696 p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); 697 } 698 UnlockSemaphoreInfo(type_semaphore); 699 qsort((void *) fonts,(size_t) i,sizeof(*fonts),TypeCompare); 700 fonts[i]=(char *) NULL; 701 *number_fonts=(size_t) i; 702 return(fonts); 703} 704 705/* 706%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 707% % 708% % 709% % 710+ I s T y p e T r e e I n s t a n t i a t e d % 711% % 712% % 713% % 714%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 715% 716% IsTypeTreeInstantiated() determines if the type tree is instantiated. If 717% not, it instantiates the tree and returns it. 718% 719% The format of the IsTypeInstantiated method is: 720% 721% MagickBooleanType IsTypeTreeInstantiated(ExceptionInfo *exception) 722% 723% A description of each parameter follows. 724% 725% o exception: return any errors or warnings in this structure. 726% 727*/ 728 729#if defined(MAGICKCORE_FONTCONFIG_DELEGATE) 730MagickExport MagickBooleanType LoadFontConfigFonts(SplayTreeInfo *type_cache, 731 ExceptionInfo *exception) 732{ 733#if !defined(FC_FULLNAME) 734#define FC_FULLNAME "fullname" 735#endif 736 737 char 738 extension[MagickPathExtent], 739 name[MagickPathExtent]; 740 741 FcChar8 742 *family, 743 *file, 744 *fullname, 745 *style; 746 747 FcConfig 748 *font_config; 749 750 FcFontSet 751 *font_set; 752 753 FcObjectSet 754 *object_set; 755 756 FcPattern 757 *pattern; 758 759 FcResult 760 status; 761 762 int 763 slant, 764 width, 765 weight; 766 767 register ssize_t 768 i; 769 770 TypeInfo 771 *type_info; 772 773 /* 774 Load system fonts. 775 */ 776 (void) exception; 777 font_config=FcInitLoadConfigAndFonts(); 778 if (font_config == (FcConfig *) NULL) 779 return(MagickFalse); 780 font_set=(FcFontSet *) NULL; 781 object_set=FcObjectSetBuild(FC_FULLNAME,FC_FAMILY,FC_STYLE,FC_SLANT, 782 FC_WIDTH,FC_WEIGHT,FC_FILE,(char *) NULL); 783 if (object_set != (FcObjectSet *) NULL) 784 { 785 pattern=FcPatternCreate(); 786 if (pattern != (FcPattern *) NULL) 787 { 788 font_set=FcFontList(0,pattern,object_set); 789 FcPatternDestroy(pattern); 790 } 791 FcObjectSetDestroy(object_set); 792 } 793 if (font_set == (FcFontSet *) NULL) 794 { 795 FcConfigDestroy(font_config); 796 return(MagickFalse); 797 } 798 for (i=0; i < (ssize_t) font_set->nfont; i++) 799 { 800 status=FcPatternGetString(font_set->fonts[i],FC_FAMILY,0,&family); 801 if (status != FcResultMatch) 802 continue; 803 status=FcPatternGetString(font_set->fonts[i],FC_FILE,0,&file); 804 if (status != FcResultMatch) 805 continue; 806 *extension='\0'; 807 GetPathComponent((const char *) file,ExtensionPath,extension); 808 if ((*extension != '\0') && (LocaleCompare(extension,"gz") == 0)) 809 continue; 810 type_info=(TypeInfo *) AcquireMagickMemory(sizeof(*type_info)); 811 if (type_info == (TypeInfo *) NULL) 812 continue; 813 (void) ResetMagickMemory(type_info,0,sizeof(*type_info)); 814 type_info->path=ConstantString("System Fonts"); 815 type_info->signature=MagickCoreSignature; 816 (void) CopyMagickString(name,"Unknown",MagickPathExtent); 817 status=FcPatternGetString(font_set->fonts[i],FC_FULLNAME,0,&fullname); 818 if ((status == FcResultMatch) && (fullname != (FcChar8 *) NULL)) 819 (void) CopyMagickString(name,(const char *) fullname,MagickPathExtent); 820 else 821 { 822 if (family != (FcChar8 *) NULL) 823 (void) CopyMagickString(name,(const char *) family,MagickPathExtent); 824 status=FcPatternGetString(font_set->fonts[i],FC_STYLE,0,&style); 825 if ((status == FcResultMatch) && (style != (FcChar8 *) NULL) && 826 (LocaleCompare((const char *) style,"Regular") != 0)) 827 { 828 (void) ConcatenateMagickString(name," ",MagickPathExtent); 829 (void) ConcatenateMagickString(name,(const char *) style, 830 MagickPathExtent); 831 } 832 } 833 type_info->name=ConstantString(name); 834 (void) SubstituteString(&type_info->name," ","-"); 835 type_info->family=ConstantString((const char *) family); 836 status=FcPatternGetInteger(font_set->fonts[i],FC_SLANT,0,&slant); 837 type_info->style=NormalStyle; 838 if (slant == FC_SLANT_ITALIC) 839 type_info->style=ItalicStyle; 840 if (slant == FC_SLANT_OBLIQUE) 841 type_info->style=ObliqueStyle; 842 status=FcPatternGetInteger(font_set->fonts[i],FC_WIDTH,0,&width); 843 type_info->stretch=NormalStretch; 844 if (width >= FC_WIDTH_ULTRACONDENSED) 845 type_info->stretch=UltraCondensedStretch; 846 if (width >= FC_WIDTH_EXTRACONDENSED) 847 type_info->stretch=ExtraCondensedStretch; 848 if (width >= FC_WIDTH_CONDENSED) 849 type_info->stretch=CondensedStretch; 850 if (width >= FC_WIDTH_SEMICONDENSED) 851 type_info->stretch=SemiCondensedStretch; 852 if (width >= FC_WIDTH_NORMAL) 853 type_info->stretch=NormalStretch; 854 if (width >= FC_WIDTH_SEMIEXPANDED) 855 type_info->stretch=SemiExpandedStretch; 856 if (width >= FC_WIDTH_EXPANDED) 857 type_info->stretch=ExpandedStretch; 858 if (width >= FC_WIDTH_EXTRAEXPANDED) 859 type_info->stretch=ExtraExpandedStretch; 860 if (width >= FC_WIDTH_ULTRAEXPANDED) 861 type_info->stretch=UltraExpandedStretch; 862 type_info->weight=400; 863 status=FcPatternGetInteger(font_set->fonts[i],FC_WEIGHT,0,&weight); 864 if (weight >= FC_WEIGHT_THIN) 865 type_info->weight=100; 866 if (weight >= FC_WEIGHT_EXTRALIGHT) 867 type_info->weight=200; 868 if (weight >= FC_WEIGHT_LIGHT) 869 type_info->weight=300; 870 if (weight >= FC_WEIGHT_NORMAL) 871 type_info->weight=400; 872 if (weight >= FC_WEIGHT_MEDIUM) 873 type_info->weight=500; 874 if (weight >= FC_WEIGHT_DEMIBOLD) 875 type_info->weight=600; 876 if (weight >= FC_WEIGHT_BOLD) 877 type_info->weight=700; 878 if (weight >= FC_WEIGHT_EXTRABOLD) 879 type_info->weight=800; 880 if (weight >= FC_WEIGHT_BLACK) 881 type_info->weight=900; 882 type_info->glyphs=ConstantString((const char *) file); 883 (void) AddValueToSplayTree(type_cache,type_info->name,type_info); 884 } 885 FcFontSetDestroy(font_set); 886 FcConfigDestroy(font_config); 887 return(MagickTrue); 888} 889#endif 890 891static MagickBooleanType IsTypeTreeInstantiated(ExceptionInfo *exception) 892{ 893 if (type_cache == (SplayTreeInfo *) NULL) 894 { 895 if (type_semaphore == (SemaphoreInfo *) NULL) 896 ActivateSemaphoreInfo(&type_semaphore); 897 LockSemaphoreInfo(type_semaphore); 898 if (type_cache == (SplayTreeInfo *) NULL) 899 { 900 type_cache=AcquireTypeCache(MagickTypeFilename,exception); 901#if defined(MAGICKCORE_WINDOWS_SUPPORT) 902 (void) NTAcquireTypeCache(type_cache,exception); 903#endif 904#if defined(MAGICKCORE_FONTCONFIG_DELEGATE) 905 (void) LoadFontConfigFonts(type_cache,exception); 906#endif 907 } 908 UnlockSemaphoreInfo(type_semaphore); 909 } 910 return(type_cache != (SplayTreeInfo *) NULL ? MagickTrue : MagickFalse); 911} 912 913/* 914%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 915% % 916% % 917% % 918% L i s t T y p e I n f o % 919% % 920% % 921% % 922%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 923% 924% ListTypeInfo() lists the fonts to a file. 925% 926% The format of the ListTypeInfo method is: 927% 928% MagickBooleanType ListTypeInfo(FILE *file,ExceptionInfo *exception) 929% 930% A description of each parameter follows. 931% 932% o file: An pointer to a FILE. 933% 934% o exception: return any errors or warnings in this structure. 935% 936*/ 937MagickExport MagickBooleanType ListTypeInfo(FILE *file,ExceptionInfo *exception) 938{ 939 char 940 weight[MagickPathExtent]; 941 942 const char 943 *family, 944 *glyphs, 945 *name, 946 *path, 947 *stretch, 948 *style; 949 950 const TypeInfo 951 **type_info; 952 953 register ssize_t 954 i; 955 956 size_t 957 number_fonts; 958 959 if (file == (FILE *) NULL) 960 file=stdout; 961 number_fonts=0; 962 type_info=GetTypeInfoList("*",&number_fonts,exception); 963 if (type_info == (const TypeInfo **) NULL) 964 return(MagickFalse); 965 *weight='\0'; 966 path=(const char *) NULL; 967 for (i=0; i < (ssize_t) number_fonts; i++) 968 { 969 if (type_info[i]->stealth != MagickFalse) 970 continue; 971 if (((path == (const char *) NULL) || 972 (LocaleCompare(path,type_info[i]->path) != 0)) && 973 (type_info[i]->path != (char *) NULL)) 974 (void) FormatLocaleFile(file,"\nPath: %s\n",type_info[i]->path); 975 path=type_info[i]->path; 976 name="unknown"; 977 if (type_info[i]->name != (char *) NULL) 978 name=type_info[i]->name; 979 family="unknown"; 980 if (type_info[i]->family != (char *) NULL) 981 family=type_info[i]->family; 982 style=CommandOptionToMnemonic(MagickStyleOptions,type_info[i]->style); 983 stretch=CommandOptionToMnemonic(MagickStretchOptions,type_info[i]->stretch); 984 glyphs="unknown"; 985 if (type_info[i]->glyphs != (char *) NULL) 986 glyphs=type_info[i]->glyphs; 987 (void) FormatLocaleString(weight,MagickPathExtent,"%.20g",(double) 988 type_info[i]->weight); 989 (void) FormatLocaleFile(file," Font: %s\n",name); 990 (void) FormatLocaleFile(file," family: %s\n",family); 991 (void) FormatLocaleFile(file," style: %s\n",style); 992 (void) FormatLocaleFile(file," stretch: %s\n",stretch); 993 (void) FormatLocaleFile(file," weight: %s\n",weight); 994 (void) FormatLocaleFile(file," glyphs: %s\n",glyphs); 995 } 996 (void) fflush(file); 997 type_info=(const TypeInfo **) RelinquishMagickMemory((void *) type_info); 998 return(MagickTrue); 999} 1000 1001/* 1002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1003% % 1004% % 1005% % 1006+ L o a d T y p e C a c h e % 1007% % 1008% % 1009% % 1010%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1011% 1012% LoadTypeCache() loads the type configurations which provides a mapping 1013% between type attributes and a type name. 1014% 1015% The format of the LoadTypeCache method is: 1016% 1017% MagickBooleanType LoadTypeCache(SplayTreeInfo *cache,const char *xml, 1018% const char *filename,const size_t depth,ExceptionInfo *exception) 1019% 1020% A description of each parameter follows: 1021% 1022% o xml: The type list in XML format. 1023% 1024% o filename: The type list filename. 1025% 1026% o depth: depth of <include /> statements. 1027% 1028% o exception: return any errors or warnings in this structure. 1029% 1030*/ 1031 1032static inline MagickBooleanType SetTypeNodePath(const char *filename, 1033 char *font_path,const char *token,char **target) 1034{ 1035 char 1036 *path; 1037 1038 path=ConstantString(token); 1039#if defined(MAGICKCORE_WINDOWS_SUPPORT) 1040 if (strchr(path,'@') != (char *) NULL) 1041 SubstituteString(&path,"@ghostscript_font_path@",font_path); 1042#endif 1043 if (IsPathAccessible(path) == MagickFalse) 1044 { 1045 /* 1046 Relative path. 1047 */ 1048 path=DestroyString(path); 1049 GetPathComponent(filename,HeadPath,font_path); 1050 (void) ConcatenateMagickString(font_path,DirectorySeparator, 1051 MagickPathExtent); 1052 (void) ConcatenateMagickString(font_path,token,MagickPathExtent); 1053 path=ConstantString(font_path); 1054#if defined(MAGICKCORE_WINDOWS_SUPPORT) 1055 if (strchr(path,'@') != (char *) NULL) 1056 SubstituteString(&path,"@ghostscript_font_path@",""); 1057#endif 1058 if (IsPathAccessible(path) == MagickFalse) 1059 { 1060 path=DestroyString(path); 1061 return(MagickFalse); 1062 } 1063 } 1064 1065 *target=path; 1066 return(MagickTrue); 1067} 1068 1069static MagickBooleanType LoadTypeCache(SplayTreeInfo *cache,const char *xml, 1070 const char *filename,const size_t depth,ExceptionInfo *exception) 1071{ 1072 char 1073 font_path[MagickPathExtent], 1074 keyword[MagickPathExtent], 1075 *token; 1076 1077 const char 1078 *q; 1079 1080 MagickStatusType 1081 status; 1082 1083 size_t 1084 extent; 1085 1086 TypeInfo 1087 *type_info; 1088 1089 /* 1090 Load the type map file. 1091 */ 1092 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(), 1093 "Loading type configure file \"%s\" ...",filename); 1094 if (xml == (const char *) NULL) 1095 return(MagickFalse); 1096 status=MagickTrue; 1097 type_info=(TypeInfo *) NULL; 1098 token=AcquireString(xml); 1099 extent=strlen(token)+MagickPathExtent; 1100#if defined(MAGICKCORE_WINDOWS_SUPPORT) 1101 /* 1102 Determine the Ghostscript font path. 1103 */ 1104 *font_path='\0'; 1105 if (NTGhostscriptFonts(font_path,MagickPathExtent-2)) 1106 (void) ConcatenateMagickString(font_path,DirectorySeparator,MagickPathExtent); 1107#endif 1108 for (q=(char *) xml; *q != '\0'; ) 1109 { 1110 /* 1111 Interpret XML. 1112 */ 1113 GetNextToken(q,&q,extent,token); 1114 if (*token == '\0') 1115 break; 1116 (void) CopyMagickString(keyword,token,MagickPathExtent); 1117 if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0) 1118 { 1119 /* 1120 Doctype element. 1121 */ 1122 while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0')) 1123 GetNextToken(q,&q,extent,token); 1124 continue; 1125 } 1126 if (LocaleNCompare(keyword,"<!--",4) == 0) 1127 { 1128 /* 1129 Comment element. 1130 */ 1131 while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0')) 1132 GetNextToken(q,&q,extent,token); 1133 continue; 1134 } 1135 if (LocaleCompare(keyword,"<include") == 0) 1136 { 1137 /* 1138 Include element. 1139 */ 1140 while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0')) 1141 { 1142 (void) CopyMagickString(keyword,token,MagickPathExtent); 1143 GetNextToken(q,&q,extent,token); 1144 if (*token != '=') 1145 continue; 1146 GetNextToken(q,&q,extent,token); 1147 if (LocaleCompare(keyword,"file") == 0) 1148 { 1149 if (depth > 200) 1150 (void) ThrowMagickException(exception,GetMagickModule(), 1151 ConfigureError,"IncludeNodeNestedTooDeeply","`%s'",token); 1152 else 1153 { 1154 char 1155 path[MagickPathExtent], 1156 *xml; 1157 1158 ExceptionInfo 1159 *sans_exception; 1160 1161 *path='\0'; 1162 GetPathComponent(filename,HeadPath,path); 1163 if (*path != '\0') 1164 (void) ConcatenateMagickString(path,DirectorySeparator, 1165 MagickPathExtent); 1166 if (*token == *DirectorySeparator) 1167 (void) CopyMagickString(path,token,MagickPathExtent); 1168 else 1169 (void) ConcatenateMagickString(path,token,MagickPathExtent); 1170 sans_exception=AcquireExceptionInfo(); 1171 xml=FileToString(path,~0UL,sans_exception); 1172 sans_exception=DestroyExceptionInfo(sans_exception); 1173 if (xml != (char *) NULL) 1174 { 1175 status&=LoadTypeCache(cache,xml,path,depth+1,exception); 1176 xml=(char *) RelinquishMagickMemory(xml); 1177 } 1178 } 1179 } 1180 } 1181 continue; 1182 } 1183 if (LocaleCompare(keyword,"<type") == 0) 1184 { 1185 /* 1186 Type element. 1187 */ 1188 type_info=(TypeInfo *) AcquireMagickMemory(sizeof(*type_info)); 1189 if (type_info == (TypeInfo *) NULL) 1190 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 1191 (void) ResetMagickMemory(type_info,0,sizeof(*type_info)); 1192 type_info->path=ConstantString(filename); 1193 type_info->signature=MagickCoreSignature; 1194 continue; 1195 } 1196 if (type_info == (TypeInfo *) NULL) 1197 continue; 1198 if (LocaleCompare(keyword,"/>") == 0) 1199 { 1200 status=AddValueToSplayTree(cache,type_info->name,type_info); 1201 if (status == MagickFalse) 1202 (void) ThrowMagickException(exception,GetMagickModule(), 1203 ResourceLimitError,"MemoryAllocationFailed","`%s'",type_info->name); 1204 type_info=(TypeInfo *) NULL; 1205 continue; 1206 } 1207 GetNextToken(q,(const char **) NULL,extent,token); 1208 if (*token != '=') 1209 continue; 1210 GetNextToken(q,&q,extent,token); 1211 GetNextToken(q,&q,extent,token); 1212 switch (*keyword) 1213 { 1214 case 'E': 1215 case 'e': 1216 { 1217 if (LocaleCompare((char *) keyword,"encoding") == 0) 1218 { 1219 type_info->encoding=ConstantString(token); 1220 break; 1221 } 1222 break; 1223 } 1224 case 'F': 1225 case 'f': 1226 { 1227 if (LocaleCompare((char *) keyword,"face") == 0) 1228 { 1229 type_info->face=StringToUnsignedLong(token); 1230 break; 1231 } 1232 if (LocaleCompare((char *) keyword,"family") == 0) 1233 { 1234 type_info->family=ConstantString(token); 1235 break; 1236 } 1237 if (LocaleCompare((char *) keyword,"format") == 0) 1238 { 1239 type_info->format=ConstantString(token); 1240 break; 1241 } 1242 if (LocaleCompare((char *) keyword,"foundry") == 0) 1243 { 1244 type_info->foundry=ConstantString(token); 1245 break; 1246 } 1247 if (LocaleCompare((char *) keyword,"fullname") == 0) 1248 { 1249 type_info->description=ConstantString(token); 1250 break; 1251 } 1252 break; 1253 } 1254 case 'G': 1255 case 'g': 1256 { 1257 if (LocaleCompare((char *) keyword,"glyphs") == 0) 1258 { 1259 if (SetTypeNodePath(filename,font_path,token,&type_info->glyphs) == 1260 MagickFalse) 1261 type_info=(TypeInfo *) DestroyTypeNode(type_info); 1262 break; 1263 } 1264 break; 1265 } 1266 case 'M': 1267 case 'm': 1268 { 1269 if (LocaleCompare((char *) keyword,"metrics") == 0) 1270 { 1271 if (SetTypeNodePath(filename,font_path,token,&type_info->metrics) == 1272 MagickFalse) 1273 type_info=(TypeInfo *) DestroyTypeNode(type_info); 1274 break; 1275 } 1276 break; 1277 } 1278 case 'N': 1279 case 'n': 1280 { 1281 if (LocaleCompare((char *) keyword,"name") == 0) 1282 { 1283 type_info->name=ConstantString(token); 1284 break; 1285 } 1286 break; 1287 } 1288 case 'S': 1289 case 's': 1290 { 1291 if (LocaleCompare((char *) keyword,"stealth") == 0) 1292 { 1293 type_info->stealth=IsStringTrue(token); 1294 break; 1295 } 1296 if (LocaleCompare((char *) keyword,"stretch") == 0) 1297 { 1298 type_info->stretch=(StretchType) ParseCommandOption( 1299 MagickStretchOptions,MagickFalse,token); 1300 break; 1301 } 1302 if (LocaleCompare((char *) keyword,"style") == 0) 1303 { 1304 type_info->style=(StyleType) ParseCommandOption(MagickStyleOptions, 1305 MagickFalse,token); 1306 break; 1307 } 1308 break; 1309 } 1310 case 'W': 1311 case 'w': 1312 { 1313 if (LocaleCompare((char *) keyword,"weight") == 0) 1314 { 1315 ssize_t 1316 weight; 1317 1318 weight=ParseCommandOption(MagickWeightOptions,MagickFalse,token); 1319 if (weight == -1) 1320 weight=StringToUnsignedLong(token); 1321 type_info->weight=(size_t) weight; 1322 break; 1323 } 1324 break; 1325 } 1326 default: 1327 break; 1328 } 1329 } 1330 token=(char *) RelinquishMagickMemory(token); 1331 return(status != 0 ? MagickTrue : MagickFalse); 1332} 1333 1334/* 1335%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1336% % 1337% % 1338% % 1339+ T y p e C o m p o n e n t G e n e s i s % 1340% % 1341% % 1342% % 1343%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1344% 1345% TypeComponentGenesis() instantiates the type component. 1346% 1347% The format of the TypeComponentGenesis method is: 1348% 1349% MagickBooleanType TypeComponentGenesis(void) 1350% 1351*/ 1352MagickPrivate MagickBooleanType TypeComponentGenesis(void) 1353{ 1354 if (type_semaphore == (SemaphoreInfo *) NULL) 1355 type_semaphore=AcquireSemaphoreInfo(); 1356 return(MagickTrue); 1357} 1358 1359/* 1360%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1361% % 1362% % 1363% % 1364+ T y p e C o m p o n e n t T e r m i n u s % 1365% % 1366% % 1367% % 1368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1369% 1370% TypeComponentTerminus() destroy type component. 1371% 1372% The format of the TypeComponentTerminus method is: 1373% 1374% void TypeComponentTerminus(void) 1375% 1376*/ 1377MagickPrivate void TypeComponentTerminus(void) 1378{ 1379 if (type_semaphore == (SemaphoreInfo *) NULL) 1380 ActivateSemaphoreInfo(&type_semaphore); 1381 LockSemaphoreInfo(type_semaphore); 1382 if (type_cache != (SplayTreeInfo *) NULL) 1383 type_cache=DestroySplayTree(type_cache); 1384 UnlockSemaphoreInfo(type_semaphore); 1385 RelinquishSemaphoreInfo(&type_semaphore); 1386} 1387