1 /*
2 % Copyright (C) 2003-2019 GraphicsMagick Group
3 % Copyright (C) 2002 ImageMagick Studio
4 %
5 % This program is covered by multiple licenses, which are described in
6 % Copyright.txt. You should have received a copy of Copyright.txt with this
7 % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
8 %
9 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10 %                                                                             %
11 %                                                                             %
12 %                                                                             %
13 %                        TTTTT  Y   Y  PPPP   EEEEE                           %
14 %                          T     Y Y   P   P  E                               %
15 %                          T      Y    PPPP   EEE                             %
16 %                          T      Y    P      E                               %
17 %                          T      Y    P      EEEEE                           %
18 %                                                                             %
19 %                                                                             %
20 %                     GraphicsMagick Image Type Methods                       %
21 %                                                                             %
22 %                                                                             %
23 %                              Software Design                                %
24 %                                John Cristy                                  %
25 %                                 May 2001                                    %
26 %                                                                             %
27 %                                                                             %
28 %                                                                             %
29 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
30 %
31 %
32 */
33 
34 /*
35   Include declarations.
36 */
37 #include "magick/studio.h"
38 #if defined(MSWINDOWS) || defined(__CYGWIN__)
39 # include "magick/nt_feature.h"
40 #endif
41 #include "magick/blob.h"
42 #include "magick/enum_strings.h"
43 #include "magick/log.h"
44 #include "magick/render.h"
45 #include "magick/semaphore.h"
46 #include "magick/type.h"
47 #include "magick/utility.h"
48 
49 /*
50   Define declarations.
51 */
52 #define TypeFilename  "type.mgk"
53 
54 /*
55   Declare type map.
56 */
57 static const char
58   TypeMap[] =
59     "<?xml version=\"1.0\"?>"
60     "<typemap>"
61     "  <type stealth=\"True\" />"
62     "</typemap>";
63 
64 /*
65   Static declarations.
66 */
67 static SemaphoreInfo
68   *type_semaphore = (SemaphoreInfo *) NULL;
69 
70 static TypeInfo
71   *type_list = (TypeInfo *) NULL;
72 
73 /*
74   Forward declarations.
75 */
76 static unsigned int
77   ReadTypeConfigureFile(const char *,const unsigned long,ExceptionInfo *);
78 
79 /*
80 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81 %                                                                             %
82 %                                                                             %
83 %                                                                             %
84 +   D e s t r o y T y p e I n f o                                             %
85 %                                                                             %
86 %                                                                             %
87 %                                                                             %
88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89 %
90 %  Method DestroyTypeInfo deallocates memory associated with the font list.
91 %
92 %  The format of the DestroyTypeInfo method is:
93 %
94 %      DestroyTypeInfo(void)
95 %
96 %
97 */
DestroyTypeInfo(void)98 MagickExport void DestroyTypeInfo(void)
99 {
100   register TypeInfo
101     *p;
102 
103   TypeInfo
104     *type_info;
105 
106   for (p=type_list; p != (TypeInfo *) NULL; )
107   {
108     type_info=p;
109     p=p->next;
110     if (type_info->path != (char *) NULL)
111       MagickFreeMemory(type_info->path);
112     if (type_info->name != (char *) NULL)
113       MagickFreeMemory(type_info->name);
114     if (type_info->description != (char *) NULL)
115       MagickFreeMemory(type_info->description);
116     if (type_info->family != (char *) NULL)
117       MagickFreeMemory(type_info->family);
118     if (type_info->encoding != (char *) NULL)
119       MagickFreeMemory(type_info->encoding);
120     if (type_info->foundry != (char *) NULL)
121       MagickFreeMemory(type_info->foundry);
122     if (type_info->format != (char *) NULL)
123       MagickFreeMemory(type_info->format);
124     if (type_info->metrics != (char *) NULL)
125       MagickFreeMemory(type_info->metrics);
126     if (type_info->glyphs != (char *) NULL)
127       MagickFreeMemory(type_info->glyphs);
128     MagickFreeMemory(type_info);
129   }
130   type_list=(TypeInfo *) NULL;
131   DestroySemaphoreInfo(&type_semaphore);
132 }
133 
134 /*
135 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
136 %                                                                             %
137 %                                                                             %
138 %                                                                             %
139 +   G e t T y p e I n f o                                                     %
140 %                                                                             %
141 %                                                                             %
142 %                                                                             %
143 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144 %
145 %  Method GetTypeInfo searches the type list for the specified name and if
146 %  found returns attributes for that type.
147 %
148 %  The format of the GetTypeInfo method is:
149 %
150 %      const TypeInfo *GetTypeInfo(const char *name,ExceptionInfo *exception)
151 %
152 %  A description of each parameter follows:
153 %
154 %    o type_info: Method GetTypeInfo searches the type list for the specified
155 %      name and if found returns attributes for that type.
156 %
157 %    o name: The type name.
158 %
159 %    o exception: Return any errors or warnings in this structure.
160 %
161 %
162 */
GetTypeInfo(const char * name,ExceptionInfo * exception)163 MagickExport const TypeInfo *GetTypeInfo(const char *name,
164   ExceptionInfo *exception)
165 {
166   register TypeInfo
167     *p;
168 
169   if (type_list == (TypeInfo *) NULL)
170     {
171       LockSemaphoreInfo(type_semaphore);
172       if (type_list == (TypeInfo *) NULL)
173         {
174           (void) ReadTypeConfigureFile(TypeFilename,0,exception);
175 #if defined(MSWINDOWS) || defined(__CYGWIN__)
176           {
177             TypeInfo
178               *type_info;
179 
180             type_info=NTGetTypeList();
181             if (type_info != (TypeInfo *) NULL)
182               {
183                 if (type_list == (TypeInfo *) NULL)
184                   type_list=type_info;
185                 else
186                   {
187                     while (type_list->next != (TypeInfo *) NULL)
188                       type_list=type_list->next;
189                     type_list->next=type_info;
190                     type_info->previous=type_list;
191                     while (type_list->previous != (TypeInfo *) NULL)
192                       type_list=type_list->previous;
193                   }
194               }
195           }
196 #endif
197         }
198       UnlockSemaphoreInfo(type_semaphore);
199     }
200   if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
201     return((const TypeInfo *) type_list);
202   /*
203     Search for requested type.
204   */
205   LockSemaphoreInfo(type_semaphore);
206   for (p=type_list; p != (TypeInfo *) NULL; p=p->next)
207     if ((p->name != (char *) NULL) && (LocaleCompare(p->name,name) == 0))
208       break;
209   if (p != (TypeInfo *) NULL)
210     if (p != type_list)
211       {
212         /*
213           Self-adjusting list.
214         */
215         if (p->previous != (TypeInfo *) NULL)
216           p->previous->next=p->next;
217         if (p->next != (TypeInfo *) NULL)
218           p->next->previous=p->previous;
219         p->previous=(TypeInfo *) NULL;
220         p->next=type_list;
221         type_list->previous=p;
222         type_list=p;
223       }
224   UnlockSemaphoreInfo(type_semaphore);
225   return((const TypeInfo *) p);
226 }
227 
228 /*
229 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
230 %                                                                             %
231 %                                                                             %
232 %                                                                             %
233 +   G e t T y p e I n f o B y F a m i l y                                     %
234 %                                                                             %
235 %                                                                             %
236 %                                                                             %
237 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
238 %
239 %  Method GetTypeInfoByFamily searches the type list for the specified family
240 %  and if found returns attributes for that type.
241 %
242 %  Type substitution and scoring algorithm contributed by Bob Friesenhahn.
243 %
244 %  The format of the GetTypeInfoByFamily method is:
245 %
246 %      const TypeInfo *GetTypeInfoByFamily(const char *family,
247 %        const StyleType style,const StretchType stretch,
248 %        const unsigned long weight,ExceptionInfo *exception)
249 %
250 %  A description of each parameter follows:
251 %
252 %    o type_info: Method GetTypeInfo searches the type list for the specified
253 %      name and if found returns attributes for that type.
254 %
255 %    o family: The type family.
256 %
257 %    o style: The type style.
258 %
259 %    o stretch: The type stretch.
260 %
261 %    o weight: The type weight.
262 %
263 %    o exception: Return any errors or warnings in this structure.
264 %
265 %
266 */
GetTypeInfoByFamily(const char * family,const StyleType style,const StretchType stretch,const unsigned long weight,ExceptionInfo * exception)267 MagickExport const TypeInfo *GetTypeInfoByFamily(const char *family,
268   const StyleType style,const StretchType stretch,const unsigned long weight,
269   ExceptionInfo *exception)
270 {
271   const TypeInfo
272     *type_info;
273 
274   int
275     range;
276 
277   register const TypeInfo
278     *p;
279 
280   register unsigned int
281     i;
282 
283   static const struct
284   {
285     char
286       name[17],
287       substitute[10];
288   }
289   fontmap[] =
290     {
291       { "fixed", "courier" },
292       { "modern","courier" },
293       { "monotype corsiva", "courier" },
294       { "news gothic", "helvetica" },
295       { "system", "courier" },
296       { "terminal", "courier" },
297       { "wingdings", "symbol" }
298     };
299 
300   unsigned int
301     max_score,
302     score;
303 
304   /*
305     Check for an exact type match.
306   */
307   (void) GetTypeInfo("*",exception);
308   if (type_list == (TypeInfo *) NULL)
309     return((TypeInfo *) NULL);
310   for (p=type_list; p != (TypeInfo *) NULL; p=p->next)
311   {
312     if (p->family == (char *) NULL)
313       continue;
314     if (family == (const char *) NULL)
315       {
316         if ((LocaleCompare(p->family,"arial") != 0) &&
317             (LocaleCompare(p->family,"helvetica") != 0))
318           continue;
319       }
320     else
321       if (LocaleCompare(p->family,family) != 0)
322         continue;
323     if ((style != AnyStyle) && (p->style != style))
324       continue;
325     if ((stretch != AnyStretch) && (p->stretch != stretch))
326       continue;
327     if ((weight != 0) && (p->weight != weight))
328       continue;
329     return((TypeInfo *) p);
330   }
331   /*
332     Check for types in the same family.
333   */
334   max_score=0;
335   type_info=(TypeInfo *) NULL;
336   for (p=type_list; p != (TypeInfo *) NULL; p=p->next)
337   {
338     if (p->family == (char *) NULL)
339       continue;
340     if (family == (const char *) NULL)
341       {
342         if ((LocaleCompare(p->family,"arial") != 0) &&
343             (LocaleCompare(p->family,"helvetica") != 0))
344           continue;
345       }
346     else
347       if (LocaleCompare(p->family,family) != 0)
348         continue;
349     score=0;
350     if ((style == AnyStyle) || (p->style == style))
351       score+=32;
352     else
353       if (((style == ItalicStyle) || (style == ObliqueStyle)) &&
354           ((p->style == ItalicStyle) || (p->style == ObliqueStyle)))
355         score+=25;
356     if (weight == 0)
357       score+=16;
358     else
359       score+=(16*(800-((int) Max(Min(weight,900),p->weight)-
360         (int) Min(Min(weight,900),p->weight))))/800;
361     if (stretch == AnyStretch)
362       score+=8;
363     else
364       {
365         range=(int) UltraExpandedStretch-(int) NormalStretch;
366         score+=(8*(range-((int) Max(stretch,p->stretch)-
367           (int) Min(stretch,p->stretch))))/range;
368       }
369     if (score > max_score)
370       {
371         max_score=score;
372         type_info=p;
373       }
374   }
375   if (type_info != (TypeInfo *) NULL)
376     return((TypeInfo *) type_info);
377   /*
378     Check for table-based substitution match.
379   */
380   for (i=0; i < ArraySize(fontmap); i++)
381   {
382     if (family == (const char *) NULL)
383       {
384         if ((LocaleCompare(fontmap[i].name,"arial") != 0) &&
385             (LocaleCompare(fontmap[i].name,"helvetica") != 0))
386           continue;
387       }
388     else
389       if (LocaleCompare(fontmap[i].name,family) != 0)
390         continue;
391     type_info=GetTypeInfoByFamily(fontmap[i].substitute,style,stretch,weight,
392       exception);
393     break;
394   }
395   if (type_info != (TypeInfo *) NULL)
396     {
397       ThrowException(exception,TypeError,FontSubstitutionRequired,
398         type_info->family);
399       return((TypeInfo *) type_info);
400     }
401   if (family != (const char *) NULL)
402     type_info=GetTypeInfoByFamily((const char *) NULL,style,stretch,weight,
403       exception);
404   return((TypeInfo *) type_info);
405 }
406 
407 /*
408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
409 %                                                                             %
410 %                                                                             %
411 %                                                                             %
412 %   G e t T y p e l i s t                                                     %
413 %                                                                             %
414 %                                                                             %
415 %                                                                             %
416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
417 %
418 %  GetTypeList() returns any types that match the specified pattern.
419 %
420 %  The format of the GetTypeList function is:
421 %
422 %      char **GetTypeList(const char *pattern,unsigned long *number_types)
423 %
424 %  A description of each parameter follows:
425 %
426 %    o pattern: Specifies a pointer to a text string containing a pattern.
427 %
428 %    o number_types:  This integer returns the number of types in the list.
429 %
430 %
431 */
GetTypeList(const char * pattern,unsigned long * number_types)432 MagickExport char **GetTypeList(const char *pattern,unsigned long *number_types)
433 {
434   char
435     **typelist;
436 
437   ExceptionInfo
438     exception;
439 
440   register const TypeInfo
441     *p;
442 
443   register long
444     i;
445 
446   /*
447     Allocate type list.
448   */
449   assert(pattern != (char *) NULL);
450   assert(number_types != (unsigned long *) NULL);
451   *number_types=0;
452   GetExceptionInfo(&exception);
453   p=GetTypeInfo("*",&exception);
454   DestroyExceptionInfo(&exception);
455   if (p == (const TypeInfo *) NULL)
456     return((char **) NULL);
457   i=0;
458   for (p=type_list; p != (const TypeInfo *) NULL; p=p->next)
459     i++;
460   typelist=MagickAllocateMemory(char **,i*sizeof(char *));
461   if (typelist == (char **) NULL)
462     return((char **) NULL);
463   i=0;
464   for (p=type_list; p != (const TypeInfo *) NULL; p=p->next)
465   {
466     if (p->stealth)
467       continue;
468     if (GlobExpression(p->name,pattern))
469       typelist[i++]=AllocateString(p->name);
470   }
471   *number_types=i;
472   return(typelist);
473 }
474 
475 /*
476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
477 %                                                                             %
478 %                                                                             %
479 %                                                                             %
480 +   I n i t i a l i z e T y p e I n f o                                       %
481 %                                                                             %
482 %                                                                             %
483 %                                                                             %
484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485 %
486 %  Method InitializeTypeInfo initializes the type facility
487 %
488 %  The format of the InitializeTypeInfo method is:
489 %
490 %      MagickPassFail InitializeTypeInfo(void)
491 %
492 %
493 */
494 MagickPassFail
InitializeTypeInfo(void)495 InitializeTypeInfo(void)
496 {
497   assert(type_semaphore == (SemaphoreInfo *) NULL);
498   type_semaphore=AllocateSemaphoreInfo();
499   return MagickPass;
500 }
501 
502 /*
503 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
504 %                                                                             %
505 %                                                                             %
506 %                                                                             %
507 %  L i s t T y p e I n f o                                                    %
508 %                                                                             %
509 %                                                                             %
510 %                                                                             %
511 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
512 %
513 %  Method ListTypeInfo lists the fonts to a file.
514 %
515 %  The format of the ListTypeInfo method is:
516 %
517 %      unsigned int ListTypeInfo(FILE *file,ExceptionInfo *exception)
518 %
519 %  A description of each parameter follows.
520 %
521 %    o file:  An pointer to a FILE.
522 %
523 %    o exception: Return any errors or warnings in this structure.
524 %
525 %
526 */
527 
ListTypeInfo(FILE * file,ExceptionInfo * exception)528 MagickExport unsigned int ListTypeInfo(FILE *file,ExceptionInfo *exception)
529 {
530   char
531     weight[MaxTextExtent];
532 
533   const char
534     *family,
535     *name,
536     *stretch,
537     *style;
538 
539   register const TypeInfo
540     *p;
541 
542   if (file == (FILE *) NULL)
543     file=stdout;
544   (void) GetTypeInfo("*",exception);
545   LockSemaphoreInfo(type_semaphore);
546   for (p=type_list; p != (const TypeInfo *) NULL; p=p->next)
547   {
548     if ((p->previous == (TypeInfo *) NULL) ||
549         (LocaleCompare(p->path,p->previous->path) != 0))
550       {
551         if (p->previous != (TypeInfo *) NULL)
552           (void) fprintf(file,"\n");
553         if (p->path != (char *) NULL)
554           (void) fprintf(file,"Path: %.1024s\n\n",p->path);
555         (void) fprintf(file,"%-32.32s %-23.23s %-7.7s %-8s %-3s\n",
556           "Name","Family","Style","Stretch","Weight");
557         (void) fprintf(file,"--------------------------------------------------"
558           "------------------------------\n");
559       }
560     if (p->stealth)
561       continue;
562     name="unknown";
563     if (p->name != (char *) NULL)
564       name=p->name;
565     family="unknown";
566     if (p->family != (char *) NULL)
567       family=p->family;
568     style=StyleTypeToString(p->style);
569     stretch=StretchTypeToString(p->stretch);
570     FormatString(weight,"%lu",p->weight);
571     (void) fprintf(file,"%-32.32s %-23.23s %-7.7s %-9s %-3s\n",
572       name,family,style,stretch,weight);
573   }
574   (void) fflush(file);
575   UnlockSemaphoreInfo(type_semaphore);
576   return(True);
577 }
578 
579 /*
580 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
581 %                                                                             %
582 %                                                                             %
583 %                                                                             %
584 +   R e a d T y p e C o n f i g u r e F i l e                                 %
585 %                                                                             %
586 %                                                                             %
587 %                                                                             %
588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
589 %
590 %  ReadTypeConfigureFile() reads the type configuration file which provides
591 %  a mapping between type attributes and font files.
592 %
593 %  The format of the ReadTypeConfigureFile method is:
594 %
595 %      unsigned int ReadTypeConfigureFile(const char *basename,
596 %        const unsigned long depth,ExceptionInfo *exception)
597 %
598 %  A description of each parameter follows:
599 %
600 %    o status: ReadTypeConfigureFile() returns True if at least one entry
601 %      is read, otherwise False is returned.
602 %
603 %    o basename:  The type configuration filename.
604 %
605 %    o depth: depth of <include /> statements.
606 %
607 %    o exception: Return any errors or warnings in this structure.
608 %
609 %
610 */
ReadTypeConfigureFile(const char * basename,const unsigned long depth,ExceptionInfo * exception)611 static unsigned int ReadTypeConfigureFile(const char *basename,
612   const unsigned long depth,ExceptionInfo *exception)
613 {
614   char
615     keyword[MaxTextExtent],
616     path[MaxTextExtent],
617     *q,
618     *token,
619     *xml;
620 
621 #if defined(MSWINDOWS)
622   char
623     GhostscriptFontDir[MaxTextExtent];
624 #endif
625 
626   size_t
627     length,
628     token_max_length;
629 
630   /*
631     Read the type configure file.
632   */
633   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
634     "File path=\"%.1024s\", recursion depth=%lu",basename,depth);
635   (void) strlcpy(path,basename,sizeof(path));
636 #if defined(MSWINDOWS)
637   /*
638     For Windows, cache the location of the Ghostscript fonts.
639   */
640   GhostscriptFontDir[0]='\0';
641   if (NTGhostscriptFonts(GhostscriptFontDir,MaxTextExtent-2))
642     (void) strlcat(GhostscriptFontDir,DirectorySeparator,
643                    sizeof(GhostscriptFontDir));
644 #endif
645   if (depth == 0)
646     {
647       /*
648         Load top configuration file based on configure search path.
649       */
650       xml=(char *) GetConfigureBlob(basename,path,&length,exception);
651       if (xml == (char *) NULL)
652         xml=AllocateString(TypeMap);
653     }
654   else
655     {
656       /*
657         Load subordinate configuration file based on path specified
658         by parent configuration file.
659       */
660       if (IsAccessibleAndNotEmpty(basename))
661         xml=(char *) FileToBlob(basename,&length,exception);
662       else
663         {
664           GetPathComponent(basename,TailPath,path);
665           xml=(char *) GetConfigureBlob(path,keyword,&length,exception);
666         }
667       if (xml == (char *) NULL)
668         return (False);
669     }
670 
671   token=AllocateString(xml);
672   token_max_length=strlen(token);
673   for (q=xml; *q != '\0'; )
674   {
675     /*
676       Interpret XML.
677     */
678     MagickGetToken(q,&q,token,token_max_length);
679     if (*token == '\0')
680       break;
681     (void) strlcpy(keyword,token,MaxTextExtent);
682     if (LocaleNCompare(keyword,"<!--",4) == 0)
683       {
684         /*
685           Comment element.
686         */
687         while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
688           MagickGetToken(q,&q,token,token_max_length);
689         continue;
690       }
691     if (LocaleCompare(keyword,"<include") == 0)
692       {
693         /*
694           Include element.
695         */
696         while ( (*token != '/' && *(token+1)  != '>') && (*q != '\0'))
697         {
698           (void) strlcpy(keyword,token,MaxTextExtent);
699           MagickGetToken(q,&q,token,token_max_length);
700           if (*token != '=')
701             continue;
702           MagickGetToken(q,&q,token,token_max_length);
703           if (LocaleCompare(keyword,"file") == 0)
704             {
705               if (depth > 200)
706                 ThrowException(exception,ConfigureError,IncludeElementNestedTooDeeply,path);
707               else
708                 {
709                   char
710                     filename[MaxTextExtent];
711 
712                   GetPathComponent(path,HeadPath,filename);
713                   if (filename[0] != '\0')
714                     (void) strlcat(filename,DirectorySeparator,sizeof(filename));
715                   (void) strlcat(filename,token,sizeof(filename));
716                   (void) ReadTypeConfigureFile(filename,depth+1,exception);
717                 }
718               if (type_list != (TypeInfo *) NULL)
719                 while (type_list->next != (TypeInfo *) NULL)
720                   type_list=type_list->next;
721             }
722         }
723         continue;
724       }
725     if (LocaleCompare(keyword,"<type") == 0)
726       {
727         TypeInfo
728           *type_info;
729 
730         /*
731           Allocate memory for the type list.
732         */
733         type_info=MagickAllocateMemory(TypeInfo *,sizeof(TypeInfo));
734         if (type_info == (TypeInfo *) NULL)
735           MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed,
736             UnableToAllocateTypeInfo);
737         (void) memset(type_info,0,sizeof(TypeInfo));
738         type_info->path=AcquireString(path);
739         type_info->signature=MagickSignature;
740         if (type_list == (TypeInfo *) NULL)
741           {
742             type_list=type_info;
743             continue;
744           }
745         /*
746           Append entry to end of type list
747         */
748         while (type_list->next != (TypeInfo *) NULL)
749           type_list=type_list->next;
750         type_list->next=type_info;
751         type_info->previous=type_list;
752         type_list=type_list->next;
753         continue;
754       }
755     if (type_list == (TypeInfo *) NULL)
756       continue;
757     MagickGetToken(q,(char **) NULL,token,token_max_length);
758     if (*token != '=')
759       continue;
760     MagickGetToken(q,&q,token,token_max_length);
761     MagickGetToken(q,&q,token,token_max_length);
762     switch (*keyword)
763     {
764       case 'E':
765       case 'e':
766       {
767         if (LocaleCompare((char *) keyword,"encoding") == 0)
768           {
769             type_list->encoding=AcquireString(token);
770             break;
771           }
772         break;
773       }
774       case 'F':
775       case 'f':
776       {
777         if (LocaleCompare((char *) keyword,"family") == 0)
778           {
779             type_list->family=AcquireString(token);
780             break;
781           }
782         if (LocaleCompare((char *) keyword,"format") == 0)
783           {
784             type_list->format=AcquireString(token);
785             break;
786           }
787         if (LocaleCompare((char *) keyword,"foundry") == 0)
788           {
789             type_list->foundry=AcquireString(token);
790             break;
791           }
792         if (LocaleCompare((char *) keyword,"fullname") == 0)
793           {
794             type_list->description=AcquireString(token);
795             break;
796           }
797         break;
798       }
799       case 'G':
800       case 'g':
801       {
802         if (LocaleCompare((char *) keyword,"glyphs") == 0)
803           {
804             char
805               *glyphs;
806 
807             glyphs=(char *) NULL;
808             (void) CloneString(&glyphs,token);
809 #if defined(MSWINDOWS)
810             if (strchr(glyphs,'@') != (char *) NULL)
811               SubstituteString(&glyphs,"@ghostscript_font_dir@",GhostscriptFontDir);
812 #endif
813             type_list->glyphs=glyphs;
814             break;
815           }
816         break;
817       }
818       case 'M':
819       case 'm':
820       {
821         if (LocaleCompare((char *) keyword,"metrics") == 0)
822           {
823             char
824               *metrics;
825 
826             metrics=AcquireString(token);
827 #if defined(MSWINDOWS)
828             if (strchr(metrics,'@') != (char *) NULL)
829               SubstituteString(&metrics,"@ghostscript_font_dir@",GhostscriptFontDir);
830 #endif
831             type_list->metrics=metrics;
832             break;
833           }
834         break;
835       }
836       case 'N':
837       case 'n':
838       {
839         if (LocaleCompare((char *) keyword,"name") == 0)
840           {
841             type_list->name=AcquireString(token);
842             break;
843           }
844         break;
845       }
846       case 'S':
847       case 's':
848       {
849         if (LocaleCompare((char *) keyword,"stealth") == 0)
850           {
851             type_list->stealth=LocaleCompare(token,"True") == 0;
852             break;
853           }
854         if (LocaleCompare((char *) keyword,"stretch") == 0)
855           {
856             if (LocaleCompare(token,"condensed") == 0)
857               type_list->stretch=CondensedStretch;
858             if (LocaleCompare(token,"expanded") == 0)
859               type_list->stretch=ExpandedStretch;
860             if (LocaleCompare(token,"extra-condensed") == 0)
861               type_list->stretch=ExtraCondensedStretch;
862             if (LocaleCompare(token,"extra-expanded") == 0)
863               type_list->stretch=ExtraExpandedStretch;
864             if (LocaleCompare(token,"normal") == 0)
865               type_list->stretch=NormalStretch;
866             if (LocaleCompare(token,"semi-condensed") == 0)
867               type_list->stretch=SemiCondensedStretch;
868             if (LocaleCompare(token,"semi-expanded") == 0)
869               type_list->stretch=SemiExpandedStretch;
870             if (LocaleCompare(token,"ultra-condensed") == 0)
871               type_list->stretch=UltraCondensedStretch;
872             if (LocaleCompare(token,"ultra-expanded") == 0)
873               type_list->stretch=UltraExpandedStretch;
874             break;
875           }
876         if (LocaleCompare((char *) keyword,"style") == 0)
877           {
878             if (LocaleCompare(token,"italic") == 0)
879               type_list->style=ItalicStyle;
880             if (LocaleCompare(token,"normal") == 0)
881               type_list->style=NormalStyle;
882             if (LocaleCompare(token,"oblique") == 0)
883               type_list->style=ObliqueStyle;
884             break;
885           }
886         break;
887       }
888       case 'W':
889       case 'w':
890       {
891         if (LocaleCompare((char *) keyword,"weight") == 0)
892           {
893             type_list->weight=MagickAtoL(token);
894             if (LocaleCompare(token,"bold") == 0)
895               type_list->weight=700;
896             if (LocaleCompare(token,"normal") == 0)
897               type_list->weight=400;
898             break;
899           }
900         break;
901       }
902       default:
903         break;
904     }
905   }
906   MagickFreeMemory(token);
907   MagickFreeMemory(xml);
908   if (type_list == (TypeInfo *) NULL)
909     return(False);
910   while (type_list->previous != (TypeInfo *) NULL)
911     type_list=type_list->previous;
912   return(True);
913 }
914