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