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