]> git.donarmstrong.com Git - lilypond.git/blob - flower/parse-afm.cc
00c348e79f361ddb083b9a4ba85847192249e800
[lilypond.git] / flower / parse-afm.cc
1 /* Our mods:
2
3 1. FontInfo has become AFM_AFM_Font_info to avoid namespace collisions.
4 2. Version is a linetoken because Bitstream Charter has a space in the version.
5 3. Added some necessary #include headers.
6 4. Added AFM_ prefixes to error codes.
7 5. Made it recognize both '\n' and '\r' as line terminators. Sheesh!
8 6. Stopped buffer overflows in token and linetoken.
9
10 Raph Levien <raph@acm.org> writing on 4 Oct 1998, updating 21 Oct 1998
11
12
13 1. parseFileFree function.
14 2. Leak fix in parseFile.
15
16 Morten Welinder <terra@diku.dk> September 1999.
17
18 */
19
20 /*
21  * (C) 1988, 1989, 1990 by Adobe Systems Incorporated. All rights reserved.
22  *
23  * This file may be freely copied and redistributed as long as:
24  *   1) This entire notice continues to be included in the file,
25  *   2) If the file has been modified in any way, a notice of such
26  *      modification is conspicuously indicated.
27  *
28  * PostScript, Display PostScript, and Adobe are registered trademarks of
29  * Adobe Systems Incorporated.
30  *
31  * ************************************************************************
32  * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT
33  * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS
34  * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR
35  * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY
36  * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION,
37  * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
38  * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
39  * ************************************************************************
40  */
41
42 /* parseAFM.c
43  *
44  * This file is used in conjuction with the parseAFM.h header file.
45  * This file contains several procedures that are used to parse AFM
46  * files. It is intended to work with an application program that needs
47  * font metric information. The program can be used as is by making a
48  * procedure call to "parseFile" (passing in the expected parameters)
49  * and having it fill in a data structure with the data from the
50  * AFM file, or an application developer may wish to customize this
51  * code.
52  *
53  * There is also a file, parseAFMclient.c, that is a sample application
54  * showing how to call the "parseFile" procedure and how to use the data
55  * after "parseFile" has returned.
56  *
57  * Please read the comments in parseAFM.h and parseAFMclient.c.
58  *
59  * History:
60  *      original: DSM  Thu Oct 20 17:39:59 PDT 1988
61  *  modified: DSM  Mon Jul  3 14:17:50 PDT 1989
62  *    - added 'storageProblem' return code
63  *        - fixed bug of not allocating extra byte for string duplication
64  *    - fixed typos
65  *  modified: DSM  Tue Apr  3 11:18:34 PDT 1990
66  *    - added free (ident) at end of parseFile routine
67  *  modified: DSM  Tue Jun 19 10:16:29 PDT 1990
68  *    - changed (width == 250) to (width = 250) in initializeArray
69  */
70
71 #include "parse-afm.hh"
72
73 #include <cstdlib>
74 #include <cerrno>
75 #include <cmath>
76 #include <cstring>
77
78 #include <sys/file.h>
79
80 #include "warn.hh"
81
82 /* your basic constants */
83 #define TRUE 1
84 #define FALSE 0
85 #define EOL '\n'                /* end-of-line indicator */
86 #define MAX_NAME 4096           /* max length for identifiers */
87
88
89
90 /* Flags that can be AND'ed together to specify exactly what
91  * information from the AFM file should be saved.
92  */
93 #define P_G     0x01    /* 0000 0001 */   /* Global Font Info      */
94 #define P_W     0x02    /* 0000 0010 */   /* Character Widths ONLY */
95 #define P_M     0x06    /* 0000 0110 */   /* All Char Metric Info  */
96 #define P_P     0x08    /* 0000 1000 */   /* Pair Kerning Info     */
97 #define P_T     0x10    /* 0001 0000 */   /* Track Kerning Info    */
98 #define P_C     0x20    /* 0010 0000 */   /* Composite Char Info   */
99
100
101 /* Commonly used flags
102  */
103 #define P_GW\
104         (P_G | P_W) 
105 #define P_GM\
106         (P_G | P_M)
107 #define P_GMP\
108         (P_G | P_M | P_P)
109 #define P_GMK\
110         (P_G | P_M | P_P | P_T) 
111 #define P_GALL\
112         (P_G | P_M | P_P | P_T | P_C)
113
114 #define METATYPE1_BUG   /* Parse Metatype1's (version unknown)
115                            'Generated' global tag as comment. */
116
117 #define lineterm EOL    /* line terminating character */
118 #define lineterm_alt '\r' /* alternative line terminating character */
119 #define normalEOF 1     /* return code from parsing routines used only */
120                         /* in this module */
121 #define Space "space"   /* used in string comparison to look for the width */
122                         /* of the space character to init the widths array */
123 #define False "false"   /* used in string comparison to check the value of */
124                         /* boolean keys (e.g. IsFixedPitch)  */
125
126 #define MATCH(A,B) (strncmp ((A), (B), MAX_NAME) == 0)
127
128
129
130 /*************************** GLOBALS ***********************/
131
132 static char *ident = NULL; /* storage buffer for keywords */
133
134
135 /* "shorts" for fast case statement
136  * The values of each of these enumerated items correspond to an entry in the
137  * table of strings defined below. Therefore, if you add a new string as
138  * new keyword into the keyStrings table, you must also add a corresponding
139  * parseKey AND it MUST be in the same position!
140  *
141  * IMPORTANT: since the sorting algorithm is a binary search, the strings of
142  * keywords must be placed in lexicographical order, below. [Therefore, the
143  * enumerated items are not necessarily in lexicographical order, depending
144  * on the name chosen. BUT, they must be placed in the same position as the
145  * corresponding key string.] The NOPE shall remain in the last position,
146  * since it does not correspond to any key string, and it is used in the
147  * "recognize" procedure to calculate how many possible keys there are.
148  */
149
150 enum parseKey {
151   ASCENDER, CHARBBOX, CODE, COMPCHAR, CAPHEIGHT, COMMENT,
152   DESCENDER, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES,
153   ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN,
154   FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME,
155 #ifdef METATYPE1_BUG
156   GENERATED,
157 #endif
158   ISFIXEDPITCH,
159   ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, CHARNAME,
160   NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES,
161   STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS,
162   STARTTRACKKERN, TRACKKERN, UNDERLINEPOSITION,
163   UNDERLINETHICKNESS, VERSION, XYWIDTH, XWIDTH, WEIGHT, XHEIGHT,
164   NOPE };
165
166 /* keywords for the system:
167  * This a table of all of the current strings that are vaild AFM keys.
168  * Each entry can be referenced by the appropriate parseKey value (an
169  * enumerated data type defined above). If you add a new keyword here,
170  * a corresponding parseKey MUST be added to the enumerated data type
171  * defined above, AND it MUST be added in the same position as the
172  * string is in this table.
173  *
174  * IMPORTANT: since the sorting algorithm is a binary search, the keywords
175  * must be placed in lexicographical order. And, NULL should remain at the
176  * end.
177  */
178
179 static char *keyStrings[] = {
180   "Ascender", "B", "C", "CC", "CapHeight", "Comment",
181   "Descender", "EncodingScheme", "EndCharMetrics", "EndComposites",
182   "EndFontMetrics", "EndKernData", "EndKernPairs", "EndTrackKern",
183   "FamilyName", "FontBBox", "FontName", "FullName",
184 #ifdef METATYPE1_BUG
185   "Generated",
186 #endif
187   "IsFixedPitch",
188   "ItalicAngle", "KP", "KPX", "L", "N",
189   "Notice", "PCC", "StartCharMetrics", "StartComposites",
190   "StartFontMetrics", "StartKernData", "StartKernPairs",
191   "StartTrackKern", "TrackKern", "UnderlinePosition",
192   "UnderlineThickness", "Version", "W", "WX", "Weight", "XHeight",
193   NULL };
194
195 /*************************** PARSING ROUTINES **************/
196
197 /*************************** token *************************/
198
199 /*  A "AFM File Conventions" tokenizer. That means that it will
200  *  return the next token delimited by white space.  See also
201  *  the `linetoken' routine, which does a similar thing but
202  *  reads all tokens until the next end-of-line.
203  */
204
205 static char*
206 token (FILE *stream)
207 {
208   int ch, idx;
209
210   /* skip over white space */
211   while ((ch = fgetc (stream)) == ' ' || ch == lineterm ||
212          ch == lineterm_alt ||
213          ch == ',' || ch == '\t' || ch == ';');
214
215   idx = 0;
216   while (idx < MAX_NAME - 1 &&
217          ch != EOF && ch != ' ' && ch != lineterm && ch != lineterm_alt
218          && ch != '\t' && ch != ':' && ch != ';')
219     {
220       ident[idx++] = ch;
221       ch = fgetc (stream);
222     } /* while */
223
224   if (ch == EOF && idx < 1) return ((char *)NULL);
225   if (idx >= 1 && ch != ':' ) ungetc (ch, stream);
226   if (idx < 1 ) ident[idx++] = ch;      /* single-character token */
227   ident[idx] = 0;
228
229   return (ident);       /* returns pointer to the token */
230
231 } /* token */
232
233
234 /*************************** linetoken *************************/
235
236 /*  "linetoken" will get read all tokens until the EOL character from
237  *  the given stream.  This is used to get any arguments that can be
238  *  more than one word (like Comment lines and FullName).
239  */
240
241 static char*
242 linetoken (FILE *stream)
243 {
244   int ch, idx;
245
246   while ((ch = fgetc (stream)) == ' ' || ch == '\t' );
247
248   idx = 0;
249   while (idx < MAX_NAME - 1 &&
250          ch != EOF && ch != lineterm && ch != lineterm_alt)
251     {
252       ident[idx++] = ch;
253       ch = fgetc (stream);
254     } /* while */
255
256   ungetc (ch, stream);
257   ident[idx] = 0;
258
259   return (ident);       /* returns pointer to the token */
260
261 } /* linetoken */
262
263
264 /*************************** recognize *************************/
265
266 /*  This function tries to match a string to a known list of
267  *  valid AFM entries (check the keyStrings array above).
268  *  "ident" contains everything from white space through the
269  *  next space, tab, or ":" character.
270  *
271  *  The algorithm is a standard Knuth binary search.
272  */
273
274 static enum parseKey
275 recognize (register char *ident)
276 {
277   int lower = 0,
278     upper = (int) NOPE,
279     midpoint = 0,
280     cmpvalue = 0;
281   BOOL found = FALSE;
282
283   while ((upper >= lower) && !found)
284     {
285       midpoint = (lower + upper)/2;
286       if (keyStrings[midpoint] == NULL)
287         break;
288       cmpvalue = strncmp (ident, keyStrings[midpoint], MAX_NAME);
289       if (cmpvalue == 0)
290         found = TRUE;
291       else
292         if (cmpvalue < 0)
293           upper = midpoint - 1;
294       else
295         lower = midpoint + 1;
296     }
297
298   if (found)
299     return (enum parseKey) midpoint;
300   else
301     return NOPE;
302 }
303
304
305 /************************* parseGlobals *****************************/
306
307 /*  This function is called by "parseFile". It will parse the AFM File
308  *  up to the "StartCharMetrics" keyword, which essentially marks the
309  *  end of the Global Font Information and the beginning of the character
310  *  metrics information.
311  *
312  *  If the caller of "parseFile" specified that it wanted the Global
313  *  Font Information (as defined by the "AFM File Specification"
314  *  document), then that information will be stored in the returned
315  *  data structure.
316  *
317  *  Any Global Font Information entries that are not found in a
318  *  given file, will have the usual default initialization value
319  *  for its type (i.e. entries of type int will be 0, etc).
320  *
321  *  This function returns an error code specifying whether there was
322  *  a premature EOF or a parsing error. This return value is used by
323  *  parseFile to determine if there is more file to parse.
324  */
325
326 static BOOL
327 parseGlobals (FILE *fp, register AFM_GlobalFontInfo *gfi)
328 {
329   BOOL cont = TRUE, save = (gfi != NULL);
330   int error = AFM_ok;
331   register char *keyword;
332
333   while (cont)
334     {
335       keyword = token (fp);
336
337       if (keyword == NULL)
338         /* Have reached an early and unexpected EOF. */
339         /* Set flag and stop parsing */
340         {
341           error = AFM_earlyEOF;
342           break;   /* get out of loop */
343         }
344       if (!save)
345         /* get tokens until the end of the Global Font info section */
346         /* without saving any of the data */
347         switch (recognize (keyword))
348           {                             
349           case STARTCHARMETRICS:
350             cont = FALSE;
351             break;
352           case ENDFONTMETRICS:  
353             cont = FALSE;
354             error = normalEOF;
355             break;
356           default:
357             break;
358           } /* switch */
359       else
360         /* otherwise parse entire global font info section, */
361         /* saving the data */
362         switch (recognize (keyword))
363           {
364           case STARTFONTMETRICS:
365             keyword = token (fp);
366             gfi->afmVersion = (char *) malloc (strlen (keyword) + 1);
367             strcpy (gfi->afmVersion, keyword);
368             break;
369           case COMMENT:
370 #ifdef METATYPE1_BUG
371           case GENERATED:
372 #endif  
373             keyword = linetoken (fp);
374             break;
375           case FONTNAME:
376             keyword = token (fp);
377             gfi->fontName = (char *) malloc (strlen (keyword) + 1);
378             strcpy (gfi->fontName, keyword);
379             break;
380           case ENCODINGSCHEME:
381             keyword = token (fp);
382             gfi->encodingScheme = (char *)
383               malloc (strlen (keyword) + 1);
384             strcpy (gfi->encodingScheme, keyword);
385             break;
386           case FULLNAME:
387             keyword = linetoken (fp);
388             gfi->fullName = (char *) malloc (strlen (keyword) + 1);
389             strcpy (gfi->fullName, keyword);
390             break;
391           case FAMILYNAME:
392             keyword = linetoken (fp);
393             gfi->familyName = (char *) malloc (strlen (keyword) + 1);
394             strcpy (gfi->familyName, keyword);
395             break;
396           case WEIGHT:
397             keyword = token (fp);
398             gfi->weight = (char *) malloc (strlen (keyword) + 1);
399             strcpy (gfi->weight, keyword);
400             break;
401           case ITALICANGLE:
402             keyword = token (fp);
403             gfi->italicAngle = atof (keyword);
404             if (errno == ERANGE) error = AFM_parseError;
405             break;
406           case ISFIXEDPITCH:
407             keyword = token (fp);
408             if (MATCH (keyword, False))
409               gfi->isFixedPitch = 0;
410             else
411               gfi->isFixedPitch = 1;
412             break;
413           case UNDERLINEPOSITION:
414             keyword = token (fp);
415             gfi->underlinePosition = atoi (keyword);
416             break;
417           case UNDERLINETHICKNESS:
418             keyword = token (fp);
419             gfi->underlineThickness = atoi (keyword);
420             break;
421           case VERSION:
422             keyword = linetoken (fp);
423             gfi->version = (char *) malloc (strlen (keyword) + 1);
424             strcpy (gfi->version, keyword);
425             break;
426           case NOTICE:
427             keyword = linetoken (fp);
428             gfi->notice = (char *) malloc (strlen (keyword) + 1);
429             strcpy (gfi->notice, keyword);
430             break;
431           case FONTBBOX:
432             keyword = token (fp);
433             gfi->fontBBox.llx = atoi (keyword);
434             keyword = token (fp);
435             gfi->fontBBox.lly = atoi (keyword);
436             keyword = token (fp);
437             gfi->fontBBox.urx = atoi (keyword);
438             keyword = token (fp);
439             gfi->fontBBox.ury = atoi (keyword);
440             break;
441           case CAPHEIGHT:
442             keyword = token (fp);
443             gfi->capHeight = atoi (keyword);
444             break;
445           case XHEIGHT:
446             keyword = token (fp);
447             gfi->xHeight = atoi (keyword);
448             break;
449           case DESCENDER:
450             keyword = token (fp);
451             gfi->descender = atoi (keyword);
452             break;
453           case ASCENDER:
454             keyword = token (fp);
455             gfi->ascender = atoi (keyword);
456             break;
457           case STARTCHARMETRICS:
458             cont = FALSE;
459             break;
460           case ENDFONTMETRICS:
461             cont = FALSE;
462             error = normalEOF;
463             break;
464           case NOPE:
465           default:
466             error = AFM_parseError;
467             break;
468           } /* switch */
469     } /* while */
470
471   return (error);
472
473 } /* parseGlobals */
474
475
476 #if 0
477 /************************* initializeArray ************************/
478
479 /*  Unmapped character codes are (at Adobe Systems) assigned the
480  *  width of the space character (if one exists) else they get the
481  *  value of 250 ems. This function initializes all entries in the
482  *  char widths array to have this value. Then any mapped character
483  *  codes will be replaced with the width of the appropriate character
484  *  when parsing the character metric section.
485
486  *  This function parses the Character Metrics Section looking
487  *  for a space character (by comparing character names). If found,
488  *  the width of the space character will be used to initialize the
489  *  values in the array of character widths.
490  *
491  *  Before returning, the position of the read/write pointer of the
492  *  file is reset to be where it was upon entering this function.
493  */
494
495 static int
496 initializeArray (FILE *fp, register int *cwi)
497 {
498   BOOL cont = TRUE, found = FALSE;
499   long opos = ftell (fp);
500   int code = 0, width = 0, i = 0, error = 0;
501   register char *keyword;
502
503   while (cont)
504     {
505       keyword = token (fp);
506       if (keyword == NULL)
507         {
508           error = AFM_earlyEOF;
509           break; /* get out of loop */
510         }
511       switch (recognize (keyword))
512         {
513         case COMMENT:
514           keyword = linetoken (fp);
515           break;
516         case CODE:
517           code = atoi (token (fp));
518           break;
519         case XWIDTH:
520           width = atoi (token (fp));
521           break;
522         case CHARNAME:
523           keyword = token (fp);
524           if (MATCH (keyword, Space))
525             {
526               cont = FALSE;
527               found = TRUE;
528             }
529           break;
530         case ENDCHARMETRICS:
531           cont = FALSE;
532           break;
533         case ENDFONTMETRICS:
534           cont = FALSE;
535           error = normalEOF;
536           break;
537         case NOPE:
538         default:
539           error = AFM_parseError;
540           break;
541         } /* switch */
542     } /* while */
543
544   if (!found)
545     width = 250;
546
547   for (i = 0; i < 256; ++i)
548     cwi[i] = width;
549
550   fseek (fp, opos, 0);
551
552   return (error);
553
554 } /* initializeArray */
555 #endif
556
557 /************************* parseCharWidths **************************/
558
559 /*  This function is called by "parseFile". It will parse the AFM File
560  *  up to the "EndCharMetrics" keyword. It will save the character
561  *  width info (as opposed to all of the character metric information)
562  *  if requested by the caller of parseFile. Otherwise, it will just
563  *  parse through the section without saving any information.
564  *
565  *  If data is to be saved, parseCharWidths is passed in a pointer
566  *  to an array of widths that has already been initialized by the
567  *  standard value for unmapped character codes. This function parses
568  *  the Character Metrics section only storing the width information
569  *  for the encoded characters into the array using the character code
570  *  as the index into that array.
571  *
572  *  This function returns an error code specifying whether there was
573  *  a premature EOF or a parsing error. This return value is used by
574  *  parseFile to determine if there is more file to parse.
575  */
576
577 static int
578 parseCharWidths (FILE *fp, register int *cwi)
579 {
580   BOOL cont = TRUE, save = (cwi != NULL);
581   int pos = 0, error = AFM_ok;
582   register char *keyword;
583
584   while (cont)
585     {
586       keyword = token (fp);
587       /* Have reached an early and unexpected EOF. */
588       /* Set flag and stop parsing */
589       if (keyword == NULL)
590         {
591           error = AFM_earlyEOF;
592           break; /* get out of loop */
593         }
594       if (!save)        
595         /* get tokens until the end of the Char Metrics section without */
596         /* saving any of the data*/
597         switch (recognize (keyword))
598           {                             
599           case ENDCHARMETRICS:
600             cont = FALSE;
601             break;
602           case ENDFONTMETRICS:
603             cont = FALSE;
604             error = normalEOF;
605             break;
606           default:
607             break;
608           } /* switch */
609       else
610         /* otherwise parse entire char metrics section, saving */
611         /* only the char x-width info */
612         switch (recognize (keyword))
613           {
614           case COMMENT:
615             keyword = linetoken (fp);
616             break;
617           case CODE:
618             keyword = token (fp);
619             pos = atoi (keyword);
620             break;
621           case XYWIDTH:
622             /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
623             keyword = token (fp); keyword = token (fp); /* eat values */
624             error = AFM_parseError;
625             break;
626           case XWIDTH:
627             keyword = token (fp);
628             if (pos >= 0) /* ignore unmapped chars */
629               cwi[pos] = atoi (keyword);
630             break;
631           case ENDCHARMETRICS:
632             cont = FALSE;
633             break;
634           case ENDFONTMETRICS:
635             cont = FALSE;
636             error = normalEOF;
637             break;
638           case CHARNAME:        /* eat values (so doesn't cause AFM_parseError) */
639             keyword = token (fp);
640             break;
641           case CHARBBOX:
642             keyword = token (fp); keyword = token (fp);
643             keyword = token (fp); keyword = token (fp);
644             break;
645           case LIGATURE:
646             keyword = token (fp); keyword = token (fp);
647             break;
648           case NOPE:
649           default:
650             error = AFM_parseError;
651             break;
652           } /* switch */
653     } /* while */
654
655   return (error);
656
657 } /* parseCharWidths */
658
659
660 /************************* parseCharMetrics ************************/
661
662 /*  This function is called by parseFile if the caller of parseFile
663  *  requested that all character metric information be saved
664  * (as opposed to only the character width information).
665  *
666  *  parseCharMetrics is passed in a pointer to an array of records
667  *  to hold information on a per character basis. This function
668  *  parses the Character Metrics section storing all character
669  *  metric information for the ALL characters (mapped and unmapped)
670  *  into the array.
671  *
672  *  This function returns an error code specifying whether there was
673  *  a premature EOF or a parsing error. This return value is used by
674  *  parseFile to determine if there is more file to parse.
675  */
676
677 static int
678 parseCharMetrics (FILE *fp, register AFM_Font_info *fi)
679 {
680   BOOL cont = TRUE, firstTime = TRUE;
681   int error = AFM_ok, count = 0;
682   register AFM_CharMetricInfo *temp = fi->cmi;
683   register char *keyword;
684
685   while (cont)
686     {
687       keyword = token (fp);
688       if (keyword == NULL)
689         {
690           error = AFM_earlyEOF;
691           break; /* get out of loop */
692         }
693       switch (recognize (keyword))
694         {
695         case COMMENT:
696           keyword = linetoken (fp);
697           break;
698         case CODE:
699           if (count < fi->numOfChars)
700             {
701               if (firstTime)
702                 firstTime = FALSE;
703               else
704                 temp++;
705               temp->code = atoi (token (fp));
706               count++;
707             }
708           else
709             {
710               warning ("Too many metrics.");
711               error = AFM_parseError;
712               cont = FALSE;
713             }
714           break;
715         case XYWIDTH:
716           temp->wx = atoi (token (fp));
717           temp->wy = atoi (token (fp));
718           break;
719         case XWIDTH:
720           temp->wx = atoi (token (fp));
721           break;
722         
723         case CHARNAME:
724           keyword = token (fp);
725           temp->name = (char *) malloc (strlen (keyword) + 1);
726           strcpy (temp->name, keyword);
727           break;
728         
729         case CHARBBOX:
730           temp->charBBox.llx = atoi (token (fp));
731           temp->charBBox.lly = atoi (token (fp));
732           temp->charBBox.urx = atoi (token (fp));
733           temp->charBBox.ury = atoi (token (fp));
734           break;
735
736         case LIGATURE: {
737           AFM_Ligature **tail = & (temp->ligs);
738           AFM_Ligature *node = *tail;
739
740           if (*tail != NULL)
741             {
742               while (node->next != NULL)
743                 node = node->next;
744               tail = & (node->next);
745             }
746
747           *tail = (AFM_Ligature *) calloc (1, sizeof (AFM_Ligature));
748           keyword = token (fp);
749           (*tail)->succ = (char *) malloc (strlen (keyword) + 1);
750           strcpy ((*tail)->succ, keyword);
751           keyword = token (fp);
752           (*tail)->lig = (char *) malloc (strlen (keyword) + 1);
753           strcpy ((*tail)->lig, keyword);
754           break; }
755         case ENDCHARMETRICS:
756           cont = FALSE;;
757           break;
758         case ENDFONTMETRICS:
759           cont = FALSE;
760           error = normalEOF;
761           break;
762         case NOPE:
763         default:
764           warning ("Unknown token");
765         
766           error = AFM_parseError;
767           break;
768         } /* switch */
769     } /* while */
770
771   if ((error == AFM_ok) && (count != fi->numOfChars))
772     {
773       warning ("Incorrect char count");
774       error = AFM_parseError;
775     }
776   return (error);
777
778 } /* parseCharMetrics */
779
780
781
782 /************************* parseAFM_TrackKernData ***********************/
783
784 /*  This function is called by "parseFile". It will parse the AFM File
785  *  up to the "EndTrackKern" or "EndKernData" keywords. It will save the
786  *  track kerning data if requested by the caller of parseFile.
787  *
788  *  parseAFM_TrackKernData is passed in a pointer to the FontInfo record.
789  *  If data is to be saved, the FontInfo record will already contain
790  *  a valid pointer to storage for the track kerning data.
791  *
792  *  This function returns an error code specifying whether there was
793  *  a premature EOF or a parsing error. This return value is used by
794  *  parseFile to determine if there is more file to parse.
795  */
796
797 static int
798 parseAFM_TrackKernData (FILE *fp, register AFM_Font_info *fi)
799 {
800   BOOL cont = TRUE, save = (fi->tkd != NULL);
801   int pos = 0, error = AFM_ok, tcount = 0;
802   register char *keyword;
803
804   while (cont)
805     {
806       keyword = token (fp);
807
808       if (keyword == NULL)
809         {
810           error = AFM_earlyEOF;
811           break; /* get out of loop */
812         }
813       if (!save)
814         /* get tokens until the end of the Track Kerning Data */
815         /* section without saving any of the data */
816         switch (recognize (keyword))
817           {
818           case ENDTRACKKERN:
819           case ENDKERNDATA:
820             cont = FALSE;
821             break;
822           case ENDFONTMETRICS:
823             cont = FALSE;
824             error = normalEOF;
825             break;
826           default:
827             break;
828           } /* switch */
829       else
830         /* otherwise parse entire Track Kerning Data section, */
831         /* saving the data */
832         switch (recognize (keyword))
833           {
834           case COMMENT:
835 #ifdef METATYPE1_BUG    
836           case GENERATED:
837 #endif  
838             keyword = linetoken (fp);
839             break;
840           case TRACKKERN:
841             if (tcount < fi->numOfTracks)
842               {
843                 keyword = token (fp);
844                 fi->tkd[pos].degree = atoi (keyword);
845                 keyword = token (fp);
846                 fi->tkd[pos].minPtSize = atof (keyword);
847                 if (errno == ERANGE) error = AFM_parseError;
848                 keyword = token (fp);
849                 fi->tkd[pos].minKernAmt = atof (keyword);
850                 if (errno == ERANGE) error = AFM_parseError;
851                 keyword = token (fp);
852                 fi->tkd[pos].maxPtSize = atof (keyword);
853                 if (errno == ERANGE) error = AFM_parseError;
854                 keyword = token (fp);
855                 fi->tkd[pos++].maxKernAmt = atof (keyword);
856                 if (errno == ERANGE) error = AFM_parseError;
857                 tcount++;
858               }
859             else
860               {
861                 error = AFM_parseError;
862                 cont = FALSE;
863               }
864             break;
865           case ENDTRACKKERN:
866           case ENDKERNDATA:
867             cont = FALSE;
868             break;
869           case ENDFONTMETRICS:
870             cont = FALSE;
871             error = normalEOF;
872             break;
873           case NOPE:
874           default:
875             error = AFM_parseError;
876             break;
877           } /* switch */
878     } /* while */
879
880   if (error == AFM_ok && tcount != fi->numOfTracks)
881     error = AFM_parseError;
882
883   return (error);
884
885 } /* parseAFM_TrackKernData */
886
887
888 /************************* parseAFM_PairKernData ************************/
889
890 /*  This function is called by "parseFile". It will parse the AFM File
891  *  up to the "EndKernPairs" or "EndKernData" keywords. It will save
892  *  the pair kerning data if requested by the caller of parseFile.
893  *
894  *  parseAFM_PairKernData is passed in a pointer to the FontInfo record.
895  *  If data is to be saved, the FontInfo record will already contain
896  *  a valid pointer to storage for the pair kerning data.
897  *
898  *  This function returns an error code specifying whether there was
899  *  a premature EOF or a parsing error. This return value is used by
900  *  parseFile to determine if there is more file to parse.
901  */
902
903 static int
904 parseAFM_PairKernData (FILE *fp, register AFM_Font_info *fi)
905 {
906   BOOL cont = TRUE, save = (fi->pkd != NULL);
907   int pos = 0, error = AFM_ok, pcount = 0;
908   register char *keyword;
909
910   while (cont)
911     {
912       keyword = token (fp);
913
914       if (keyword == NULL)
915         {
916           error = AFM_earlyEOF;
917           break; /* get out of loop */
918         }
919       if (!save)
920         /* get tokens until the end of the Pair Kerning Data */
921         /* section without saving any of the data */
922         switch (recognize (keyword))
923           {
924           case ENDKERNPAIRS:
925           case ENDKERNDATA:
926             cont = FALSE;
927             break;
928           case ENDFONTMETRICS:
929             cont = FALSE;
930             error = normalEOF;
931             break;
932           default:
933             break;
934           } /* switch */
935       else
936         /* otherwise parse entire Pair Kerning Data section, */
937         /* saving the data */
938         switch (recognize (keyword))
939           {
940           case COMMENT:
941             keyword = linetoken (fp);
942             break;
943           case KERNPAIR:
944             if (pcount < fi->numOfPairs)
945               {
946                 keyword = token (fp);
947                 fi->pkd[pos].name1 = (char *)
948                   malloc (strlen (keyword) + 1);
949                 strcpy (fi->pkd[pos].name1, keyword);
950                 keyword = token (fp);
951                 fi->pkd[pos].name2 = (char *)
952                   malloc (strlen (keyword) + 1);
953                 strcpy (fi->pkd[pos].name2, keyword);
954                 keyword = token (fp);
955                 fi->pkd[pos].xamt = atoi (keyword);
956                 keyword = token (fp);
957                 fi->pkd[pos++].yamt = atoi (keyword);
958                 pcount++;
959               }
960             else
961               {
962                 error = AFM_parseError;
963                 cont = FALSE;
964               }
965             break;
966           case KERNPAIRXAMT:
967             if (pcount < fi->numOfPairs)
968               {
969                 keyword = token (fp);
970                 fi->pkd[pos].name1 = (char *)
971                   malloc (strlen (keyword) + 1);
972                 strcpy (fi->pkd[pos].name1, keyword);
973                 keyword = token (fp);
974                 fi->pkd[pos].name2 = (char *)
975                   malloc (strlen (keyword) + 1);
976                 strcpy (fi->pkd[pos].name2, keyword);
977                 keyword = token (fp);
978                 fi->pkd[pos++].xamt = atoi (keyword);
979                 pcount++;
980               }
981             else
982               {
983                 error = AFM_parseError;
984                 cont = FALSE;
985               }
986             break;
987           case ENDKERNPAIRS:
988           case ENDKERNDATA:
989             cont = FALSE;
990             break;
991           case ENDFONTMETRICS:
992             cont = FALSE;
993             error = normalEOF;
994             break;
995           case NOPE:
996           default:
997             error = AFM_parseError;
998             break;
999           } /* switch */
1000     } /* while */
1001
1002   if (error == AFM_ok && pcount != fi->numOfPairs)
1003     error = AFM_parseError;
1004
1005   return (error);
1006
1007 } /* parseAFM_PairKernData */
1008
1009
1010 /************************* parseAFM_CompCharData **************************/
1011
1012 /*  This function is called by "parseFile". It will parse the AFM File
1013  *  up to the "EndComposites" keyword. It will save the composite
1014  *  character data if requested by the caller of parseFile.
1015  *
1016  *  parseAFM_CompCharData is passed in a pointer to the FontInfo record, and
1017  *  a boolean representing if the data should be saved.
1018  *
1019  *  This function will create the appropriate amount of storage for
1020  *  the composite character data and store a pointer to the storage
1021  *  in the FontInfo record.
1022  *
1023  *  This function returns an error code specifying whether there was
1024  *  a premature EOF or a parsing error. This return value is used by
1025  *  parseFile to determine if there is more file to parse.
1026  */
1027
1028 static int
1029 parseAFM_CompCharData (FILE *fp, register AFM_Font_info *fi)
1030 {
1031   BOOL cont = TRUE, firstTime = TRUE, save = (fi->ccd != NULL);
1032   int pos = 0, j = 0, error = AFM_ok, ccount = 0, pcount = 0;
1033   register char *keyword;
1034
1035   while (cont)
1036     {
1037       keyword = token (fp);
1038       if (keyword == NULL)
1039         /* Have reached an early and unexpected EOF. */
1040         /* Set flag and stop parsing */
1041         {
1042           error = AFM_earlyEOF;
1043           break; /* get out of loop */
1044         }
1045       if (ccount > fi->numOfComps)
1046         {
1047           error = AFM_parseError;
1048           break; /* get out of loop */
1049         }
1050       if (!save)
1051         /* get tokens until the end of the Composite Character info */
1052         /* section without saving any of the data */
1053         switch (recognize (keyword))
1054           {
1055           case ENDCOMPOSITES:
1056             cont = FALSE;
1057             break;
1058           case ENDFONTMETRICS:
1059             cont = FALSE;
1060             error = normalEOF;
1061             break;
1062           default:
1063             break;
1064           } /* switch */
1065       else
1066         /* otherwise parse entire Composite Character info section, */
1067         /* saving the data */
1068         switch (recognize (keyword))
1069           {
1070           case COMMENT:
1071             keyword = linetoken (fp);
1072             break;
1073           case COMPCHAR:
1074             if (ccount < fi->numOfComps)
1075               {
1076                 keyword = token (fp);
1077                 if (pcount != fi->ccd[pos].numOfPieces)
1078                   error = AFM_parseError;
1079                 pcount = 0;
1080                 if (firstTime) firstTime = FALSE;
1081                 else pos++;
1082                 fi->ccd[pos].ccName = (char *)
1083                   malloc (strlen (keyword) + 1);
1084                 strcpy (fi->ccd[pos].ccName, keyword);
1085                 keyword = token (fp);
1086                 fi->ccd[pos].numOfPieces = atoi (keyword);
1087                 fi->ccd[pos].pieces = (AFM_Pcc *)
1088                   calloc (fi->ccd[pos].numOfPieces, sizeof (AFM_Pcc));
1089                 j = 0;
1090                 ccount++;
1091               }
1092             else
1093               {
1094                 error = AFM_parseError;
1095                 cont = FALSE;
1096               }
1097             break;
1098           case COMPCHARPIECE:
1099             if (pcount < fi->ccd[pos].numOfPieces)
1100               {
1101                 keyword = token (fp);
1102                 fi->ccd[pos].pieces[j].AFM_PccName = (char *)
1103                   malloc (strlen (keyword) + 1);
1104                 strcpy (fi->ccd[pos].pieces[j].AFM_PccName, keyword);
1105                 keyword = token (fp);
1106                 fi->ccd[pos].pieces[j].deltax = atoi (keyword);
1107                 keyword = token (fp);
1108                 fi->ccd[pos].pieces[j++].deltay = atoi (keyword);
1109                 pcount++;
1110               }
1111             else
1112               error = AFM_parseError;
1113             break;
1114           case ENDCOMPOSITES:
1115             cont = FALSE;
1116             break;
1117           case ENDFONTMETRICS:
1118             cont = FALSE;
1119             error = normalEOF;
1120             break;
1121           case NOPE:
1122           default:
1123             error = AFM_parseError;
1124             break;
1125           } /* switch */
1126     } /* while */
1127
1128   if (error == AFM_ok && ccount != fi->numOfComps)
1129     error = AFM_parseError;
1130
1131   return (error);
1132
1133 } /* parseAFM_CompCharData */
1134
1135
1136
1137
1138 /*************************** 'PUBLIC' FUNCTION ********************/
1139
1140 void
1141 AFM_free (AFM_Font_info *fi)
1142 {
1143   if (fi->gfi) {
1144     free (fi->gfi->afmVersion);
1145     free (fi->gfi->fontName);
1146     free (fi->gfi->fullName);
1147     free (fi->gfi->familyName);
1148     free (fi->gfi->weight);
1149     free (fi->gfi->version);
1150     free (fi->gfi->notice);
1151     free (fi->gfi->encodingScheme);
1152     free (fi->gfi);
1153   }
1154
1155   /* This contains just scalars.  */
1156   free (fi->cwi);
1157
1158   if (fi->cmi) {
1159     int i;
1160     for (i = 0; i < fi->numOfChars; i++) {
1161       free (fi->cmi[i].name);
1162       while (fi->cmi[i].ligs) {
1163         AFM_Ligature *tmp;
1164         tmp = fi->cmi[i].ligs;
1165         free (tmp->succ);
1166         free (tmp->lig);
1167         free (tmp);
1168         fi->cmi[i].ligs = fi->cmi[i].ligs->next;
1169       }
1170     }
1171     free (fi->cmi);
1172   }
1173
1174   /* This contains just scalars.  */
1175   free (fi->tkd);
1176
1177   if (fi->pkd) {
1178     int i;
1179     for (i = 0; i < fi->numOfPairs; i++) {
1180       free (fi->pkd[i].name1);
1181       free (fi->pkd[i].name2);
1182     }
1183     free (fi->pkd);
1184   }
1185
1186   if (fi->ccd) {
1187     int i, j;
1188     for (i = 0; i < fi->numOfComps; i++) {
1189       free (fi->ccd[i].ccName);
1190       for (j = 0; j < fi->ccd[i].numOfPieces; j++) {
1191         free (fi->ccd[i].pieces[j].AFM_PccName);
1192       }
1193       free (fi->ccd[i].pieces);
1194     }
1195     free (fi->ccd);
1196   }
1197
1198   free (fi);
1199 }
1200
1201
1202 /*************************** parseFile *****************************/
1203
1204 /*  parseFile is the only 'public' procedure available. It is called
1205  *  from an application wishing to get information from an AFM file.
1206  *  The caller of this function is responsible for locating and opening
1207  *  an AFM file and handling all errors associated with that task.
1208  *
1209  *  parseFile expects 3 parameters: a vaild file pointer, a pointer
1210  *  to a (FontInfo *) variable (for which storage will be allocated and
1211  *  the data requested filled in), and a mask specifying which
1212  *  data from the AFM File should be saved in the FontInfo structure.
1213  *
1214  *  The file will be parsed and the requested data will be stored in
1215  *  a record of type FontInfo (refer to ParseAFM.h).
1216  *
1217  *  parseFile returns an error code as defined in parseAFM.h.
1218  *
1219  *  The position of the read/write pointer associated with the file
1220  *  pointer upon return of this function is undefined.
1221  */
1222
1223 int
1224 AFM_parseFile (FILE *fp, AFM_Font_info **fi, int flags)
1225 {
1226
1227   int code = AFM_ok;    /* return code from each of the parsing routines */
1228   int error = AFM_ok;   /* used as the return code from this function */
1229
1230   register char *keyword; /* used to store a token */   
1231
1232                         
1233   /* storage data for the global variable ident */                      
1234   if (!ident)
1235     ident = (char *) calloc (MAX_NAME, sizeof (char));
1236   if (ident == NULL)
1237     {
1238       error = AFM_storageProblem;
1239       return error;
1240     }
1241
1242   (*fi) = (AFM_Font_info *) calloc (1, sizeof (AFM_Font_info));
1243   if ((*fi) == NULL)
1244     {
1245       error = AFM_storageProblem;
1246       return error;
1247     }
1248
1249   if (flags & P_G)
1250     {
1251       (*fi)->gfi = (AFM_GlobalFontInfo *) calloc (1,
1252                                                   sizeof (AFM_GlobalFontInfo));
1253       if ((*fi)->gfi == NULL)
1254         {
1255           error = AFM_storageProblem;
1256           return error;
1257         }
1258     }
1259
1260   /* The AFM File begins with Global Font Information. This section */
1261   /* will be parsed whether or not information should be saved. */
1262   code = parseGlobals (fp, (*fi)->gfi);
1263
1264   if (code < 0)
1265     error = code;
1266
1267   /* The Global Font Information is followed by the Character Metrics */
1268   /* section. Which procedure is used to parse this section depends on */
1269   /* how much information should be saved. If all of the metrics info */
1270   /* is wanted, parseCharMetrics is called. If only the character widths */
1271   /* is wanted, parseCharWidths is called. parseCharWidths will also */
1272   /* be called in the case that no character data is to be saved, just */
1273   /* to parse through the section. */
1274
1275   if ((code != normalEOF) && (code != AFM_earlyEOF))
1276     {
1277       (*fi)->numOfChars = atoi (token (fp));
1278       if (flags & (P_M ^ P_W))
1279         {
1280           (*fi)->cmi = (AFM_CharMetricInfo *)
1281             calloc ((*fi)->numOfChars, sizeof (AFM_CharMetricInfo));
1282           if ((*fi)->cmi == NULL)
1283             {
1284               error = AFM_storageProblem;
1285               return error;
1286             }
1287           code = parseCharMetrics (fp, *fi);
1288         }
1289       else
1290         {
1291           if (flags & P_W)
1292             {
1293               (*fi)->cwi = (int *) calloc (256, sizeof (int));
1294               if ((*fi)->cwi == NULL)
1295                 {
1296                   error = AFM_storageProblem;
1297                   return (error);
1298                 }
1299             }
1300           /* parse section regardless */
1301           code = parseCharWidths (fp, (*fi)->cwi);
1302         } /* else */
1303     } /* if */
1304
1305   if ((error != AFM_earlyEOF) && (code < 0))
1306     error = code;
1307
1308   /* The remaining sections of the AFM are optional. This code will */
1309   /* look at the next keyword in the file to determine what section */
1310   /* is next, and then allocate the appropriate amount of storage */
1311   /* for the data (if the data is to be saved) and call the */
1312   /* appropriate parsing routine to parse the section. */
1313
1314   while ((code != normalEOF) && (code != AFM_earlyEOF))
1315     {
1316       keyword = token (fp);
1317       if (keyword == NULL)
1318         /* Have reached an early and unexpected EOF. */
1319         /* Set flag and stop parsing */
1320         {
1321           code = AFM_earlyEOF;
1322           break; /* get out of loop */
1323         }
1324       switch (recognize (keyword))
1325         {
1326         case STARTKERNDATA:
1327           break;
1328         case ENDKERNDATA:
1329           break;
1330         case STARTTRACKKERN:
1331           keyword = token (fp);
1332           if (flags & P_T)
1333             {
1334               (*fi)->numOfTracks = atoi (keyword);
1335               (*fi)->tkd = (AFM_TrackKernData *)
1336                 calloc ((*fi)->numOfTracks, sizeof (AFM_TrackKernData));
1337               if ((*fi)->tkd == NULL)
1338                 {
1339                   error = AFM_storageProblem;
1340                   return (error);
1341                 }
1342             } /* if */
1343           code = parseAFM_TrackKernData (fp, *fi);
1344           break;
1345         case STARTKERNPAIRS:
1346           keyword = token (fp);
1347           if (flags & P_P)
1348             {
1349               (*fi)->numOfPairs = atoi (keyword);
1350               (*fi)->pkd = (AFM_PairKernData *)
1351                 calloc ((*fi)->numOfPairs, sizeof (AFM_PairKernData));
1352               if ((*fi)->pkd == NULL)
1353                 {
1354                   error = AFM_storageProblem;
1355                   return (error);
1356                 }
1357             } /* if */
1358           code = parseAFM_PairKernData (fp, *fi);
1359           break;
1360         case STARTCOMPOSITES:
1361           keyword = token (fp);
1362           if (flags & P_C)
1363             {
1364               (*fi)->numOfComps = atoi (keyword);
1365               (*fi)->ccd = (AFM_CompCharData *)
1366                 calloc ((*fi)->numOfComps, sizeof (AFM_CompCharData));
1367               if ((*fi)->ccd == NULL)
1368                 {
1369                   error = AFM_storageProblem;
1370                   return (error);
1371                 }
1372             } /* if */
1373           code = parseAFM_CompCharData (fp, *fi);
1374           break;
1375         case ENDFONTMETRICS:
1376           code = normalEOF;
1377           break;
1378         case NOPE:
1379         default:
1380           code = AFM_parseError;
1381           break;
1382         } /* switch */
1383
1384       if ((error != AFM_earlyEOF) && (code < 0))
1385         error = code;
1386
1387     } /* while */
1388
1389   if ((error != AFM_earlyEOF) && (code < 0))
1390     error = code;
1391
1392   if (ident != NULL) { free (ident); ident = NULL; }
1393
1394   return (error);
1395
1396 } /* parseFile */
1397