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