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