| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % TTTTT Y Y PPPP EEEEE % |
| % T Y Y P P E % |
| % T Y PPPP EEE % |
| % T Y P E % |
| % T Y P EEEEE % |
| % % |
| % % |
| % MagickCore Image Type Methods % |
| % % |
| % Software Design % |
| % Cristy % |
| % May 2001 % |
| % % |
| % % |
| % Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization % |
| % dedicated to making software imaging solutions freely available. % |
| % % |
| % You may not use this file except in compliance with the License. You may % |
| % obtain a copy of the License at % |
| % % |
| % https://imagemagick.org/script/license.php % |
| % % |
| % Unless required by applicable law or agreed to in writing, software % |
| % distributed under the License is distributed on an "AS IS" BASIS, % |
| % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % |
| % See the License for the specific language governing permissions and % |
| % limitations under the License. % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % |
| */ |
| |
| /* |
| Include declarations. |
| */ |
| #include "MagickCore/studio.h" |
| #include "MagickCore/blob.h" |
| #include "MagickCore/client.h" |
| #include "MagickCore/configure.h" |
| #include "MagickCore/draw.h" |
| #include "MagickCore/exception.h" |
| #include "MagickCore/exception-private.h" |
| #include "MagickCore/image-private.h" |
| #include "MagickCore/linked-list.h" |
| #include "MagickCore/log.h" |
| #include "MagickCore/memory_.h" |
| #include "MagickCore/memory-private.h" |
| #include "MagickCore/nt-feature.h" |
| #include "MagickCore/nt-base-private.h" |
| #include "MagickCore/option.h" |
| #include "MagickCore/semaphore.h" |
| #include "MagickCore/splay-tree.h" |
| #include "MagickCore/string_.h" |
| #include "MagickCore/string-private.h" |
| #include "MagickCore/type.h" |
| #include "MagickCore/type-private.h" |
| #include "MagickCore/token.h" |
| #include "MagickCore/utility.h" |
| #include "MagickCore/utility-private.h" |
| #include "MagickCore/xml-tree.h" |
| #if defined(MAGICKCORE_FONTCONFIG_DELEGATE) |
| # include "fontconfig/fontconfig.h" |
| #if (FC_VERSION < 20209) |
| #undef FC_WEIGHT_LIGHT |
| #define FC_WIDTH "width" /* Int */ |
| #define FC_WIDTH_ULTRACONDENSED 50 |
| #define FC_WIDTH_EXTRACONDENSED 63 |
| #define FC_WIDTH_CONDENSED 75 |
| #define FC_WIDTH_SEMICONDENSED 87 |
| #define FC_WIDTH_NORMAL 100 |
| #define FC_WIDTH_SEMIEXPANDED 113 |
| #define FC_WIDTH_EXPANDED 125 |
| #define FC_WIDTH_EXTRAEXPANDED 150 |
| #define FC_WIDTH_ULTRAEXPANDED 200 |
| |
| #define FC_WEIGHT_THIN 0 |
| #define FC_WEIGHT_EXTRALIGHT 40 |
| #define FC_WEIGHT_ULTRALIGHT FC_WEIGHT_EXTRALIGHT |
| #define FC_WEIGHT_LIGHT 50 |
| #define FC_WEIGHT_BOOK 75 |
| #define FC_WEIGHT_REGULAR 80 |
| #define FC_WEIGHT_NORMAL FC_WEIGHT_REGULAR |
| #define FC_WEIGHT_MEDIUM 100 |
| #define FC_WEIGHT_DEMIBOLD 180 |
| #define FC_WEIGHT_SEMIBOLD FC_WEIGHT_DEMIBOLD |
| #define FC_WEIGHT_BOLD 200 |
| #define FC_WEIGHT_EXTRABOLD 205 |
| #define FC_WEIGHT_ULTRABOLD FC_WEIGHT_EXTRABOLD |
| #define FC_WEIGHT_BLACK 210 |
| #define FC_WEIGHT_HEAVY FC_WEIGHT_BLACK |
| #endif |
| #endif |
| #if defined(MAGICKCORE_WINDOWS_SUPPORT) |
| # include "MagickCore/nt-feature.h" |
| #endif |
| |
| /* |
| Define declarations. |
| */ |
| #define MagickTypeFilename "type.xml" |
| |
| /* |
| Declare type map. |
| */ |
| static const char |
| TypeMap[] = |
| "<?xml version=\"1.0\"?>" |
| "<typemap>" |
| " <type stealth=\"True\" name=\"fixed\" family=\"helvetica\"/>" |
| " <type stealth=\"True\" name=\"helvetica\" family=\"helvetica\"/>" |
| "</typemap>"; |
| |
| /* |
| Static declarations. |
| */ |
| static SemaphoreInfo |
| *type_semaphore = (SemaphoreInfo *) NULL; |
| |
| static SplayTreeInfo |
| *type_cache = (SplayTreeInfo *) NULL; |
| |
| /* |
| Forward declarations. |
| */ |
| static MagickBooleanType |
| IsTypeTreeInstantiated(ExceptionInfo *), |
| LoadTypeCache(SplayTreeInfo *,const char *,const char *,const size_t, |
| ExceptionInfo *); |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % A c q u i r e T y p e S p l a y T r e e % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % AcquireTypeCache() caches one or more type configuration files which |
| % provides a mapping between type attributes and a type name. |
| % |
| % The format of the AcquireTypeCache method is: |
| % |
| % SplayTreeInfo *AcquireTypeCache(const char *filename, |
| % ExceptionInfo *exception) |
| % |
| % A description of each parameter follows: |
| % |
| % o filename: the font file name. |
| % |
| % o exception: return any errors or warnings in this structure. |
| % |
| */ |
| |
| static void *DestroyTypeNode(void *type_info) |
| { |
| TypeInfo |
| *p; |
| |
| p=(TypeInfo *) type_info; |
| if (p->path != (char *) NULL) |
| p->path=DestroyString(p->path); |
| if (p->name != (char *) NULL) |
| p->name=DestroyString(p->name); |
| if (p->description != (char *) NULL) |
| p->description=DestroyString(p->description); |
| if (p->family != (char *) NULL) |
| p->family=DestroyString(p->family); |
| if (p->encoding != (char *) NULL) |
| p->encoding=DestroyString(p->encoding); |
| if (p->foundry != (char *) NULL) |
| p->foundry=DestroyString(p->foundry); |
| if (p->format != (char *) NULL) |
| p->format=DestroyString(p->format); |
| if (p->metrics != (char *) NULL) |
| p->metrics=DestroyString(p->metrics); |
| if (p->glyphs != (char *) NULL) |
| p->glyphs=DestroyString(p->glyphs); |
| return(RelinquishMagickMemory(p)); |
| } |
| |
| static SplayTreeInfo *AcquireTypeCache(const char *filename, |
| ExceptionInfo *exception) |
| { |
| MagickStatusType |
| status; |
| |
| SplayTreeInfo |
| *cache; |
| |
| cache=NewSplayTree(CompareSplayTreeString,(void *(*)(void *)) NULL, |
| DestroyTypeNode); |
| status=MagickTrue; |
| #if !MAGICKCORE_ZERO_CONFIGURATION_SUPPORT |
| { |
| char |
| *font_path, |
| path[MagickPathExtent]; |
| |
| const StringInfo |
| *option; |
| |
| LinkedListInfo |
| *options; |
| |
| *path='\0'; |
| options=GetConfigureOptions(filename,exception); |
| option=(const StringInfo *) GetNextValueInLinkedList(options); |
| while (option != (const StringInfo *) NULL) |
| { |
| (void) CopyMagickString(path,GetStringInfoPath(option),MagickPathExtent); |
| status&=LoadTypeCache(cache,(const char *) |
| GetStringInfoDatum(option),GetStringInfoPath(option),0,exception); |
| option=(const StringInfo *) GetNextValueInLinkedList(options); |
| } |
| options=DestroyConfigureOptions(options); |
| font_path=GetEnvironmentValue("MAGICK_FONT_PATH"); |
| if (font_path != (char *) NULL) |
| { |
| char |
| *xml; |
| |
| /* |
| Search MAGICK_FONT_PATH. |
| */ |
| (void) FormatLocaleString(path,MagickPathExtent,"%s%s%s",font_path, |
| DirectorySeparator,filename); |
| xml=FileToString(path,~0UL,exception); |
| if (xml != (void *) NULL) |
| { |
| status&=LoadTypeCache(cache,xml,path,0,exception); |
| xml=DestroyString(xml); |
| } |
| font_path=DestroyString(font_path); |
| } |
| } |
| #endif |
| if (GetNumberOfNodesInSplayTree(cache) == 0) |
| status&=LoadTypeCache(cache,TypeMap,"built-in",0,exception); |
| return(cache); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| + G e t T y p e I n f o % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % GetTypeInfo searches the type list for the specified name and if found |
| % returns attributes for that type. |
| % |
| % The format of the GetTypeInfo method is: |
| % |
| % const TypeInfo *GetTypeInfo(const char *name,ExceptionInfo *exception) |
| % |
| % A description of each parameter follows: |
| % |
| % o name: the type name. |
| % |
| % o exception: return any errors or warnings in this structure. |
| % |
| */ |
| MagickExport const TypeInfo *GetTypeInfo(const char *name, |
| ExceptionInfo *exception) |
| { |
| assert(exception != (ExceptionInfo *) NULL); |
| if (IsTypeTreeInstantiated(exception) == MagickFalse) |
| return((const TypeInfo *) NULL); |
| if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0)) |
| return((const TypeInfo *) GetRootValueFromSplayTree(type_cache)); |
| return((const TypeInfo *) GetValueFromSplayTree(type_cache,name)); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| + G e t T y p e I n f o B y F a m i l y % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % GetTypeInfoByFamily() searches the type list for the specified family and if |
| % found returns attributes for that type. |
| % |
| % Type substitution and scoring algorithm contributed by Bob Friesenhahn. |
| % |
| % The format of the GetTypeInfoByFamily method is: |
| % |
| % const TypeInfo *GetTypeInfoByFamily(const char *family, |
| % const StyleType style,const StretchType stretch, |
| % const size_t weight,ExceptionInfo *exception) |
| % |
| % A description of each parameter follows: |
| % |
| % o family: the type family. |
| % |
| % o style: the type style. |
| % |
| % o stretch: the type stretch. |
| % |
| % o weight: the type weight. |
| % |
| % o exception: return any errors or warnings in this structure. |
| % |
| */ |
| MagickExport const TypeInfo *GetTypeInfoByFamily(const char *family, |
| const StyleType style,const StretchType stretch,const size_t weight, |
| ExceptionInfo *exception) |
| { |
| typedef struct _Fontmap |
| { |
| const char |
| name[17], |
| substitute[10]; |
| } Fontmap; |
| |
| const TypeInfo |
| *type_info; |
| |
| const TypeInfo |
| *p; |
| |
| ssize_t |
| i; |
| |
| ssize_t |
| range; |
| |
| static const Fontmap |
| fontmap[] = |
| { |
| { "fixed", "courier" }, |
| { "modern","courier" }, |
| { "monotype corsiva", "courier" }, |
| { "news gothic", "helvetica" }, |
| { "system", "courier" }, |
| { "terminal", "courier" }, |
| { "wingdings", "symbol" } |
| }; |
| |
| size_t |
| font_weight, |
| max_score, |
| score; |
| |
| /* |
| Check for an exact type match. |
| */ |
| (void) GetTypeInfo("*",exception); |
| if (type_cache == (SplayTreeInfo *) NULL) |
| return((TypeInfo *) NULL); |
| font_weight=(size_t) (weight == 0 ? 400 : weight); |
| LockSemaphoreInfo(type_semaphore); |
| ResetSplayTreeIterator(type_cache); |
| type_info=(const TypeInfo *) NULL; |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| while (p != (const TypeInfo *) NULL) |
| { |
| if (p->family == (char *) NULL) |
| { |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| continue; |
| } |
| if (family == (const char *) NULL) |
| { |
| if ((LocaleCompare(p->family,"arial") != 0) && |
| (LocaleCompare(p->family,"helvetica") != 0)) |
| { |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| continue; |
| } |
| } |
| else |
| if (LocaleCompare(p->family,family) != 0) |
| { |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| continue; |
| } |
| if ((style != UndefinedStyle) && (style != AnyStyle) && (p->style != style)) |
| { |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| continue; |
| } |
| if ((stretch != UndefinedStretch) && (stretch != AnyStretch) && |
| (p->stretch != stretch)) |
| { |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| continue; |
| } |
| if (p->weight != font_weight) |
| { |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| continue; |
| } |
| type_info=p; |
| break; |
| } |
| UnlockSemaphoreInfo(type_semaphore); |
| if (type_info != (const TypeInfo *) NULL) |
| return(type_info); |
| /* |
| Check for types in the same family. |
| */ |
| max_score=0; |
| LockSemaphoreInfo(type_semaphore); |
| ResetSplayTreeIterator(type_cache); |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| while (p != (const TypeInfo *) NULL) |
| { |
| if (p->family == (char *) NULL) |
| { |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| continue; |
| } |
| if (family == (const char *) NULL) |
| { |
| if ((LocaleCompare(p->family,"arial") != 0) && |
| (LocaleCompare(p->family,"helvetica") != 0)) |
| { |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| continue; |
| } |
| } |
| else |
| if (LocaleCompare(p->family,family) != 0) |
| { |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| continue; |
| } |
| score=0; |
| if ((style == UndefinedStyle) || (style == AnyStyle) || (p->style == style)) |
| score+=32; |
| else |
| if (((style == ItalicStyle) || (style == ObliqueStyle)) && |
| ((p->style == ItalicStyle) || (p->style == ObliqueStyle))) |
| score+=25; |
| score+=(16*(800-((ssize_t) MagickMax(MagickMin(font_weight,900),p->weight)- |
| (ssize_t) MagickMin(MagickMin(font_weight,900),p->weight))))/800; |
| if ((stretch == UndefinedStretch) || (stretch == AnyStretch)) |
| score+=8; |
| else |
| { |
| range=(ssize_t) UltraExpandedStretch-(ssize_t) NormalStretch; |
| score+=(8*(range-((ssize_t) MagickMax(stretch,p->stretch)- |
| (ssize_t) MagickMin(stretch,p->stretch))))/range; |
| } |
| if (score > max_score) |
| { |
| max_score=score; |
| type_info=p; |
| } |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| } |
| UnlockSemaphoreInfo(type_semaphore); |
| if (type_info != (const TypeInfo *) NULL) |
| return(type_info); |
| /* |
| Check for table-based substitution match. |
| */ |
| for (i=0; i < (ssize_t) (sizeof(fontmap)/sizeof(fontmap[0])); i++) |
| { |
| if (family == (const char *) NULL) |
| { |
| if ((LocaleCompare(fontmap[i].name,"arial") != 0) && |
| (LocaleCompare(fontmap[i].name,"helvetica") != 0)) |
| continue; |
| } |
| else |
| if (LocaleCompare(fontmap[i].name,family) != 0) |
| continue; |
| type_info=GetTypeInfoByFamily(fontmap[i].substitute,style,stretch,weight, |
| exception); |
| break; |
| } |
| if (type_info != (const TypeInfo *) NULL) |
| { |
| (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning, |
| "FontSubstitutionRequired","`%s'",type_info->family); |
| return(type_info); |
| } |
| if (family != (const char *) NULL) |
| type_info=GetTypeInfoByFamily((const char *) NULL,style,stretch,weight, |
| exception); |
| return(type_info); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % G e t T y p e I n f o L i s t % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % GetTypeInfoList() returns any fonts that match the specified pattern. |
| % |
| % The format of the GetTypeInfoList function is: |
| % |
| % const TypeInfo **GetTypeInfoList(const char *pattern, |
| % size_t *number_fonts,ExceptionInfo *exception) |
| % |
| % A description of each parameter follows: |
| % |
| % o pattern: Specifies a pointer to a text string containing a pattern. |
| % |
| % o number_fonts: This integer returns the number of types in the list. |
| % |
| % o exception: return any errors or warnings in this structure. |
| % |
| */ |
| |
| #if defined(__cplusplus) || defined(c_plusplus) |
| extern "C" { |
| #endif |
| |
| static int TypeInfoCompare(const void *x,const void *y) |
| { |
| const TypeInfo |
| **p, |
| **q; |
| |
| p=(const TypeInfo **) x, |
| q=(const TypeInfo **) y; |
| if (LocaleCompare((*p)->path,(*q)->path) == 0) |
| return(LocaleCompare((*p)->name,(*q)->name)); |
| return(LocaleCompare((*p)->path,(*q)->path)); |
| } |
| |
| #if defined(__cplusplus) || defined(c_plusplus) |
| } |
| #endif |
| |
| MagickExport const TypeInfo **GetTypeInfoList(const char *pattern, |
| size_t *number_fonts,ExceptionInfo *exception) |
| { |
| const TypeInfo |
| **fonts; |
| |
| const TypeInfo |
| *p; |
| |
| ssize_t |
| i; |
| |
| /* |
| Allocate type list. |
| */ |
| assert(pattern != (char *) NULL); |
| (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern); |
| assert(number_fonts != (size_t *) NULL); |
| *number_fonts=0; |
| p=GetTypeInfo("*",exception); |
| if (p == (const TypeInfo *) NULL) |
| return((const TypeInfo **) NULL); |
| fonts=(const TypeInfo **) AcquireQuantumMemory((size_t) |
| GetNumberOfNodesInSplayTree(type_cache)+1UL,sizeof(*fonts)); |
| if (fonts == (const TypeInfo **) NULL) |
| return((const TypeInfo **) NULL); |
| /* |
| Generate type list. |
| */ |
| LockSemaphoreInfo(type_semaphore); |
| ResetSplayTreeIterator(type_cache); |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| for (i=0; p != (const TypeInfo *) NULL; ) |
| { |
| if ((p->stealth == MagickFalse) && |
| (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse)) |
| fonts[i++]=p; |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| } |
| UnlockSemaphoreInfo(type_semaphore); |
| qsort((void *) fonts,(size_t) i,sizeof(*fonts),TypeInfoCompare); |
| fonts[i]=(TypeInfo *) NULL; |
| *number_fonts=(size_t) i; |
| return(fonts); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % G e t T y p e L i s t % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % GetTypeList() returns any fonts that match the specified pattern. |
| % |
| % The format of the GetTypeList function is: |
| % |
| % char **GetTypeList(const char *pattern,size_t *number_fonts, |
| % ExceptionInfo *exception) |
| % |
| % A description of each parameter follows: |
| % |
| % o pattern: Specifies a pointer to a text string containing a pattern. |
| % |
| % o number_fonts: This integer returns the number of fonts in the list. |
| % |
| % o exception: return any errors or warnings in this structure. |
| % |
| */ |
| |
| #if defined(__cplusplus) || defined(c_plusplus) |
| extern "C" { |
| #endif |
| |
| static int TypeCompare(const void *x,const void *y) |
| { |
| const char |
| **p, |
| **q; |
| |
| p=(const char **) x; |
| q=(const char **) y; |
| return(LocaleCompare(*p,*q)); |
| } |
| |
| #if defined(__cplusplus) || defined(c_plusplus) |
| } |
| #endif |
| |
| MagickExport char **GetTypeList(const char *pattern,size_t *number_fonts, |
| ExceptionInfo *exception) |
| { |
| char |
| **fonts; |
| |
| const TypeInfo |
| *p; |
| |
| ssize_t |
| i; |
| |
| /* |
| Allocate type list. |
| */ |
| assert(pattern != (char *) NULL); |
| (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern); |
| assert(number_fonts != (size_t *) NULL); |
| *number_fonts=0; |
| p=GetTypeInfo("*",exception); |
| if (p == (const TypeInfo *) NULL) |
| return((char **) NULL); |
| fonts=(char **) AcquireQuantumMemory((size_t) |
| GetNumberOfNodesInSplayTree(type_cache)+1UL,sizeof(*fonts)); |
| if (fonts == (char **) NULL) |
| return((char **) NULL); |
| /* |
| Generate type list. |
| */ |
| LockSemaphoreInfo(type_semaphore); |
| ResetSplayTreeIterator(type_cache); |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| for (i=0; p != (const TypeInfo *) NULL; ) |
| { |
| if ((p->stealth == MagickFalse) && |
| (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse)) |
| fonts[i++]=ConstantString(p->name); |
| p=(const TypeInfo *) GetNextValueInSplayTree(type_cache); |
| } |
| UnlockSemaphoreInfo(type_semaphore); |
| qsort((void *) fonts,(size_t) i,sizeof(*fonts),TypeCompare); |
| fonts[i]=(char *) NULL; |
| *number_fonts=(size_t) i; |
| return(fonts); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| + I s T y p e T r e e I n s t a n t i a t e d % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % IsTypeTreeInstantiated() determines if the type tree is instantiated. If |
| % not, it instantiates the tree and returns it. |
| % |
| % The format of the IsTypeInstantiated method is: |
| % |
| % MagickBooleanType IsTypeTreeInstantiated(ExceptionInfo *exception) |
| % |
| % A description of each parameter follows. |
| % |
| % o exception: return any errors or warnings in this structure. |
| % |
| */ |
| |
| #if defined(MAGICKCORE_FONTCONFIG_DELEGATE) |
| MagickExport MagickBooleanType LoadFontConfigFonts(SplayTreeInfo *type_cache, |
| ExceptionInfo *exception) |
| { |
| #if !defined(FC_FULLNAME) |
| #define FC_FULLNAME "fullname" |
| #endif |
| |
| char |
| extension[MagickPathExtent], |
| name[MagickPathExtent]; |
| |
| FcBool |
| result; |
| |
| FcChar8 |
| *family, |
| *file, |
| *fullname, |
| *style; |
| |
| FcConfig |
| *font_config; |
| |
| FcFontSet |
| *font_set; |
| |
| FcObjectSet |
| *object_set; |
| |
| FcPattern |
| *pattern; |
| |
| FcResult |
| status; |
| |
| int |
| slant, |
| width, |
| weight; |
| |
| ssize_t |
| i; |
| |
| TypeInfo |
| *type_info; |
| |
| /* |
| Load system fonts. |
| */ |
| (void) exception; |
| result=FcInit(); |
| if (result == 0) |
| return(MagickFalse); |
| font_config=FcConfigGetCurrent(); |
| if (font_config == (FcConfig *) NULL) |
| return(MagickFalse); |
| FcConfigSetRescanInterval(font_config,0); |
| font_set=(FcFontSet *) NULL; |
| object_set=FcObjectSetBuild(FC_FULLNAME,FC_FAMILY,FC_STYLE,FC_SLANT, |
| FC_WIDTH,FC_WEIGHT,FC_FILE,(char *) NULL); |
| if (object_set != (FcObjectSet *) NULL) |
| { |
| pattern=FcPatternCreate(); |
| if (pattern != (FcPattern *) NULL) |
| { |
| font_set=FcFontList(font_config,pattern,object_set); |
| FcPatternDestroy(pattern); |
| } |
| FcObjectSetDestroy(object_set); |
| } |
| if (font_set == (FcFontSet *) NULL) |
| { |
| FcConfigDestroy(font_config); |
| return(MagickFalse); |
| } |
| for (i=0; i < (ssize_t) font_set->nfont; i++) |
| { |
| status=FcPatternGetString(font_set->fonts[i],FC_FAMILY,0,&family); |
| if (status != FcResultMatch) |
| continue; |
| status=FcPatternGetString(font_set->fonts[i],FC_FILE,0,&file); |
| if (status != FcResultMatch) |
| continue; |
| *extension='\0'; |
| GetPathComponent((const char *) file,ExtensionPath,extension); |
| if ((*extension != '\0') && (LocaleCompare(extension,"gz") == 0)) |
| continue; |
| type_info=(TypeInfo *) AcquireMagickMemory(sizeof(*type_info)); |
| if (type_info == (TypeInfo *) NULL) |
| continue; |
| (void) memset(type_info,0,sizeof(*type_info)); |
| type_info->path=ConstantString("System Fonts"); |
| type_info->signature=MagickCoreSignature; |
| (void) CopyMagickString(name,"Unknown",MagickPathExtent); |
| status=FcPatternGetString(font_set->fonts[i],FC_FULLNAME,0,&fullname); |
| if ((status == FcResultMatch) && (fullname != (FcChar8 *) NULL)) |
| (void) CopyMagickString(name,(const char *) fullname,MagickPathExtent); |
| else |
| { |
| if (family != (FcChar8 *) NULL) |
| (void) CopyMagickString(name,(const char *) family,MagickPathExtent); |
| status=FcPatternGetString(font_set->fonts[i],FC_STYLE,0,&style); |
| if ((status == FcResultMatch) && (style != (FcChar8 *) NULL) && |
| (LocaleCompare((const char *) style,"Regular") != 0)) |
| { |
| (void) ConcatenateMagickString(name," ",MagickPathExtent); |
| (void) ConcatenateMagickString(name,(const char *) style, |
| MagickPathExtent); |
| } |
| } |
| type_info->name=ConstantString(name); |
| (void) SubstituteString(&type_info->name," ","-"); |
| type_info->family=ConstantString((const char *) family); |
| status=FcPatternGetInteger(font_set->fonts[i],FC_SLANT,0,&slant); |
| type_info->style=NormalStyle; |
| if (slant == FC_SLANT_ITALIC) |
| type_info->style=ItalicStyle; |
| if (slant == FC_SLANT_OBLIQUE) |
| type_info->style=ObliqueStyle; |
| status=FcPatternGetInteger(font_set->fonts[i],FC_WIDTH,0,&width); |
| type_info->stretch=NormalStretch; |
| if (width >= FC_WIDTH_ULTRACONDENSED) |
| type_info->stretch=UltraCondensedStretch; |
| if (width >= FC_WIDTH_EXTRACONDENSED) |
| type_info->stretch=ExtraCondensedStretch; |
| if (width >= FC_WIDTH_CONDENSED) |
| type_info->stretch=CondensedStretch; |
| if (width >= FC_WIDTH_SEMICONDENSED) |
| type_info->stretch=SemiCondensedStretch; |
| if (width >= FC_WIDTH_NORMAL) |
| type_info->stretch=NormalStretch; |
| if (width >= FC_WIDTH_SEMIEXPANDED) |
| type_info->stretch=SemiExpandedStretch; |
| if (width >= FC_WIDTH_EXPANDED) |
| type_info->stretch=ExpandedStretch; |
| if (width >= FC_WIDTH_EXTRAEXPANDED) |
| type_info->stretch=ExtraExpandedStretch; |
| if (width >= FC_WIDTH_ULTRAEXPANDED) |
| type_info->stretch=UltraExpandedStretch; |
| type_info->weight=400; |
| status=FcPatternGetInteger(font_set->fonts[i],FC_WEIGHT,0,&weight); |
| if (weight >= FC_WEIGHT_THIN) |
| type_info->weight=100; |
| if (weight >= FC_WEIGHT_EXTRALIGHT) |
| type_info->weight=200; |
| if (weight >= FC_WEIGHT_LIGHT) |
| type_info->weight=300; |
| if (weight >= FC_WEIGHT_NORMAL) |
| type_info->weight=400; |
| if (weight >= FC_WEIGHT_MEDIUM) |
| type_info->weight=500; |
| if (weight >= FC_WEIGHT_DEMIBOLD) |
| type_info->weight=600; |
| if (weight >= FC_WEIGHT_BOLD) |
| type_info->weight=700; |
| if (weight >= FC_WEIGHT_EXTRABOLD) |
| type_info->weight=800; |
| if (weight >= FC_WEIGHT_BLACK) |
| type_info->weight=900; |
| type_info->glyphs=ConstantString((const char *) file); |
| (void) AddValueToSplayTree(type_cache,type_info->name,type_info); |
| } |
| FcFontSetDestroy(font_set); |
| FcConfigDestroy(font_config); |
| return(MagickTrue); |
| } |
| #endif |
| |
| static MagickBooleanType IsTypeTreeInstantiated(ExceptionInfo *exception) |
| { |
| if (type_cache == (SplayTreeInfo *) NULL) |
| { |
| if (type_semaphore == (SemaphoreInfo *) NULL) |
| ActivateSemaphoreInfo(&type_semaphore); |
| LockSemaphoreInfo(type_semaphore); |
| if (type_cache == (SplayTreeInfo *) NULL) |
| { |
| SplayTreeInfo |
| *splay_tree; |
| |
| splay_tree=AcquireTypeCache(MagickTypeFilename,exception); |
| #if defined(MAGICKCORE_WINDOWS_SUPPORT) |
| (void) NTAcquireTypeCache(splay_tree,exception); |
| #endif |
| #if defined(MAGICKCORE_FONTCONFIG_DELEGATE) |
| (void) LoadFontConfigFonts(splay_tree,exception); |
| #endif |
| type_cache=splay_tree; |
| } |
| UnlockSemaphoreInfo(type_semaphore); |
| } |
| return(type_cache != (SplayTreeInfo *) NULL ? MagickTrue : MagickFalse); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % L i s t T y p e I n f o % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % ListTypeInfo() lists the fonts to a file. |
| % |
| % The format of the ListTypeInfo method is: |
| % |
| % MagickBooleanType ListTypeInfo(FILE *file,ExceptionInfo *exception) |
| % |
| % A description of each parameter follows. |
| % |
| % o file: An pointer to a FILE. |
| % |
| % o exception: return any errors or warnings in this structure. |
| % |
| */ |
| MagickExport MagickBooleanType ListTypeInfo(FILE *file,ExceptionInfo *exception) |
| { |
| char |
| weight[MagickPathExtent]; |
| |
| const char |
| *family, |
| *glyphs, |
| *name, |
| *path, |
| *stretch, |
| *style; |
| |
| const TypeInfo |
| **type_info; |
| |
| ssize_t |
| i; |
| |
| size_t |
| number_fonts; |
| |
| if (file == (FILE *) NULL) |
| file=stdout; |
| number_fonts=0; |
| type_info=GetTypeInfoList("*",&number_fonts,exception); |
| if (type_info == (const TypeInfo **) NULL) |
| return(MagickFalse); |
| *weight='\0'; |
| path=(const char *) NULL; |
| for (i=0; i < (ssize_t) number_fonts; i++) |
| { |
| if (type_info[i]->stealth != MagickFalse) |
| continue; |
| if (((path == (const char *) NULL) || |
| (LocaleCompare(path,type_info[i]->path) != 0)) && |
| (type_info[i]->path != (char *) NULL)) |
| (void) FormatLocaleFile(file,"\nPath: %s\n",type_info[i]->path); |
| path=type_info[i]->path; |
| name="unknown"; |
| if (type_info[i]->name != (char *) NULL) |
| name=type_info[i]->name; |
| family="unknown"; |
| if (type_info[i]->family != (char *) NULL) |
| family=type_info[i]->family; |
| style=CommandOptionToMnemonic(MagickStyleOptions,type_info[i]->style); |
| stretch=CommandOptionToMnemonic(MagickStretchOptions,type_info[i]->stretch); |
| glyphs="unknown"; |
| if (type_info[i]->glyphs != (char *) NULL) |
| glyphs=type_info[i]->glyphs; |
| (void) FormatLocaleString(weight,MagickPathExtent,"%.20g",(double) |
| type_info[i]->weight); |
| (void) FormatLocaleFile(file," Font: %s\n",name); |
| (void) FormatLocaleFile(file," family: %s\n",family); |
| (void) FormatLocaleFile(file," style: %s\n",style); |
| (void) FormatLocaleFile(file," stretch: %s\n",stretch); |
| (void) FormatLocaleFile(file," weight: %s\n",weight); |
| (void) FormatLocaleFile(file," glyphs: %s\n",glyphs); |
| } |
| (void) fflush(file); |
| type_info=(const TypeInfo **) RelinquishMagickMemory((void *) type_info); |
| return(MagickTrue); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| + L o a d T y p e C a c h e % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % LoadTypeCache() loads the type configurations which provides a mapping |
| % between type attributes and a type name. |
| % |
| % The format of the LoadTypeCache method is: |
| % |
| % MagickBooleanType LoadTypeCache(SplayTreeInfo *cache,const char *xml, |
| % const char *filename,const size_t depth,ExceptionInfo *exception) |
| % |
| % A description of each parameter follows: |
| % |
| % o xml: The type list in XML format. |
| % |
| % o filename: The type list filename. |
| % |
| % o depth: depth of <include /> statements. |
| % |
| % o exception: return any errors or warnings in this structure. |
| % |
| */ |
| |
| static inline MagickBooleanType SetTypeNodePath(const char *filename, |
| char *font_path,const char *token,char **target) |
| { |
| char |
| *path; |
| |
| path=ConstantString(token); |
| #if defined(MAGICKCORE_WINDOWS_SUPPORT) |
| if (strchr(path,'@') != (char *) NULL) |
| SubstituteString(&path,"@ghostscript_font_path@",font_path); |
| #endif |
| if (IsPathAccessible(path) == MagickFalse) |
| { |
| /* |
| Relative path. |
| */ |
| path=DestroyString(path); |
| GetPathComponent(filename,HeadPath,font_path); |
| (void) ConcatenateMagickString(font_path,DirectorySeparator, |
| MagickPathExtent); |
| (void) ConcatenateMagickString(font_path,token,MagickPathExtent); |
| path=ConstantString(font_path); |
| #if defined(MAGICKCORE_WINDOWS_SUPPORT) |
| if (strchr(path,'@') != (char *) NULL) |
| SubstituteString(&path,"@ghostscript_font_path@",""); |
| #endif |
| if (IsPathAccessible(path) == MagickFalse) |
| { |
| path=DestroyString(path); |
| return(MagickFalse); |
| } |
| } |
| |
| *target=path; |
| return(MagickTrue); |
| } |
| |
| static MagickBooleanType LoadTypeCache(SplayTreeInfo *cache,const char *xml, |
| const char *filename,const size_t depth,ExceptionInfo *exception) |
| { |
| char |
| font_path[MagickPathExtent], |
| keyword[MagickPathExtent], |
| *token; |
| |
| const char |
| *q; |
| |
| MagickStatusType |
| status; |
| |
| size_t |
| extent; |
| |
| TypeInfo |
| *type_info; |
| |
| /* |
| Load the type map file. |
| */ |
| (void) LogMagickEvent(ConfigureEvent,GetMagickModule(), |
| "Loading type configure file \"%s\" ...",filename); |
| if (xml == (const char *) NULL) |
| return(MagickFalse); |
| status=MagickTrue; |
| type_info=(TypeInfo *) NULL; |
| token=AcquireString(xml); |
| extent=strlen(token)+MagickPathExtent; |
| #if defined(MAGICKCORE_WINDOWS_SUPPORT) |
| /* |
| Determine the Ghostscript font path. |
| */ |
| *font_path='\0'; |
| if (NTGhostscriptFonts(font_path,MagickPathExtent-2)) |
| (void) ConcatenateMagickString(font_path,DirectorySeparator,MagickPathExtent); |
| #endif |
| for (q=(char *) xml; *q != '\0'; ) |
| { |
| /* |
| Interpret XML. |
| */ |
| (void) GetNextToken(q,&q,extent,token); |
| if (*token == '\0') |
| break; |
| (void) CopyMagickString(keyword,token,MagickPathExtent); |
| if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0) |
| { |
| /* |
| Doctype element. |
| */ |
| while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0')) |
| (void) GetNextToken(q,&q,extent,token); |
| continue; |
| } |
| if (LocaleNCompare(keyword,"<!--",4) == 0) |
| { |
| /* |
| Comment element. |
| */ |
| while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0')) |
| (void) GetNextToken(q,&q,extent,token); |
| continue; |
| } |
| if (LocaleCompare(keyword,"<include") == 0) |
| { |
| /* |
| Include element. |
| */ |
| while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0')) |
| { |
| (void) CopyMagickString(keyword,token,MagickPathExtent); |
| (void) GetNextToken(q,&q,extent,token); |
| if (*token != '=') |
| continue; |
| (void) GetNextToken(q,&q,extent,token); |
| if (LocaleCompare(keyword,"file") == 0) |
| { |
| if (depth > MagickMaxRecursionDepth) |
| (void) ThrowMagickException(exception,GetMagickModule(), |
| ConfigureError,"IncludeNodeNestedTooDeeply","`%s'",token); |
| else |
| { |
| char |
| path[MagickPathExtent], |
| *file_xml; |
| |
| ExceptionInfo |
| *sans_exception; |
| |
| *path='\0'; |
| GetPathComponent(filename,HeadPath,path); |
| if (*path != '\0') |
| (void) ConcatenateMagickString(path,DirectorySeparator, |
| MagickPathExtent); |
| if (*token == *DirectorySeparator) |
| (void) CopyMagickString(path,token,MagickPathExtent); |
| else |
| (void) ConcatenateMagickString(path,token,MagickPathExtent); |
| sans_exception=AcquireExceptionInfo(); |
| file_xml=FileToString(path,~0UL,sans_exception); |
| sans_exception=DestroyExceptionInfo(sans_exception); |
| if (file_xml != (char *) NULL) |
| { |
| status&=LoadTypeCache(cache,file_xml,path,depth+1, |
| exception); |
| file_xml=(char *) RelinquishMagickMemory(file_xml); |
| } |
| } |
| } |
| } |
| continue; |
| } |
| if (LocaleCompare(keyword,"<type") == 0) |
| { |
| /* |
| Type element. |
| */ |
| type_info=(TypeInfo *) AcquireCriticalMemory(sizeof(*type_info)); |
| (void) memset(type_info,0,sizeof(*type_info)); |
| type_info->path=ConstantString(filename); |
| type_info->signature=MagickCoreSignature; |
| continue; |
| } |
| if (type_info == (TypeInfo *) NULL) |
| continue; |
| if ((LocaleCompare(keyword,"/>") == 0) || |
| (LocaleCompare(keyword,"</policy>") == 0)) |
| { |
| status=AddValueToSplayTree(cache,type_info->name,type_info); |
| if (status == MagickFalse) |
| (void) ThrowMagickException(exception,GetMagickModule(), |
| ResourceLimitError,"MemoryAllocationFailed","`%s'",type_info->name); |
| type_info=(TypeInfo *) NULL; |
| continue; |
| } |
| (void) GetNextToken(q,(const char **) NULL,extent,token); |
| if (*token != '=') |
| continue; |
| (void) GetNextToken(q,&q,extent,token); |
| (void) GetNextToken(q,&q,extent,token); |
| switch (*keyword) |
| { |
| case 'E': |
| case 'e': |
| { |
| if (LocaleCompare((char *) keyword,"encoding") == 0) |
| { |
| type_info->encoding=ConstantString(token); |
| break; |
| } |
| break; |
| } |
| case 'F': |
| case 'f': |
| { |
| if (LocaleCompare((char *) keyword,"face") == 0) |
| { |
| type_info->face=StringToUnsignedLong(token); |
| break; |
| } |
| if (LocaleCompare((char *) keyword,"family") == 0) |
| { |
| type_info->family=ConstantString(token); |
| break; |
| } |
| if (LocaleCompare((char *) keyword,"format") == 0) |
| { |
| type_info->format=ConstantString(token); |
| break; |
| } |
| if (LocaleCompare((char *) keyword,"foundry") == 0) |
| { |
| type_info->foundry=ConstantString(token); |
| break; |
| } |
| if (LocaleCompare((char *) keyword,"fullname") == 0) |
| { |
| type_info->description=ConstantString(token); |
| break; |
| } |
| break; |
| } |
| case 'G': |
| case 'g': |
| { |
| if (LocaleCompare((char *) keyword,"glyphs") == 0) |
| { |
| if (SetTypeNodePath(filename,font_path,token,&type_info->glyphs) == |
| MagickFalse) |
| type_info=(TypeInfo *) DestroyTypeNode(type_info); |
| break; |
| } |
| break; |
| } |
| case 'M': |
| case 'm': |
| { |
| if (LocaleCompare((char *) keyword,"metrics") == 0) |
| { |
| if (SetTypeNodePath(filename,font_path,token,&type_info->metrics) == |
| MagickFalse) |
| type_info=(TypeInfo *) DestroyTypeNode(type_info); |
| break; |
| } |
| break; |
| } |
| case 'N': |
| case 'n': |
| { |
| if (LocaleCompare((char *) keyword,"name") == 0) |
| { |
| type_info->name=ConstantString(token); |
| break; |
| } |
| break; |
| } |
| case 'S': |
| case 's': |
| { |
| if (LocaleCompare((char *) keyword,"stealth") == 0) |
| { |
| type_info->stealth=IsStringTrue(token); |
| break; |
| } |
| if (LocaleCompare((char *) keyword,"stretch") == 0) |
| { |
| type_info->stretch=(StretchType) ParseCommandOption( |
| MagickStretchOptions,MagickFalse,token); |
| break; |
| } |
| if (LocaleCompare((char *) keyword,"style") == 0) |
| { |
| type_info->style=(StyleType) ParseCommandOption(MagickStyleOptions, |
| MagickFalse,token); |
| break; |
| } |
| break; |
| } |
| case 'W': |
| case 'w': |
| { |
| if (LocaleCompare((char *) keyword,"weight") == 0) |
| { |
| ssize_t |
| weight; |
| |
| weight=ParseCommandOption(MagickWeightOptions,MagickFalse,token); |
| if (weight == -1) |
| weight=(ssize_t) StringToUnsignedLong(token); |
| type_info->weight=(size_t) weight; |
| break; |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| token=(char *) RelinquishMagickMemory(token); |
| return(status != 0 ? MagickTrue : MagickFalse); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| + T y p e C o m p o n e n t G e n e s i s % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % TypeComponentGenesis() instantiates the type component. |
| % |
| % The format of the TypeComponentGenesis method is: |
| % |
| % MagickBooleanType TypeComponentGenesis(void) |
| % |
| */ |
| MagickPrivate MagickBooleanType TypeComponentGenesis(void) |
| { |
| if (type_semaphore == (SemaphoreInfo *) NULL) |
| type_semaphore=AcquireSemaphoreInfo(); |
| return(MagickTrue); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| + T y p e C o m p o n e n t T e r m i n u s % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % TypeComponentTerminus() destroy type component. |
| % |
| % The format of the TypeComponentTerminus method is: |
| % |
| % void TypeComponentTerminus(void) |
| % |
| */ |
| MagickPrivate void TypeComponentTerminus(void) |
| { |
| if (type_semaphore == (SemaphoreInfo *) NULL) |
| ActivateSemaphoreInfo(&type_semaphore); |
| LockSemaphoreInfo(type_semaphore); |
| if (type_cache != (SplayTreeInfo *) NULL) |
| type_cache=DestroySplayTree(type_cache); |
| UnlockSemaphoreInfo(type_semaphore); |
| RelinquishSemaphoreInfo(&type_semaphore); |
| } |