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