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