/*
% Copyright (C) 2003-2019 GraphicsMagick Group
% Copyright (C) 2002 ImageMagick Studio
%
% This program is covered by multiple licenses, which are described in
% Copyright.txt. You should have received a copy of Copyright.txt with this
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%                        TTTTT  Y   Y  PPPP   EEEEE                           %
%                          T     Y Y   P   P  E                               %
%                          T      Y    PPPP   EEE                             %
%                          T      Y    P      E                               %
%                          T      Y    P      EEEEE                           %
%                                                                             %
%                                                                             %
%                     GraphicsMagick Image Type Methods                       %
%                                                                             %
%                                                                             %
%                              Software Design                                %
%                                John Cristy                                  %
%                                 May 2001                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
*/

/*
  Include declarations.
*/
#include "magick/studio.h"
#if defined(MSWINDOWS) || defined(__CYGWIN__)
# include "magick/nt_feature.h"
#endif
#include "magick/blob.h"
#include "magick/enum_strings.h"
#include "magick/log.h"
#include "magick/render.h"
#include "magick/semaphore.h"
#include "magick/type.h"
#include "magick/utility.h"

/*
  Define declarations.
*/
#define TypeFilename  "type.mgk"

/*
  Declare type map.
*/
static const char
  TypeMap[] =
    "<?xml version=\"1.0\"?>"
    "<typemap>"
    "  <type stealth=\"True\" />"
    "</typemap>";

/*
  Static declarations.
*/
static SemaphoreInfo
  *type_semaphore = (SemaphoreInfo *) NULL;

static TypeInfo
  *type_list = (TypeInfo *) NULL;

