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