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