/*
  Forward declarations.
*/
static unsigned int
  ReadTypeConfigureFile(const char *,const unsigned long,ExceptionInfo *);

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
+   D e s t r o y T y p e I n f o                                             %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method DestroyTypeInfo deallocates memory associated with the font list.
%
%  The format of the DestroyTypeInfo method is:
%
%      DestroyTypeInfo(void)
%
%
*/
MagickExport void DestroyTypeInfo(void)
{
  register TypeInfo
    *p;

  TypeInfo
    *type_info;

  for (p=type_list; p != (TypeInfo *) NULL; )
  {
    type_info=p;
    p=p->next;
    if (type_info->path != (char *) NULL)
      MagickFreeMemory(type_info->path);
    if (type_info->name != (char *) NULL)
      MagickFreeMemory(type_info->name);
    if (type_info->description != (char *) NULL)
      MagickFreeMemory(type_info->description);
    if (type_info->family != (char *) NULL)
      MagickFreeMemory(type_info->family);
    if (type_info->encoding != (char *) NULL)
      MagickFreeMemory(type_info->encoding);
    if (type_info->foundry != (char *) NULL)
      MagickFreeMemory(type_info->foundry);
    if (type_info->format != (char *) NULL)
      MagickFreeMemory(type_info->format);
    if (type_info->metrics != (char *) NULL)
      MagickFreeMemory(type_info->metrics);
    if (type_info->glyphs != (char *) NULL)
      MagickFreeMemory(type_info->glyphs);
    MagickFreeMemory(type_info);
  }
  type_list=(TypeInfo *) NULL;
  DestroySemaphoreInfo(&type_semaphore);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
+   G e t T y p e I n f o                                                     %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method 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 type_info: Method GetTypeInfo searches the type list for the specified
%      name and if found returns attributes for that type.
%
%    o name: The type name.
%
%    o exception: Return any errors or warnings in this structure.
%
%
*/
MagickExport const TypeInfo *GetTypeInfo(const char *name,
  ExceptionInfo *exception)
{
  register TypeInfo
    *p;

  if (type_list == (TypeInfo *) NULL)
    {
      LockSemaphoreInfo(type_semaphore);
      if (type_list == (TypeInfo *) NULL)
        {
          (void) ReadTypeConfigureFile(TypeFilename,0,exception);
#if defined(MSWINDOWS) || defined(__CYGWIN__)
          {
            TypeInfo
              *type_info;

            type_info=NTGetTypeList();
            if (type_info != (TypeInfo *) NULL)
              {
                if (type_list == (TypeInfo *) NULL)
                  type_list=type_info;
                else
                  {
                    while (type_list->next != (TypeInfo *) NULL)
                      type_list=type_list->next;
                    type_list->next=type_info;
                    type_info->previous=type_list;
                    while (type_list->previous != (TypeInfo *) NULL)
                      type_list=type_list->previous;
                  }
              }
          }
#endif
        }
      UnlockSemaphoreInfo(type_semaphore);
    }
  if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
    return((const TypeInfo *) type_list);
  /*
    Search for requested type.
  */
  LockSemaphoreInfo(type_semaphore);
  for (p=type_list; p != (TypeInfo *) NULL; p=p->next)
    if ((p->name != (char *) NULL) && (LocaleCompare(p->name,name) == 0))
      break;
  if (p != (TypeInfo *) NULL)
    if (p != type_list)
      {
        /*
          Self-adjusting list.
        */
        if (p->previous != (TypeInfo *) NULL)
          p->previous->next=p->next;
        if (p->next != (TypeInfo *) NULL)
          p->next->previous=p->previous;
        p->previous=(TypeInfo *) NULL;
        p->next=type_list;
        type_list->previous=p;
        type_list=p;
      }
  UnlockSemaphoreInfo(type_semaphore);
  return((const TypeInfo *) p);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
+   G e t T y p e I n f o B y F a m i l y                                     %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method 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 unsigned long weight,ExceptionInfo *exception)
%
%  A description of each parameter follows:
%
%    o type_info: Method GetTypeInfo searches the type list for the specified
%      name and if found returns attributes for that type.
%
%    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 unsigned long weight,
  ExceptionInfo *exception)
{
  const TypeInfo
    *type_info;

  int
    range;

  register const TypeInfo
    *p;

  register unsigned int
    i;

  static const struct
  {
    char
      name[17],
      substitute[10];
  }
  fontmap[] =
    {
      { "fixed", "courier" },
      { "modern","courier" },
      { "monotype corsiva", "courier" },
      { "news gothic", "helvetica" },
      { "system", "courier" },
      { "terminal", "courier" },
      { "wingdings", "symbol" }
    };

  unsigned int
    max_score,
    score;

  /*
    Check for an exact type match.
  */
  (void) GetTypeInfo("*",exception);
  if (type_list == (TypeInfo *) NULL)
    return((TypeInfo *) NULL);
  for (p=type_list; p != (TypeInfo *) NULL; p=p->next)
  {
    if (p->family == (char *) NULL)
      continue;
    if (family == (const char *) NULL)
      {
        if ((LocaleCompare(p->family,"arial") != 0) &&
            (LocaleCompare(p->family,"helvetica") != 0))
          continue;
      }
    else
      if (LocaleCompare(p->family,family) != 0)
        continue;
    if ((style != AnyStyle) && (p->style != style))
      continue;
    if ((stretch != AnyStretch) && (p->stretch != stretch))
      continue;
    if ((weight != 0) && (p->weight != weight))
      continue;
    return((TypeInfo *) p);
  }
  /*
    Check for types in the same family.
  */
  max_score=0;
  type_info=(TypeInfo *) NULL;
  for (p=type_list; p != (TypeInfo *) NULL; p=p->next)
  {
    if (p->family == (char *) NULL)
      continue;
    if (family == (const char *) NULL)
      {
        if ((LocaleCompare(p->family,"arial") != 0) &&
            (LocaleCompare(p->family,"helvetica") != 0))
          continue;
      }
    else
      if (LocaleCompare(p->family,family) != 0)
        continue;
    score=0;
    if ((style == AnyStyle) || (p->style == style))
      score+=32;
    else
      if (((style == ItalicStyle) || (style == ObliqueStyle)) &&
          ((p->style == ItalicStyle) || (p->style == ObliqueStyle)))
        score+=25;
    if (weight == 0)
      score+=16;
    else
      score+=(16*(800-((int) Max(Min(weight,900),p->weight)-
        (int) Min(Min(weight,900),p->weight))))/800;
    if (stretch == AnyStretch)
      score+=8;
    else
      {
        range=(int) UltraExpandedStretch-(int) NormalStretch;
        score+=(8*(range-((int) Max(stretch,p->stretch)-
          (int) Min(stretch,p->stretch))))/range;
      }
    if (score > max_score)
      {
        max_score=score;
        type_info=p;
      }
  }
  if (type_info != (TypeInfo *) NULL)
    return((TypeInfo *) type_info);
  /*
    Check for table-based substitution match.
  */
  for (i=0; i < ArraySize(fontmap); 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 != (TypeInfo *) NULL)
    {
      ThrowException(exception,TypeError,FontSubstitutionRequired,
        type_info->family);
      return((TypeInfo *) type_info);
    }
  if (family != (const char *) NULL)
    type_info=GetTypeInfoByFamily((const char *) NULL,style,stretch,weight,
      exception);
  return((TypeInfo *) type_info);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   G e t T y p e l i s t                                                     %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  GetTypeList() returns any types that match the specified pattern.
%
%  The format of the GetTypeList function is:
%
%      char **GetTypeList(const char *pattern,unsigned long *number_types)
%
%  A description of each parameter follows:
%
%    o pattern: Specifies a pointer to a text string containing a pattern.
%
%    o number_types:  This integer returns the number of types in the list.
%
%
*/
MagickExport char **GetTypeList(const char *pattern,unsigned long *number_types)
{
  char
    **typelist;

  ExceptionInfo
    exception;

  register const TypeInfo
    *p;

  register long
    i;

  /*
    Allocate type list.
  */
  assert(pattern != (char *) NULL);
  assert(number_types != (unsigned long *) NULL);
  *number_types=0;
  GetExceptionInfo(&exception);
  p=GetTypeInfo("*",&exception);
  DestroyExceptionInfo(&exception);
  if (p == (const TypeInfo *) NULL)
    return((char **) NULL);
  i=0;
  for (p=type_list; p != (const TypeInfo *) NULL; p=p->next)
    i++;
  typelist=MagickAllocateMemory(char **,i*sizeof(char *));
  if (typelist == (char **) NULL)
    return((char **) NULL);
  i=0;
  for (p=type_list; p != (const TypeInfo *) NULL; p=p->next)
  {
    if (p->stealth)
      continue;
    if (GlobExpression(p->name,pattern))
      typelist[i++]=AllocateString(p->name);
  }
  *number_types=i;
  return(typelist);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
+   I n i t i a l i z e T y p e I n f o                                       %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method InitializeTypeInfo initializes the type facility
%
%  The format of the InitializeTypeInfo method is:
%
%      MagickPassFail InitializeTypeInfo(void)
%
%
*/
MagickPassFail
InitializeTypeInfo(void)
{
  assert(type_semaphore == (SemaphoreInfo *) NULL);
  type_semaphore=AllocateSemaphoreInfo();
  return MagickPass;
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%  L i s t T y p e I n f o                                                    %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Method ListTypeInfo lists the fonts to a file.
%
%  The format of the ListTypeInfo method is:
%
%      unsigned int 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 unsigned int ListTypeInfo(FILE *file,ExceptionInfo *exception)
{
  char
    weight[MaxTextExtent];

  const char
    *family,
    *name,
    *stretch,
    *style;

  register const TypeInfo
    *p;

  if (file == (FILE *) NULL)
    file=stdout;
  (void) GetTypeInfo("*",exception);
  LockSemaphoreInfo(type_semaphore);
  for (p=type_list; p != (const TypeInfo *) NULL; p=p->next)
  {
    if ((p->previous == (TypeInfo *) NULL) ||
        (LocaleCompare(p->path,p->previous->path) != 0))
      {
        if (p->previous != (TypeInfo *) NULL)
          (void) fprintf(file,"\n");
        if (p->path != (char *) NULL)
          (void) fprintf(file,"Path: %.1024s\n\n",p->path);
        (void) fprintf(file,"%-32.32s %-23.23s %-7.7s %-8s %-3s\n",
          "Name","Family","Style","Stretch","Weight");
        (void) fprintf(file,"--------------------------------------------------"
          "------------------------------\n");
      }
    if (p->stealth)
      continue;
    name="unknown";
    if (p->name != (char *) NULL)
      name=p->name;
    family="unknown";
    if (p->family != (char *) NULL)
      family=p->family;
    style=StyleTypeToString(p->style);
    stretch=StretchTypeToString(p->stretch);
    FormatString(weight,"%lu",p->weight);
    (void) fprintf(file,"%-32.32s %-23.23s %-7.7s %-9s %-3s\n",
      name,family,style,stretch,weight);
  }
  (void) fflush(file);
  UnlockSemaphoreInfo(type_semaphore);
  return(True);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
+   R e a d T y p e C o n f i g u r e F i l e                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  ReadTypeConfigureFile() reads the type configuration file which provides
%  a mapping between type attributes and font files.
%
%  The format of the ReadTypeConfigureFile method is:
%
%      unsigned int ReadTypeConfigureFile(const char *basename,
%        const unsigned long depth,ExceptionInfo *exception)
%
%  A description of each parameter follows:
%
%    o status: ReadTypeConfigureFile() returns True if at least one entry
%      is read, otherwise False is returned.
%
%    o basename:  The type configuration filename.
%
%    o depth: depth of <include /> statements.
%
%    o exception: Return any errors or warnings in this structure.
%
%
*/
static unsigned int ReadTypeConfigureFile(const char *basename,
  const unsigned long depth,ExceptionInfo *exception)
{
  char
    keyword[MaxTextExtent],
    path[MaxTextExtent],
    *q,
    *token,
    *xml;

#if defined(MSWINDOWS)
  char
    GhostscriptFontDir[MaxTextExtent];
#endif

  size_t
    length,
    token_max_length;

  /*
    Read the type configure file.
  */
  (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
    "File path=\"%.1024s\", recursion depth=%lu",basename,depth);
  (void) strlcpy(path,basename,sizeof(path));
#if defined(MSWINDOWS)
  /*
    For Windows, cache the location of the Ghostscript fonts.
  */
  GhostscriptFontDir[0]='\0';
  if (NTGhostscriptFonts(GhostscriptFontDir,MaxTextExtent-2))
    (void) strlcat(GhostscriptFontDir,DirectorySeparator,
                   sizeof(GhostscriptFontDir));
#endif
  if (depth == 0)
    {
      /*
        Load top configuration file based on configure search path.
      */
      xml=(char *) GetConfigureBlob(basename,path,&length,exception);
      if (xml == (char *) NULL)
        xml=AllocateString(TypeMap);
    }
  else
    {
      /*
        Load subordinate configuration file based on path specified
        by parent configuration file.
      */
      if (IsAccessibleAndNotEmpty(basename))
        xml=(char *) FileToBlob(basename,&length,exception);
      else
        {
          GetPathComponent(basename,TailPath,path);
          xml=(char *) GetConfigureBlob(path,keyword,&length,exception);
        }
      if (xml == (char *) NULL)
        return (False);
    }

  token=AllocateString(xml);
  token_max_length=strlen(token);
  for (q=xml; *q != '\0'; )
  {
    /*
      Interpret XML.
    */
    MagickGetToken(q,&q,token,token_max_length);
    if (*token == '\0')
      break;
    (void) strlcpy(keyword,token,MaxTextExtent);
    if (LocaleNCompare(keyword,"<!--",4) == 0)
      {
        /*
          Comment element.
        */
        while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
          MagickGetToken(q,&q,token,token_max_length);
        continue;
      }
    if (LocaleCompare(keyword,"<include") == 0)
      {
        /*
          Include element.
        */
        while ( (*token != '/' && *(token+1)  != '>') && (*q != '\0'))
        {
          (void) strlcpy(keyword,token,MaxTextExtent);
          MagickGetToken(q,&q,token,token_max_length);
          if (*token != '=')
            continue;
          MagickGetToken(q,&q,token,token_max_length);
          if (LocaleCompare(keyword,"file") == 0)
            {
              if (depth > 200)
                ThrowException(exception,ConfigureError,IncludeElementNestedTooDeeply,path);
              else
                {
                  char
                    filename[MaxTextExtent];

                  GetPathComponent(path,HeadPath,filename);
                  if (filename[0] != '\0')
                    (void) strlcat(filename,DirectorySeparator,sizeof(filename));
                  (void) strlcat(filename,token,sizeof(filename));
                  (void) ReadTypeConfigureFile(filename,depth+1,exception);
                }
              if (type_list != (TypeInfo *) NULL)
                while (type_list->next != (TypeInfo *) NULL)
                  type_list=type_list->next;
            }
        }
        continue;
      }
    if (LocaleCompare(keyword,"<type") == 0)
      {
        TypeInfo
          *type_info;

        /*
          Allocate memory for the type list.
        */
        type_info=MagickAllocateMemory(TypeInfo *,sizeof(TypeInfo));
        if (type_info == (TypeInfo *) NULL)
          MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed,
            UnableToAllocateTypeInfo);
        (void) memset(type_info,0,sizeof(TypeInfo));
        type_info->path=AcquireString(path);
        type_info->signature=MagickSignature;
        if (type_list == (TypeInfo *) NULL)
          {
            type_list=type_info;
            continue;
          }
        /*
          Append entry to end of type list
        */
        while (type_list->next != (TypeInfo *) NULL)
          type_list=type_list->next;
        type_list->next=type_info;
        type_info->previous=type_list;
        type_list=type_list->next;
        continue;
      }
    if (type_list == (TypeInfo *) NULL)
      continue;
    MagickGetToken(q,(char **) NULL,token,token_max_length);
    if (*token != '=')
      continue;
    MagickGetToken(q,&q,token,token_max_length);
    MagickGetToken(q,&q,token,token_max_length);
    switch (*keyword)
    {
      case 'E':
      case 'e':
      {
        if (LocaleCompare((char *) keyword,"encoding") == 0)
          {
            type_list->encoding=AcquireString(token);
            break;
          }
        break;
      }
      case 'F':
      case 'f':
      {
        if (LocaleCompare((char *) keyword,"family") == 0)
          {
            type_list->family=AcquireString(token);
            break;
          }
        if (LocaleCompare((char *) keyword,"format") == 0)
          {
            type_list->format=AcquireString(token);
            break;
          }
        if (LocaleCompare((char *) keyword,"foundry") == 0)
          {
            type_list->foundry=AcquireString(token);
            break;
          }
        if (LocaleCompare((char *) keyword,"fullname") == 0)
          {
            type_list->description=AcquireString(token);
            break;
          }
        break;
      }
      case 'G':
      case 'g':
      {
        if (LocaleCompare((char *) keyword,"glyphs") == 0)
          {
            char
              *glyphs;

            glyphs=(char *) NULL;
            (void) CloneString(&glyphs,token);
#if defined(MSWINDOWS)
            if (strchr(glyphs,'@') != (char *) NULL)
              SubstituteString(&glyphs,"@ghostscript_font_dir@",GhostscriptFontDir);
#endif
            type_list->glyphs=glyphs;
            break;
          }
        break;
      }
      case 'M':
      case 'm':
      {
        if (LocaleCompare((char *) keyword,"metrics") == 0)
          {
            char
              *metrics;

            metrics=AcquireString(token);
#if defined(MSWINDOWS)
            if (strchr(metrics,'@') != (char *) NULL)
              SubstituteString(&metrics,"@ghostscript_font_dir@",GhostscriptFontDir);
#endif
            type_list->metrics=metrics;
            break;
          }
        break;
      }
      case 'N':
      case 'n':
      {
        if (LocaleCompare((char *) keyword,"name") == 0)
          {
            type_list->name=AcquireString(token);
            break;
          }
        break;
      }
      case 'S':
      case 's':
      {
        if (LocaleCompare((char *) keyword,"stealth") == 0)
          {
            type_list->stealth=LocaleCompare(token,"True") == 0;
            break;
          }
        if (LocaleCompare((char *) keyword,"stretch") == 0)
          {
            if (LocaleCompare(token,"condensed") == 0)
              type_list->stretch=CondensedStretch;
            if (LocaleCompare(token,"expanded") == 0)
              type_list->stretch=ExpandedStretch;
            if (LocaleCompare(token,"extra-condensed") == 0)
              type_list->stretch=ExtraCondensedStretch;
            if (LocaleCompare(token,"extra-expanded") == 0)
              type_list->stretch=ExtraExpandedStretch;
            if (LocaleCompare(token,"normal") == 0)
              type_list->stretch=NormalStretch;
            if (LocaleCompare(token,"semi-condensed") == 0)
              type_list->stretch=SemiCondensedStretch;
            if (LocaleCompare(token,"semi-expanded") == 0)
              type_list->stretch=SemiExpandedStretch;
            if (LocaleCompare(token,"ultra-condensed") == 0)
              type_list->stretch=UltraCondensedStretch;
            if (LocaleCompare(token,"ultra-expanded") == 0)
              type_list->stretch=UltraExpandedStretch;
            break;
          }
        if (LocaleCompare((char *) keyword,"style") == 0)
          {
            if (LocaleCompare(token,"italic") == 0)
              type_list->style=ItalicStyle;
            if (LocaleCompare(token,"normal") == 0)
              type_list->style=NormalStyle;
            if (LocaleCompare(token,"oblique") == 0)
              type_list->style=ObliqueStyle;
            break;
          }
        break;
      }
      case 'W':
      case 'w':
      {
        if (LocaleCompare((char *) keyword,"weight") == 0)
          {
            type_list->weight=MagickAtoL(token);
            if (LocaleCompare(token,"bold") == 0)
              type_list->weight=700;
            if (LocaleCompare(token,"normal") == 0)
              type_list->weight=400;
            break;
          }
        break;
      }
      default:
        break;
    }
  }
  MagickFreeMemory(token);
  MagickFreeMemory(xml);
  if (type_list == (TypeInfo *) NULL)
    return(False);
  while (type_list->previous != (TypeInfo *) NULL)
    type_list=type_list->previous;
  return(True);
}
