/*
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
% %
|
% %
|
% %
|
% 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-2019 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 = (const char *)
|
"<?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)
|
{
|
register 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 !defined(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,
|
*substitute;
|
} Fontmap;
|
|
const TypeInfo
|
*type_info;
|
|
register const TypeInfo
|
*p;
|
|
register 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" },
|
{ NULL, NULL }
|
};
|
|
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; fontmap[i].name != (char *) NULL; 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(),TypeError,
|
"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;
|
|
register const TypeInfo
|
*p;
|
|
register 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)
|
{
|
register 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;
|
|
register const TypeInfo
|
*p;
|
|
register 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;
|
|
register 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;
|
|
register 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.
|
*/
|
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'))
|
GetNextToken(q,&q,extent,token);
|
continue;
|
}
|
if (LocaleNCompare(keyword,"<!--",4) == 0)
|
{
|
/*
|
Comment element.
|
*/
|
while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
|
GetNextToken(q,&q,extent,token);
|
continue;
|
}
|
if (LocaleCompare(keyword,"<include") == 0)
|
{
|
/*
|
Include element.
|
*/
|
while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
|
{
|
(void) CopyMagickString(keyword,token,MagickPathExtent);
|
GetNextToken(q,&q,extent,token);
|
if (*token != '=')
|
continue;
|
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;
|
}
|
GetNextToken(q,(const char **) NULL,extent,token);
|
if (*token != '=')
|
continue;
|
GetNextToken(q,&q,extent,token);
|
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);
|
}
|