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