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  * Changes made for OpenOffice.org
25  *
26  *  10/24/2000 pl       - changed code to compile with c++-compilers
27  *                      - added namespace to avoid symbol clashes
28  *                      - replaced BOOL by bool
29  *                      - added function to free space allocated by parseFile
30  *  10/26/2000 pl       - added additional keys
31  *                      - added ability to parse slightly broken files
32  *                      - added charwidth member to GlobalFontInfo
33  *  04/26/2001 pl       - added OpenOffice header
34  *  10/19/2005 pl       - performance increase:
35  *                         - fread file in one pass
36  *                         - replace file io by buffer access
37  *  10/20/2005 pl       - performance increase:
38  *                         - use one table lookup in token() routine
39  *                           instead of many conditions
40  *                         - return token length in toke() routine
41  *                         - use hash lookup instead of binary search
42  *                           in recognize() routine
43  */
44 
45 // MARKER(update_precomp.py): autogen include statement, do not remove
46 #include "precompiled_vcl.hxx"
47 
48 /* parseAFM.c
49  *
50  * This file is used in conjuction with the parseAFM.h header file.
51  * This file contains several procedures that are used to parse AFM
52  * files. It is intended to work with an application program that needs
53  * font metric information. The program can be used as is by making a
54  * procedure call to "parseFile" (passing in the expected parameters)
55  * and having it fill in a data structure with the data from the
56  * AFM file, or an application developer may wish to customize this
57  * code.
58  *
59  * There is also a file, parseAFMclient.c, that is a sample application
60  * showing how to call the "parseFile" procedure and how to use the data
61  * after "parseFile" has returned.
62  *
63  * Please read the comments in parseAFM.h and parseAFMclient.c.
64  *
65  * History:
66  *  original: DSM  Thu Oct 20 17:39:59 PDT 1988
67  *  modified: DSM  Mon Jul  3 14:17:50 PDT 1989
68  *    - added 'storageProblem' return code
69  *    - fixed bug of not allocating extra byte for string duplication
70  *    - fixed typos
71  *  modified: DSM  Tue Apr  3 11:18:34 PDT 1990
72  *    - added free(ident) at end of parseFile routine
73  *  modified: DSM  Tue Jun 19 10:16:29 PDT 1990
74  *    - changed (width == 250) to (width = 250) in initializeArray
75  */
76 
77 #include <stdio.h>
78 #include <string.h>
79 #include <stdlib.h>
80 #include <errno.h>
81 #include <sys/file.h>
82 #include <sys/stat.h>
83 #include <math.h>
84 
85 #include "parseAFM.hxx"
86 #include "vcl/strhelper.hxx"
87 
88 #include "rtl/alloc.h"
89 
90 #define lineterm EOL    /* line terminating character */
91 #define normalEOF 1 /* return code from parsing routines used only */
92 /* in this module */
93 #define Space "space"   /* used in string comparison to look for the width */
94 /* of the space character to init the widths array */
95 #define False "false"   /* used in string comparison to check the value of */
96 /* boolean keys (e.g. IsFixedPitch)  */
97 
98 #define MATCH(A,B)      (strncmp((A),(B), MAX_NAME) == 0)
99 
100 namespace psp {
101 
102 class FileInputStream
103 {
104     char*         m_pMemory;
105     unsigned int        m_nPos;
106     unsigned int        m_nLen;
107     public:
108     FileInputStream( const char* pFilename );
109     ~FileInputStream();
110 
111     int getChar() { return (m_nPos < m_nLen) ? int(m_pMemory[m_nPos++]) : -1; }
112     void ungetChar()
113     {
114         if( m_nPos > 0 )
115             m_nPos--;
116     }
117     unsigned int tell() const { return m_nPos; }
118     void seek( unsigned int nPos )
119     // NOTE: do not check input data since only results of tell()
120     // get seek()ed in this file
121     { m_nPos = nPos; }
122 };
123 
124 FileInputStream::FileInputStream( const char* pFilename ) :
125     m_pMemory( NULL ),
126     m_nPos( 0 ),
127     m_nLen( 0 )
128 {
129     struct stat aStat;
130     if( ! stat( pFilename, &aStat ) &&
131         S_ISREG( aStat.st_mode )    &&
132         aStat.st_size > 0
133       )
134     {
135         FILE* fp = fopen( pFilename, "r" );
136         if( fp )
137         {
138             m_pMemory = (char*)rtl_allocateMemory( aStat.st_size );
139             m_nLen = (unsigned int)fread( m_pMemory, 1, aStat.st_size, fp );
140             fclose( fp );
141         }
142     }
143 }
144 
145 FileInputStream::~FileInputStream()
146 {
147     rtl_freeMemory( m_pMemory );
148 }
149 
150 /*************************** GLOBALS ***********************/
151 /* "shorts" for fast case statement
152  * The values of each of these enumerated items correspond to an entry in the
153  * table of strings defined below. Therefore, if you add a new string as
154  * new keyword into the keyStrings table, you must also add a corresponding
155  * parseKey AND it MUST be in the same position!
156  *
157  * IMPORTANT: since the sorting algorithm is a binary search, the strings of
158  * keywords must be placed in lexicographical order, below. [Therefore, the
159  * enumerated items are not necessarily in lexicographical order, depending
160  * on the name chosen. BUT, they must be placed in the same position as the
161  * corresponding key string.] The NOPE shall remain in the last position,
162  * since it does not correspond to any key string, and it is used in the
163  * "recognize" procedure to calculate how many possible keys there are.
164  */
165 
166 // some metrics have Ascent, Descent instead Ascender, Descender or Em
167 // which is not allowed per afm spcification, but let us handle
168 // this gently
169 enum parseKey {
170     ASCENDER, ASCENT, CHARBBOX, CODE, COMPCHAR, CODEHEX, CAPHEIGHT, CHARWIDTH, CHARACTERSET, CHARACTERS, COMMENT,
171     DESCENDER, DESCENT, EM, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES, ENDDIRECTION,
172     ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN,
173     FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME, ISBASEFONT, ISFIXEDPITCH,
174     ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, MAPPINGSCHEME, METRICSSETS, CHARNAME,
175     NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES, STARTDIRECTION,
176     STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS,
177     STARTTRACKKERN, STDHW, STDVW, TRACKKERN, UNDERLINEPOSITION,
178     UNDERLINETHICKNESS, VVECTOR, VERSION, XYWIDTH, X0WIDTH, XWIDTH, WEIGHT, XHEIGHT,
179     NOPE
180 };
181 
182 /*************************** PARSING ROUTINES **************/
183 
184 /*************************** token *************************/
185 
186 /*  A "AFM file Conventions" tokenizer. That means that it will
187  *  return the next token delimited by white space.  See also
188  *  the `linetoken' routine, which does a similar thing but
189  *  reads all tokens until the next end-of-line.
190  */
191 
192 // token white space is ' ', '\n', '\r', ',', '\t', ';'
193 static const bool is_white_Array[ 256 ] =
194 {   false, false, false, false, false, false, false, false, // 0-7
195     false,  true,  true, false, false,  true, false, false, // 8-15
196     false, false, false, false, false, false, false, false, // 16-23
197     false, false, false, false, false, false, false, false, // 24-31
198      true, false, false, false, false, false, false, false, // 32-39
199     false, false, false, false,  true, false, false, false, // 40-47
200     false, false, false, false, false, false, false, false, // 48-55
201     false, false, false,  true, false, false, false, false, // 56-63
202 
203     false, false, false, false, false, false, false, false, // 64 -
204     false, false, false, false, false, false, false, false,
205     false, false, false, false, false, false, false, false,
206     false, false, false, false, false, false, false, false,
207     false, false, false, false, false, false, false, false,
208     false, false, false, false, false, false, false, false,
209     false, false, false, false, false, false, false, false,
210     false, false, false, false, false, false, false, false, // 127
211 
212     false, false, false, false, false, false, false, false, // 128 -
213     false, false, false, false, false, false, false, false,
214     false, false, false, false, false, false, false, false,
215     false, false, false, false, false, false, false, false,
216     false, false, false, false, false, false, false, false,
217     false, false, false, false, false, false, false, false,
218     false, false, false, false, false, false, false, false,
219     false, false, false, false, false, false, false, false, // 191
220 
221     false, false, false, false, false, false, false, false, // 192 -
222     false, false, false, false, false, false, false, false,
223     false, false, false, false, false, false, false, false,
224     false, false, false, false, false, false, false, false,
225     false, false, false, false, false, false, false, false,
226     false, false, false, false, false, false, false, false,
227     false, false, false, false, false, false, false, false,
228     false, false, false, false, false, false, false, false, // 255
229 };
230 // token delimiters are ' ', '\n', '\r', '\t', ':', ';'
231 static const bool is_delimiter_Array[ 256 ] =
232 {   false, false, false, false, false, false, false, false, // 0-7
233     false,  true,  true, false, false,  true, false, false, // 8-15
234     false, false, false, false, false, false, false, false, // 16-23
235     false, false, false, false, false, false, false, false, // 24-31
236      true, false, false, false, false, false, false, false, // 32-39
237     false, false, false, false, false, false, false, false, // 40-47
238     false, false, false, false, false, false, false, false, // 48-55
239     false, false,  true,  true, false, false, false, false, // 56-63
240 
241     false, false, false, false, false, false, false, false, // 64 -
242     false, false, false, false, false, false, false, false,
243     false, false, false, false, false, false, false, false,
244     false, false, false, false, false, false, false, false,
245     false, false, false, false, false, false, false, false,
246     false, false, false, false, false, false, false, false,
247     false, false, false, false, false, false, false, false,
248     false, false, false, false, false, false, false, false, // 127
249 
250     false, false, false, false, false, false, false, false, // 128 -
251     false, false, false, false, false, false, false, false,
252     false, false, false, false, false, false, false, false,
253     false, false, false, false, false, false, false, false,
254     false, false, false, false, false, false, false, false,
255     false, false, false, false, false, false, false, false,
256     false, false, false, false, false, false, false, false,
257     false, false, false, false, false, false, false, false, // 191
258 
259     false, false, false, false, false, false, false, false, // 192 -
260     false, false, false, false, false, false, false, false,
261     false, false, false, false, false, false, false, false,
262     false, false, false, false, false, false, false, false,
263     false, false, false, false, false, false, false, false,
264     false, false, false, false, false, false, false, false,
265     false, false, false, false, false, false, false, false,
266     false, false, false, false, false, false, false, false, // 255
267 };
268 static char *token( FileInputStream* stream, int& rLen )
269 {
270     static char ident[MAX_NAME]; /* storage buffer for keywords */
271 
272     int ch, idx;
273 
274     /* skip over white space */
275     // relies on EOF = -1
276     while( is_white_Array[ (ch = stream->getChar()) & 255 ] )
277         ;
278 
279     idx = 0;
280     while( ch != -1 && ! is_delimiter_Array[ ch & 255 ] && idx < MAX_NAME-1 )
281     {
282         ident[idx++] = ch;
283         ch = stream->getChar();
284     }
285 
286     if (ch == -1 && idx < 1) return ((char *)NULL);
287     if (idx >= 1 && ch != ':' && ch != -1) stream->ungetChar();
288     if (idx < 1 ) ident[idx++] = ch;    /* single-character token */
289     ident[idx] = 0;
290     rLen = idx;
291 
292     return(ident);  /* returns pointer to the token */
293 
294 } /* token */
295 
296 
297 /*************************** linetoken *************************/
298 
299 /*  "linetoken" will get read all tokens until the EOL character from
300  *  the given stream.  This is used to get any arguments that can be
301  *  more than one word (like Comment lines and FullName).
302  */
303 
304 static char *linetoken( FileInputStream* stream )
305 {
306     static char ident[MAX_NAME]; /* storage buffer for keywords */
307     int ch, idx;
308 
309     while ((ch = stream->getChar()) == ' ' || ch == '\t' ) ;
310 
311     idx = 0;
312     while (ch != -1 && ch != lineterm && ch != '\r' && idx < MAX_NAME-1 )
313     {
314         ident[idx++] = ch;
315         ch = stream->getChar();
316     } /* while */
317 
318     stream->ungetChar();
319     ident[idx] = 0;
320 
321     return(ident);  /* returns pointer to the token */
322 
323 } /* linetoken */
324 
325 
326 /*************************** recognize *************************/
327 
328 /*  This function tries to match a string to a known list of
329  *  valid AFM entries (check the keyStrings array above).
330  *  "ident" contains everything from white space through the
331  *  next space, tab, or ":" character.
332  *
333  *  The algorithm is a standard Knuth binary search.
334  */
335 #include "afm_hash.cpp"
336 
337 static inline enum parseKey recognize( register char* ident, int len)
338 {
339     const hash_entry* pEntry = AfmKeywordHash::in_word_set( ident, len );
340     return pEntry ? pEntry->eKey : NOPE;
341 
342 } /* recognize */
343 
344 
345 /************************* parseGlobals *****************************/
346 
347 /*  This function is called by "parseFile". It will parse the AFM file
348  *  up to the "StartCharMetrics" keyword, which essentially marks the
349  *  end of the Global Font Information and the beginning of the character
350  *  metrics information.
351  *
352  *  If the caller of "parseFile" specified that it wanted the Global
353  *  Font Information (as defined by the "AFM file Specification"
354  *  document), then that information will be stored in the returned
355  *  data structure.
356  *
357  *  Any Global Font Information entries that are not found in a
358  *  given file, will have the usual default initialization value
359  *  for its type (i.e. entries of type int will be 0, etc).
360  *
361  *  This function returns an error code specifying whether there was
362  *  a premature EOF or a parsing error. This return value is used by
363  *  parseFile to determine if there is more file to parse.
364  */
365 
366 static int parseGlobals( FileInputStream* fp, register GlobalFontInfo* gfi )
367 {
368     bool cont = true, save = (gfi != NULL);
369     int error = ok;
370     register char *keyword;
371     int direction = -1;
372     int tokenlen;
373 
374     while (cont)
375     {
376         keyword = token(fp, tokenlen);
377 
378         if (keyword == NULL)
379             /* Have reached an early and unexpected EOF. */
380             /* Set flag and stop parsing */
381         {
382             error = earlyEOF;
383             break;   /* get out of loop */
384         }
385         if (!save)
386             /* get tokens until the end of the Global Font info section */
387             /* without saving any of the data */
388             switch (recognize(keyword, tokenlen))
389             {
390                 case STARTCHARMETRICS:
391                     cont = false;
392                     break;
393                 case ENDFONTMETRICS:
394                     cont = false;
395                     error = normalEOF;
396                     break;
397                 default:
398                     break;
399             } /* switch */
400         else
401             /* otherwise parse entire global font info section, */
402             /* saving the data */
403             switch(recognize(keyword, tokenlen))
404             {
405                 case STARTFONTMETRICS:
406                     if ((keyword = token(fp,tokenlen)) != NULL)
407                         gfi->afmVersion = strdup( keyword );
408                     break;
409                 case COMMENT:
410                     keyword = linetoken(fp);
411                     break;
412                 case FONTNAME:
413                     if ((keyword = token(fp,tokenlen)) != NULL)
414                         gfi->fontName = strdup( keyword );
415                     break;
416                 case ENCODINGSCHEME:
417                     if ((keyword = token(fp,tokenlen)) != NULL)
418                         gfi->encodingScheme = strdup( keyword );
419                     break;
420                 case FULLNAME:
421                     if ((keyword = linetoken(fp)) != NULL)
422                         gfi->fullName = strdup( keyword );
423                     break;
424                 case FAMILYNAME:
425                     if ((keyword = linetoken(fp)) != NULL)
426                         gfi->familyName = strdup( keyword );
427                     break;
428                 case WEIGHT:
429                     if ((keyword = token(fp,tokenlen)) != NULL)
430                         gfi->weight = strdup( keyword );
431                     break;
432                 case ITALICANGLE:
433                     if ((keyword = token(fp,tokenlen)) != NULL)
434                         gfi->italicAngle = StringToDouble( keyword );
435                     break;
436                 case ISFIXEDPITCH:
437                     if ((keyword = token(fp,tokenlen)) != NULL)
438                     {
439                         if (MATCH(keyword, False))
440                             gfi->isFixedPitch = 0;
441                         else
442                             gfi->isFixedPitch = 1;
443                     }
444                     break;
445                 case UNDERLINEPOSITION:
446                     if ((keyword = token(fp,tokenlen)) != NULL)
447                         gfi->underlinePosition = atoi(keyword);
448                     break;
449                 case UNDERLINETHICKNESS:
450                     if ((keyword = token(fp,tokenlen)) != NULL)
451                         gfi->underlineThickness = atoi(keyword);
452                     break;
453                 case VERSION:
454                     if ((keyword = token(fp,tokenlen)) != NULL)
455                         gfi->version = strdup( keyword );
456                     break;
457                 case NOTICE:
458                     if ((keyword = linetoken(fp)) != NULL)
459                         gfi->notice = strdup( keyword );
460                     break;
461                 case FONTBBOX:
462                     if ((keyword = token(fp,tokenlen)) != NULL)
463                         gfi->fontBBox.llx = atoi(keyword);
464                     if ((keyword = token(fp,tokenlen)) != NULL)
465                         gfi->fontBBox.lly = atoi(keyword);
466                     if ((keyword = token(fp,tokenlen)) != NULL)
467                         gfi->fontBBox.urx = atoi(keyword);
468                     if ((keyword = token(fp,tokenlen)) != NULL)
469                         gfi->fontBBox.ury = atoi(keyword);
470                     break;
471                 case CAPHEIGHT:
472                     if ((keyword = token(fp,tokenlen)) != NULL)
473                         gfi->capHeight = atoi(keyword);
474                     break;
475                 case XHEIGHT:
476                     if ((keyword = token(fp,tokenlen)) != NULL)
477                         gfi->xHeight = atoi(keyword);
478                     break;
479                 case DESCENT:
480                     if ((keyword = token(fp,tokenlen)) != NULL)
481                         gfi->descender = -atoi(keyword);
482                     break;
483                 case DESCENDER:
484                     if ((keyword = token(fp,tokenlen)) != NULL)
485                         gfi->descender = atoi(keyword);
486                     break;
487                 case ASCENT:
488                 case ASCENDER:
489                     if ((keyword = token(fp,tokenlen)) != NULL)
490                         gfi->ascender = atoi(keyword);
491                     break;
492                 case STARTCHARMETRICS:
493                     cont = false;
494                     break;
495                 case ENDFONTMETRICS:
496                     cont = false;
497                     error = normalEOF;
498                     break;
499                 case EM:
500                     // skip one token
501                     keyword = token(fp,tokenlen);
502                     break;
503                 case STARTDIRECTION:
504                     if ((keyword = token(fp,tokenlen)) != NULL)
505                         direction = atoi(keyword);
506                     break; /* ignore this for now */
507                 case ENDDIRECTION:
508                     break; /* ignore this for now */
509                 case MAPPINGSCHEME:
510                     keyword = token(fp,tokenlen);
511                     break; /* ignore     this for now */
512                 case CHARACTERS:
513                     keyword = token(fp,tokenlen);
514                     break; /* ignore this for now */
515                 case ISBASEFONT:
516                     keyword = token(fp,tokenlen);
517                     break; /* ignore this for now */
518                 case CHARACTERSET:
519                     keyword=token(fp,tokenlen); //ignore
520                     break;
521                 case STDHW:
522                     keyword=token(fp,tokenlen); //ignore
523                     break;
524                 case STDVW:
525                     keyword=token(fp,tokenlen); //ignore
526                     break;
527                 case CHARWIDTH:
528                     if ((keyword = token(fp,tokenlen)) != NULL)
529                     {
530                         if (direction == 0)
531                             gfi->charwidth = atoi(keyword);
532                     }
533                     keyword = token(fp,tokenlen);
534                     /* ignore y-width for now */
535                     break;
536                 case METRICSSETS:
537                     keyword = token(fp,tokenlen);
538                     break; /* ignore this for now */
539                 case NOPE:
540                 default:
541                     error = parseError;
542                     break;
543             } /* switch */
544     } /* while */
545 
546     return(error);
547 
548 } /* parseGlobals */
549 
550 
551 #if 0
552 /************************* initializeArray ************************/
553 
554 /*  Unmapped character codes are (at Adobe Systems) assigned the
555  *  width of the space character (if one exists) else they get the
556  *  value of 250 ems. This function initializes all entries in the
557  *  char widths array to have this value. Then any mapped character
558  *  codes will be replaced with the width of the appropriate character
559  *  when parsing the character metric section.
560 
561  *  This function parses the Character Metrics Section looking
562  *  for a space character (by comparing character names). If found,
563  *  the width of the space character will be used to initialize the
564  *  values in the array of character widths.
565  *
566  *  Before returning, the position of the read/write pointer of the
567  *  FileInputStream is reset to be where it was upon entering this function.
568  */
569 
570 static int initializeArray( FileInputStream* fp, register int* cwi)
571 {
572     bool cont = true, found = false;
573     unsigned int opos = fp->tell();
574     int code = 0, width = 0, i = 0, error = 0, tokenlen;
575     register char *keyword;
576 
577     while (cont)
578     {
579         keyword = token(fp,tokenlen);
580         if (keyword == NULL)
581         {
582             error = earlyEOF;
583             break; /* get out of loop */
584         }
585         switch(recognize(keyword,tokenlen))
586         {
587             case COMMENT:
588                 keyword = linetoken(fp);
589                 break;
590             case CODE:
591                 if ((keyword = token(fp,tokenlen)) != NULL)
592                     code = atoi(keyword);
593                 break;
594             case CODEHEX:
595                 if ((keyword = token(fp,tokenlen)) != NULL)
596                     sscanf(keyword,"<%x>", &code);
597                 break;
598             case XWIDTH:
599                 if ((keyword = token(fp,tokenlen)) != NULL)
600                     width = atoi(keyword);
601                 break;
602             case X0WIDTH:
603                 (void) token(fp,tokenlen);
604                 break;
605             case CHARNAME:
606                 if ((keyword = token(fp,tokenlen)) != NULL)
607                     if (MATCH(keyword, Space))
608                     {
609                         cont = false;
610                         found = true;
611                     }
612                 break;
613             case ENDCHARMETRICS:
614                 cont = false;
615                 break;
616             case ENDFONTMETRICS:
617                 cont = false;
618                 error = normalEOF;
619                 break;
620             case NOPE:
621             default:
622                 error = parseError;
623                 break;
624         } /* switch */
625     } /* while */
626 
627     if (!found)
628         width = 250;
629 
630     for (i = 0; i < 256; ++i)
631         cwi[i] = width;
632 
633     fp->seek(opos);
634 
635     return(error);
636 
637 } /* initializeArray */
638 #endif
639 
640 /************************* parseCharWidths **************************/
641 
642 /*  This function is called by "parseFile". It will parse the AFM file
643  *  up to the "EndCharMetrics" keyword. It will save the character
644  *  width info (as opposed to all of the character metric information)
645  *  if requested by the caller of parseFile. Otherwise, it will just
646  *  parse through the section without saving any information.
647  *
648  *  If data is to be saved, parseCharWidths is passed in a pointer
649  *  to an array of widths that has already been initialized by the
650  *  standard value for unmapped character codes. This function parses
651  *  the Character Metrics section only storing the width information
652  *  for the encoded characters into the array using the character code
653  *  as the index into that array.
654  *
655  *  This function returns an error code specifying whether there was
656  *  a premature EOF or a parsing error. This return value is used by
657  *  parseFile to determine if there is more file to parse.
658  */
659 
660 static int parseCharWidths( FileInputStream* fp, register int* cwi)
661 {
662     bool cont = true, save = (cwi != NULL);
663     int pos = 0, error = ok, tokenlen;
664     register char *keyword;
665 
666     while (cont)
667     {
668         keyword = token(fp,tokenlen);
669         /* Have reached an early and unexpected EOF. */
670         /* Set flag and stop parsing */
671         if (keyword == NULL)
672         {
673             error = earlyEOF;
674             break; /* get out of loop */
675         }
676         if (!save)
677             /* get tokens until the end of the Char Metrics section without */
678             /* saving any of the data*/
679             switch (recognize(keyword,tokenlen))
680             {
681                 case ENDCHARMETRICS:
682                     cont = false;
683                     break;
684                 case ENDFONTMETRICS:
685                     cont = false;
686                     error = normalEOF;
687                     break;
688                 default:
689                     break;
690             } /* switch */
691         else
692             /* otherwise parse entire char metrics section, saving */
693             /* only the char x-width info */
694             switch(recognize(keyword,tokenlen))
695             {
696                 case COMMENT:
697                     keyword = linetoken(fp);
698                     break;
699                 case CODE:
700                     if ((keyword = token(fp,tokenlen)) != NULL)
701                         pos = atoi(keyword);
702                     break;
703                 case XYWIDTH:
704                     /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
705                     keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); /* eat values */
706                     error = parseError;
707                     break;
708                 case CODEHEX:
709                     if ((keyword = token(fp,tokenlen)) != NULL)
710                         sscanf(keyword, "<%x>", &pos);
711                     break;
712                 case X0WIDTH:
713                     (void) token(fp,tokenlen);
714                     break;
715                 case XWIDTH:
716                     if ((keyword = token(fp,tokenlen)) != NULL)
717                         if (pos >= 0) /* ignore unmapped chars */
718                             cwi[pos] = atoi(keyword);
719                     break;
720                 case ENDCHARMETRICS:
721                     cont = false;
722                     break;
723                 case ENDFONTMETRICS:
724                     cont = false;
725                     error = normalEOF;
726                     break;
727                 case CHARNAME:  /* eat values (so doesn't cause parseError) */
728                     keyword = token(fp,tokenlen);
729                     break;
730                 case CHARBBOX:
731                     keyword = token(fp,tokenlen); keyword = token(fp,tokenlen);
732                     keyword = token(fp,tokenlen); keyword = token(fp,tokenlen);
733                     break;
734                 case LIGATURE:
735                     keyword = token(fp,tokenlen); keyword = token(fp,tokenlen);
736                     break;
737                 case VVECTOR:
738                     keyword = token(fp,tokenlen);
739                     keyword = token(fp,tokenlen);
740                     break;
741                 case NOPE:
742                 default:
743                     error = parseError;
744                     break;
745             } /* switch */
746     } /* while */
747 
748     return(error);
749 
750 } /* parseCharWidths */
751 
752 
753 /*
754  * number of char metrics is almost allways inaccurate, so be gentle and try to
755  * adapt our internal storage by adjusting the allocated list
756  */
757 
758 static int
759 reallocFontMetrics( void **pp_fontmetrics, int *p_oldcount, int n_newcount, unsigned int n_size )
760 {
761     char *p_tmpmetrics = NULL;
762 
763     if ((pp_fontmetrics == NULL) || (*pp_fontmetrics == NULL))
764         return storageProblem;
765 
766     if (*p_oldcount == n_newcount)
767         return ok;
768 
769     p_tmpmetrics = (char*)realloc(*pp_fontmetrics, n_newcount * n_size);
770     if (p_tmpmetrics == NULL)
771         return storageProblem;
772 
773     if ( n_newcount > *p_oldcount )
774     {
775         char *p_inimetrics = p_tmpmetrics + n_size * *p_oldcount;
776         int   n_inimetrics = n_size * (n_newcount - *p_oldcount);
777         memset( p_inimetrics, 0, n_inimetrics );
778     }
779 
780     *pp_fontmetrics = p_tmpmetrics;
781     *p_oldcount    = n_newcount;
782 
783     return ok;
784 }
785 
786 static unsigned int
787 enlargeCount( unsigned int n_oldcount )
788 {
789     unsigned int n_newcount = n_oldcount + n_oldcount / 5;
790     if (n_oldcount == n_newcount )
791         n_newcount = n_oldcount + 5;
792 
793     return n_newcount;
794 }
795 
796 /************************* parseCharMetrics ************************/
797 
798 /*  This function is called by parseFile if the caller of parseFile
799  *  requested that all character metric information be saved
800  *  (as opposed to only the character width information).
801  *
802  *  parseCharMetrics is passed in a pointer to an array of records
803  *  to hold information on a per character basis. This function
804  *  parses the Character Metrics section storing all character
805  *  metric information for the ALL characters (mapped and unmapped)
806  *  into the array.
807  *
808  *  This function returns an error code specifying whether there was
809  *  a premature EOF or a parsing error. This return value is used by
810  *  parseFile to determine if there is more file to parse.
811  */
812 
813 static int parseCharMetrics( FileInputStream* fp, register FontInfo* fi)
814 {
815     bool cont = true, firstTime = true;
816     int error = ok, count = 0, tokenlen;
817     register CharMetricInfo *temp = fi->cmi;
818     register char *keyword;
819 
820     while (cont)
821     {
822         keyword = token(fp,tokenlen);
823         if (keyword == NULL)
824         {
825             error = earlyEOF;
826             break; /* get out of loop */
827         }
828         switch(recognize(keyword,tokenlen))
829         {
830             case COMMENT:
831                 keyword = linetoken(fp);
832                 break;
833             case CODE:
834                 if (!(count < fi->numOfChars))
835                 {
836                     reallocFontMetrics( (void**)&(fi->cmi),
837                                         &(fi->numOfChars), enlargeCount(fi->numOfChars),
838                                         sizeof(CharMetricInfo) );
839                     temp = &(fi->cmi[ count - 1 ]);
840                 }
841                 if (count < fi->numOfChars)
842                 {
843                     if (firstTime) firstTime = false;
844                     else temp++;
845                     if ((keyword = token(fp,tokenlen)) != NULL)
846                         temp->code = atoi(keyword);
847                     if (fi->gfi && fi->gfi->charwidth)
848                         temp->wx = fi->gfi->charwidth;
849                     count++;
850                 }
851                 else
852                 {
853                     error = parseError;
854                     cont = false;
855                 }
856                 break;
857             case CODEHEX:
858                 if (!(count < fi->numOfChars ))
859                 {
860                     reallocFontMetrics( (void**)&(fi->cmi),
861                                         &(fi->numOfChars), enlargeCount(fi->numOfChars),
862                                         sizeof(CharMetricInfo) );
863                     temp = &(fi->cmi[ count - 1 ]);
864                 }
865                 if (count < fi->numOfChars) {
866                     if (firstTime)
867                         firstTime = false;
868                     else
869                         temp++;
870                     if ((keyword = token(fp,tokenlen)) != NULL)
871                         sscanf(keyword,"<%x>", &temp->code);
872                     if (fi->gfi && fi->gfi->charwidth)
873                         temp->wx = fi->gfi->charwidth;
874                     count++;
875                 }
876                 else {
877                     error = parseError;
878                     cont = false;
879                 }
880                 break;
881             case XYWIDTH:
882                 if ((keyword = token(fp,tokenlen)) != NULL)
883                     temp->wx = atoi(keyword);
884                 if ((keyword = token(fp,tokenlen)) != NULL)
885                     temp->wy = atoi(keyword);
886                 break;
887             case X0WIDTH:
888                 if ((keyword = token(fp,tokenlen)) != NULL)
889                     temp->wx = atoi(keyword);
890                 break;
891             case XWIDTH:
892                 if ((keyword = token(fp,tokenlen)) != NULL)
893                     temp->wx = atoi(keyword);
894                 break;
895             case CHARNAME:
896                 if ((keyword = token(fp,tokenlen)) != NULL)
897                     temp->name = (char *)strdup(keyword);
898                 break;
899             case CHARBBOX:
900                 if ((keyword = token(fp,tokenlen)) != NULL)
901                     temp->charBBox.llx = atoi(keyword);
902                 if ((keyword = token(fp,tokenlen)) != NULL)
903                     temp->charBBox.lly = atoi(keyword);
904                 if ((keyword = token(fp,tokenlen)) != NULL)
905                     temp->charBBox.urx = atoi(keyword);
906                 if ((keyword = token(fp,tokenlen)) != NULL)
907                     temp->charBBox.ury = atoi(keyword);
908                 break;
909             case LIGATURE: {
910                 Ligature **tail = &(temp->ligs);
911                 Ligature *node = *tail;
912 
913                 if (*tail != NULL)
914                 {
915                     while (node->next != NULL)
916                         node = node->next;
917                     tail = &(node->next);
918                 }
919 
920                 *tail = (Ligature *) calloc(1, sizeof(Ligature));
921                 if ((keyword = token(fp,tokenlen)) != NULL)
922                     (*tail)->succ = (char *)strdup(keyword);
923                 if ((keyword = token(fp,tokenlen)) != NULL)
924                     (*tail)->lig = (char *)strdup(keyword);
925                 break; }
926             case ENDCHARMETRICS:
927                 cont = false;;
928                 break;
929             case ENDFONTMETRICS:
930                 cont = false;
931                 error = normalEOF;
932                 break;
933             case VVECTOR:
934                 keyword = token(fp,tokenlen);
935                 keyword = token(fp,tokenlen);
936                 break;
937             case NOPE:
938             default:
939                 error = parseError;
940                 break;
941         } /* switch */
942     } /* while */
943 
944     if ((error == ok) && (count != fi->numOfChars))
945         error = reallocFontMetrics( (void**)&(fi->cmi), &(fi->numOfChars),
946                                     count, sizeof(CharMetricInfo) );
947 
948     if ((error == ok) && (count != fi->numOfChars))
949         error = parseError;
950 
951     return(error);
952 
953 } /* parseCharMetrics */
954 
955 
956 
957 /************************* parseTrackKernData ***********************/
958 
959 /*  This function is called by "parseFile". It will parse the AFM file
960  *  up to the "EndTrackKern" or "EndKernData" keywords. It will save the
961  *  track kerning data if requested by the caller of parseFile.
962  *
963  *  parseTrackKernData is passed in a pointer to the FontInfo record.
964  *  If data is to be saved, the FontInfo record will already contain
965  *  a valid pointer to storage for the track kerning data.
966  *
967  *  This function returns an error code specifying whether there was
968  *  a premature EOF or a parsing error. This return value is used by
969  *  parseFile to determine if there is more file to parse.
970  */
971 
972 static int parseTrackKernData( FileInputStream* fp, register FontInfo* fi)
973 {
974     bool cont = true, save = (fi->tkd != NULL);
975     int pos = 0, error = ok, tcount = 0, tokenlen;
976     register char *keyword;
977 
978     while (cont)
979     {
980         keyword = token(fp,tokenlen);
981 
982         if (keyword == NULL)
983         {
984             error = earlyEOF;
985             break; /* get out of loop */
986         }
987         if (!save)
988             /* get tokens until the end of the Track Kerning Data */
989             /* section without saving any of the data */
990             switch(recognize(keyword,tokenlen))
991             {
992                 case ENDTRACKKERN:
993                 case ENDKERNDATA:
994                     cont = false;
995                     break;
996                 case ENDFONTMETRICS:
997                     cont = false;
998                     error = normalEOF;
999                     break;
1000                 default:
1001                     break;
1002             } /* switch */
1003         else
1004             /* otherwise parse entire Track Kerning Data section, */
1005             /* saving the data */
1006             switch(recognize(keyword,tokenlen))
1007             {
1008                 case COMMENT:
1009                     keyword = linetoken(fp);
1010                     break;
1011                 case TRACKKERN:
1012                     if (!(tcount < fi->numOfTracks))
1013                     {
1014                         reallocFontMetrics( (void**)&(fi->tkd), &(fi->numOfTracks),
1015                                             enlargeCount(fi->numOfTracks), sizeof(TrackKernData) );
1016                     }
1017 
1018                     if (tcount < fi->numOfTracks)
1019                     {
1020                         if ((keyword = token(fp,tokenlen)) != NULL)
1021                             fi->tkd[pos].degree = atoi(keyword);
1022                         if ((keyword = token(fp,tokenlen)) != NULL)
1023                             fi->tkd[pos].minPtSize = StringToDouble(keyword);
1024                         if ((keyword = token(fp,tokenlen)) != NULL)
1025                             fi->tkd[pos].minKernAmt = StringToDouble(keyword);
1026                         if ((keyword = token(fp,tokenlen)) != NULL)
1027                             fi->tkd[pos].maxPtSize = StringToDouble(keyword);
1028                         if ((keyword = token(fp,tokenlen)) != NULL)
1029                             fi->tkd[pos++].maxKernAmt = StringToDouble(keyword);
1030                         tcount++;
1031                     }
1032                     else
1033                     {
1034                         error = parseError;
1035                         cont = false;
1036                     }
1037                     break;
1038                 case ENDTRACKKERN:
1039                 case ENDKERNDATA:
1040                     cont = false;
1041                     break;
1042                 case ENDFONTMETRICS:
1043                     cont = false;
1044                     error = normalEOF;
1045                     break;
1046                 case NOPE:
1047                 default:
1048                     error = parseError;
1049                     break;
1050             } /* switch */
1051     } /* while */
1052 
1053     if (error == ok && tcount != fi->numOfTracks)
1054         error = reallocFontMetrics( (void**)&(fi->tkd), &(fi->numOfTracks),
1055                                     tcount, sizeof(TrackKernData) );
1056 
1057     if (error == ok && tcount != fi->numOfTracks)
1058         error = parseError;
1059 
1060     return(error);
1061 
1062 } /* parseTrackKernData */
1063 
1064 
1065 /************************* parsePairKernData ************************/
1066 
1067 /*  This function is called by "parseFile". It will parse the AFM file
1068  *  up to the "EndKernPairs" or "EndKernData" keywords. It will save
1069  *  the pair kerning data if requested by the caller of parseFile.
1070  *
1071  *  parsePairKernData is passed in a pointer to the FontInfo record.
1072  *  If data is to be saved, the FontInfo record will already contain
1073  *  a valid pointer to storage for the pair kerning data.
1074  *
1075  *  This function returns an error code specifying whether there was
1076  *  a premature EOF or a parsing error. This return value is used by
1077  *  parseFile to determine if there is more file to parse.
1078  */
1079 
1080 static int parsePairKernData( FileInputStream* fp, register FontInfo* fi)
1081 {
1082     bool cont = true, save = (fi->pkd != NULL);
1083     int pos = 0, error = ok, pcount = 0, tokenlen;
1084     register char *keyword;
1085 
1086     while (cont)
1087     {
1088         keyword = token(fp,tokenlen);
1089 
1090         if (keyword == NULL)
1091         {
1092             error = earlyEOF;
1093             break; /* get out of loop */
1094         }
1095         if (!save)
1096             /* get tokens until the end of the Pair Kerning Data */
1097             /* section without saving any of the data */
1098             switch(recognize(keyword,tokenlen))
1099             {
1100                 case ENDKERNPAIRS:
1101                 case ENDKERNDATA:
1102                     cont = false;
1103                     break;
1104                 case ENDFONTMETRICS:
1105                     cont = false;
1106                     error = normalEOF;
1107                     break;
1108                 default:
1109                     break;
1110             } /* switch */
1111         else
1112             /* otherwise parse entire Pair Kerning Data section, */
1113             /* saving the data */
1114             switch(recognize(keyword,tokenlen))
1115             {
1116                 case COMMENT:
1117                     keyword = linetoken(fp);
1118                     break;
1119                 case KERNPAIR:
1120                     if (!(pcount < fi->numOfPairs))
1121                     {
1122                         reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs),
1123                                             enlargeCount(fi->numOfPairs), sizeof(PairKernData) );
1124                     }
1125                     if (pcount < fi->numOfPairs)
1126                     {
1127                         if ((keyword = token(fp,tokenlen)) != NULL)
1128                             fi->pkd[pos].name1 = strdup( keyword );
1129                         if ((keyword = token(fp,tokenlen)) != NULL)
1130                             fi->pkd[pos].name2 = strdup( keyword );
1131                         if ((keyword = token(fp,tokenlen)) != NULL)
1132                             fi->pkd[pos].xamt = atoi(keyword);
1133                         if ((keyword = token(fp,tokenlen)) != NULL)
1134                             fi->pkd[pos++].yamt = atoi(keyword);
1135                         pcount++;
1136                     }
1137                     else
1138                     {
1139                         error = parseError;
1140                         cont = false;
1141                     }
1142                     break;
1143                 case KERNPAIRXAMT:
1144                     if (!(pcount < fi->numOfPairs))
1145                     {
1146                         reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs),
1147                                             enlargeCount(fi->numOfPairs), sizeof(PairKernData) );
1148                     }
1149                     if (pcount < fi->numOfPairs)
1150                     {
1151                         if ((keyword = token(fp,tokenlen)) != NULL)
1152                             fi->pkd[pos].name1 = strdup( keyword );
1153                         if ((keyword = token(fp,tokenlen)) != NULL)
1154                             fi->pkd[pos].name2 = strdup( keyword );
1155                         if ((keyword = token(fp,tokenlen)) != NULL)
1156                             fi->pkd[pos++].xamt = atoi(keyword);
1157                         pcount++;
1158                     }
1159                     else
1160                     {
1161                         error = parseError;
1162                         cont = false;
1163                     }
1164                     break;
1165                 case ENDKERNPAIRS:
1166                 case ENDKERNDATA:
1167                     cont = false;
1168                     break;
1169                 case ENDFONTMETRICS:
1170                     cont = false;
1171                     error = normalEOF;
1172                     break;
1173                 case NOPE:
1174                 default:
1175                     error = parseError;
1176                     break;
1177             } /* switch */
1178     } /* while */
1179 
1180     if ((error == ok) && (pcount != fi->numOfPairs))
1181         error = reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs),
1182                                     pcount, sizeof(PairKernData) );
1183 
1184     if (error == ok && pcount != fi->numOfPairs)
1185         error = parseError;
1186 
1187     return(error);
1188 
1189 } /* parsePairKernData */
1190 
1191 
1192 /************************* parseCompCharData **************************/
1193 
1194 /*  This function is called by "parseFile". It will parse the AFM file
1195  *  up to the "EndComposites" keyword. It will save the composite
1196  *  character data if requested by the caller of parseFile.
1197  *
1198  *  parseCompCharData is passed in a pointer to the FontInfo record, and
1199  *  a boolean representing if the data should be saved.
1200  *
1201  *  This function will create the appropriate amount of storage for
1202  *  the composite character data and store a pointer to the storage
1203  *  in the FontInfo record.
1204  *
1205  *  This function returns an error code specifying whether there was
1206  *  a premature EOF or a parsing error. This return value is used by
1207  *  parseFile to determine if there is more file to parse.
1208  */
1209 
1210 static int parseCompCharData( FileInputStream* fp, register FontInfo* fi)
1211 {
1212     bool cont = true, firstTime = true, save = (fi->ccd != NULL);
1213     int pos = 0, j = 0, error = ok, ccount = 0, pcount = 0, tokenlen;
1214     register char *keyword;
1215 
1216     while (cont)
1217     {
1218         keyword = token(fp,tokenlen);
1219         if (keyword == NULL)
1220             /* Have reached an early and unexpected EOF. */
1221             /* Set flag and stop parsing */
1222         {
1223             error = earlyEOF;
1224             break; /* get out of loop */
1225         }
1226         if (ccount > fi->numOfComps)
1227         {
1228             reallocFontMetrics( (void**)&(fi->ccd), &(fi->numOfComps),
1229                                 enlargeCount(fi->numOfComps), sizeof(CompCharData) );
1230         }
1231         if (ccount > fi->numOfComps)
1232         {
1233             error = parseError;
1234             break; /* get out of loop */
1235         }
1236         if (!save)
1237             /* get tokens until the end of the Composite Character info */
1238             /* section without saving any of the data */
1239             switch(recognize(keyword,tokenlen))
1240             {
1241                 case ENDCOMPOSITES:
1242                     cont = false;
1243                     break;
1244                 case ENDFONTMETRICS:
1245                     cont = false;
1246                     error = normalEOF;
1247                     break;
1248                 case COMMENT:
1249                 case COMPCHAR:
1250                     keyword = linetoken(fp);
1251                     break;
1252                 default:
1253                     break;
1254             } /* switch */
1255         else
1256             /* otherwise parse entire Composite Character info section, */
1257             /* saving the data */
1258             switch(recognize(keyword,tokenlen))
1259             {
1260                 case COMMENT:
1261                     keyword = linetoken(fp);
1262                     break;
1263                 case COMPCHAR:
1264                     if (!(ccount < fi->numOfComps))
1265                     {
1266                         reallocFontMetrics( (void**)&(fi->ccd), &(fi->numOfComps),
1267                                             enlargeCount(fi->numOfComps), sizeof(CompCharData) );
1268                     }
1269                     if (ccount < fi->numOfComps)
1270                     {
1271                         keyword = token(fp,tokenlen);
1272                         if (pcount != fi->ccd[pos].numOfPieces)
1273                             error = parseError;
1274                         pcount = 0;
1275                         if (firstTime) firstTime = false;
1276                         else pos++;
1277                         fi->ccd[pos].ccName = strdup( keyword );
1278                         if ((keyword = token(fp,tokenlen)) != NULL)
1279                             fi->ccd[pos].numOfPieces = atoi(keyword);
1280                         fi->ccd[pos].pieces = (Pcc *)
1281                             calloc(fi->ccd[pos].numOfPieces, sizeof(Pcc));
1282                         j = 0;
1283                         ccount++;
1284                     }
1285                     else
1286                     {
1287                         error = parseError;
1288                         cont = false;
1289                     }
1290                     break;
1291                 case COMPCHARPIECE:
1292                     if (pcount < fi->ccd[pos].numOfPieces)
1293                     {
1294                         if ((keyword = token(fp,tokenlen)) != NULL)
1295                             fi->ccd[pos].pieces[j].pccName = strdup( keyword );
1296                         if ((keyword = token(fp,tokenlen)) != NULL)
1297                             fi->ccd[pos].pieces[j].deltax = atoi(keyword);
1298                         if ((keyword = token(fp,tokenlen)) != NULL)
1299                             fi->ccd[pos].pieces[j++].deltay = atoi(keyword);
1300                         pcount++;
1301                     }
1302                     else
1303                         error = parseError;
1304                     break;
1305                 case ENDCOMPOSITES:
1306                     cont = false;
1307                     break;
1308                 case ENDFONTMETRICS:
1309                     cont = false;
1310                     error = normalEOF;
1311                     break;
1312                 case NOPE:
1313                 default:
1314                     error = parseError;
1315                     break;
1316             } /* switch */
1317     } /* while */
1318 
1319     if (error == ok && ccount != fi->numOfComps)
1320         reallocFontMetrics( (void**)&(fi->ccd), &(fi->numOfComps),
1321                             ccount, sizeof(CompCharData) );
1322 
1323     if (error == ok && ccount != fi->numOfComps)
1324         error = parseError;
1325 
1326     return(error);
1327 
1328 } /* parseCompCharData */
1329 
1330 
1331 
1332 
1333 /*************************** 'PUBLIC' FUNCTION ********************/
1334 
1335 
1336 /*************************** parseFile *****************************/
1337 
1338 /*  parseFile is the only 'public' procedure available. It is called
1339  *  from an application wishing to get information from an AFM file.
1340  *  The caller of this function is responsible for locating and opening
1341  *  an AFM file and handling all errors associated with that task.
1342  *
1343  *  parseFile expects 3 parameters: a filename pointer, a pointer
1344  *  to a (FontInfo *) variable (for which storage will be allocated and
1345  *  the data requested filled in), and a mask specifying which
1346  *  data from the AFM file should be saved in the FontInfo structure.
1347  *
1348  *  The file will be parsed and the requested data will be stored in
1349  *  a record of type FontInfo (refer to ParseAFM.h).
1350  *
1351  *  parseFile returns an error code as defined in parseAFM.h.
1352  *
1353  *  The position of the read/write pointer associated with the file
1354  *  pointer upon return of this function is undefined.
1355  */
1356 
1357 int parseFile( const char* pFilename, FontInfo** fi, FLAGS flags)
1358 {
1359     FileInputStream aFile( pFilename );
1360 
1361     int code = ok;  /* return code from each of the parsing routines */
1362     int error = ok; /* used as the return code from this function */
1363     int tokenlen;
1364 
1365     register char *keyword; /* used to store a token */
1366 
1367 
1368     (*fi) = (FontInfo *) calloc(1, sizeof(FontInfo));
1369     if ((*fi) == NULL) {error = storageProblem; return(error);}
1370 
1371     if (flags & P_G)
1372     {
1373         (*fi)->gfi = (GlobalFontInfo *) calloc(1, sizeof(GlobalFontInfo));
1374         if ((*fi)->gfi == NULL) {error = storageProblem; return(error);}
1375     }
1376 
1377     /* The AFM file begins with Global Font Information. This section */
1378     /* will be parsed whether or not information should be saved. */
1379     code = parseGlobals(&aFile, (*fi)->gfi);
1380 
1381     if (code < 0) error = code;
1382 
1383     /* The Global Font Information is followed by the Character Metrics */
1384     /* section. Which procedure is used to parse this section depends on */
1385     /* how much information should be saved. If all of the metrics info */
1386     /* is wanted, parseCharMetrics is called. If only the character widths */
1387     /* is wanted, parseCharWidths is called. parseCharWidths will also */
1388     /* be called in the case that no character data is to be saved, just */
1389     /* to parse through the section. */
1390 
1391     if ((code != normalEOF) && (code != earlyEOF))
1392     {
1393         if ((keyword = token(&aFile,tokenlen)) != NULL)
1394             (*fi)->numOfChars = atoi(keyword);
1395         if (flags & (P_M ^ P_W))
1396         {
1397             (*fi)->cmi = (CharMetricInfo *)
1398                 calloc((*fi)->numOfChars, sizeof(CharMetricInfo));
1399             if ((*fi)->cmi == NULL) {error = storageProblem; return(error);}
1400             code = parseCharMetrics(&aFile, *fi);
1401         }
1402         else
1403         {
1404             if (flags & P_W)
1405             {
1406                 (*fi)->cwi = (int *) calloc(256, sizeof(int));
1407                 if ((*fi)->cwi == NULL)
1408                 {
1409                     error = storageProblem;
1410                     return(error);
1411                 }
1412             }
1413             /* parse section regardless */
1414             code = parseCharWidths(&aFile, (*fi)->cwi);
1415         } /* else */
1416     } /* if */
1417 
1418     if ((error != earlyEOF) && (code < 0)) error = code;
1419 
1420     /* The remaining sections of the AFM are optional. This code will */
1421     /* look at the next keyword in the file to determine what section */
1422     /* is next, and then allocate the appropriate amount of storage */
1423     /* for the data (if the data is to be saved) and call the */
1424     /* appropriate parsing routine to parse the section. */
1425 
1426     while ((code != normalEOF) && (code != earlyEOF))
1427     {
1428         keyword = token(&aFile,tokenlen);
1429         if (keyword == NULL)
1430             /* Have reached an early and unexpected EOF. */
1431             /* Set flag and stop parsing */
1432         {
1433             code = earlyEOF;
1434             break; /* get out of loop */
1435         }
1436         switch(recognize(keyword,tokenlen))
1437         {
1438             case STARTKERNDATA:
1439                 break;
1440             case ENDKERNDATA:
1441                 break;
1442             case STARTTRACKKERN:
1443                 keyword = token(&aFile,tokenlen);
1444                 if ((flags & P_T) && keyword)
1445                 {
1446                     (*fi)->numOfTracks = atoi(keyword);
1447                     (*fi)->tkd = (TrackKernData *)
1448                         calloc((*fi)->numOfTracks, sizeof(TrackKernData));
1449                     if ((*fi)->tkd == NULL)
1450                     {
1451                         error = storageProblem;
1452                         return(error);
1453                     }
1454                 } /* if */
1455                 code = parseTrackKernData(&aFile, *fi);
1456                 break;
1457             case STARTKERNPAIRS:
1458                 keyword = token(&aFile,tokenlen);
1459                 if ((flags & P_P) && keyword)
1460                 {
1461                     (*fi)->numOfPairs = atoi(keyword);
1462                     (*fi)->pkd = (PairKernData *)
1463                         calloc((*fi)->numOfPairs, sizeof(PairKernData));
1464                     if ((*fi)->pkd == NULL)
1465                     {
1466                         error = storageProblem;
1467                         return(error);
1468                     }
1469                 } /* if */
1470                 code = parsePairKernData(&aFile, *fi);
1471                 break;
1472             case STARTCOMPOSITES:
1473                 keyword = token(&aFile,tokenlen);
1474                 if ((flags & P_C) && keyword)
1475                 {
1476                     (*fi)->numOfComps = atoi(keyword);
1477                     (*fi)->ccd = (CompCharData *)
1478                         calloc((*fi)->numOfComps, sizeof(CompCharData));
1479                     if ((*fi)->ccd == NULL)
1480                     {
1481                         error = storageProblem;
1482                         return(error);
1483                     }
1484                 } /* if */
1485                 code = parseCompCharData(&aFile, *fi);
1486                 break;
1487             case ENDFONTMETRICS:
1488                 code = normalEOF;
1489                 break;
1490             case COMMENT:
1491                 linetoken(&aFile);
1492                 break;
1493             case NOPE:
1494             default:
1495                 code = parseError;
1496                 break;
1497         } /* switch */
1498 
1499         if ((error != earlyEOF) && (code < 0)) error = code;
1500 
1501     } /* while */
1502 
1503     if ((error != earlyEOF) && (code < 0)) error = code;
1504 
1505     return(error);
1506 
1507 } /* parseFile */
1508 
1509 void
1510 freeFontInfo (FontInfo *fi)
1511 {
1512     int i, j;
1513 
1514     if (fi->gfi)
1515     {
1516         free (fi->gfi->afmVersion);
1517         free (fi->gfi->fontName);
1518         free (fi->gfi->fullName);
1519         free (fi->gfi->familyName);
1520         free (fi->gfi->weight);
1521         free (fi->gfi->version);
1522         free (fi->gfi->notice);
1523         free (fi->gfi->encodingScheme);
1524         free (fi->gfi);
1525     }
1526 
1527     free (fi->cwi);
1528 
1529     if (fi->cmi)
1530     {
1531         for (i = 0; i < fi->numOfChars; i++)
1532         {
1533             Ligature *ligs;
1534             free (fi->cmi[i].name);
1535             ligs = fi->cmi[i].ligs;
1536             while (ligs)
1537             {
1538                 Ligature *tmp;
1539                 tmp = ligs;
1540                 ligs = ligs->next;
1541                 free (tmp->succ);
1542                 free (tmp->lig);
1543                 free (tmp);
1544             }
1545         }
1546         free (fi->cmi);
1547     }
1548 
1549     free (fi->tkd);
1550 
1551     if (fi->pkd)
1552     {
1553         for ( i = 0; i < fi->numOfPairs; i++)
1554         {
1555             free (fi->pkd[i].name1);
1556             free (fi->pkd[i].name2);
1557         }
1558         free (fi->pkd);
1559     }
1560 
1561     if (fi->ccd)
1562     {
1563         for (i = 0; i < fi->numOfComps; i++)
1564         {
1565             free (fi->ccd[i].ccName);
1566             for (j = 0; j < fi->ccd[i].numOfPieces; j++)
1567                 free (fi->ccd[i].pieces[j].pccName);
1568 
1569             free (fi->ccd[i].pieces);
1570         }
1571         free (fi->ccd);
1572     }
1573 
1574     free (fi);
1575 }
1576 
1577 } // namspace
1578