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