Back to index

tetex-bin  3.0
parseAFM.c
Go to the documentation of this file.
00001 /*--------------------------------------------------------------------------
00002   ----- File:        parseAFM.c
00003   ----- Author:      Adobe Systems Inc., modifications by
00004                      Rainer Menzner (Rainer.Menzner@web.de)
00005   ----- Date:        2002-10-26
00006   ----- Description: This file is part of the t1-library. It is the original
00007                      parseAFM.h modified at a few points, especially for
00008                    reading MSDOS-style AFM files.
00009   ----- Copyright:   t1lib is copyrighted (c) Rainer Menzner, 1996-2002.
00010                      As of version 0.5, t1lib is distributed under the
00011                    GNU General Public Library Lincense. The
00012                    conditions can be found in the files LICENSE and
00013                    LGPL, which should reside in the toplevel
00014                    directory of the distribution.  Please note that 
00015                    there are parts of t1lib that are subject to
00016                    other licenses:
00017                    The parseAFM-package is copyrighted by Adobe Systems
00018                    Inc.
00019                    The type1 rasterizer is copyrighted by IBM and the
00020                    X11-consortium.
00021   ----- Warranties:  Of course, there's NO WARRANTY OF ANY KIND :-)
00022   ----- Credits:     I want to thank IBM and the X11-consortium for making
00023                      their rasterizer freely available.
00024                    Also thanks to Piet Tutelaers for his ps2pk, from
00025                    which I took the rasterizer sources in a format
00026                    independent from X11.
00027                      Thanks to all people who make free software living!
00028 --------------------------------------------------------------------------*/
00029 
00030 /*
00031  * (C) 1988, 1989, 1990 by Adobe Systems Incorporated. All rights reserved.
00032  *
00033  * This file may be freely copied and redistributed as long as:
00034  *   1) This entire notice continues to be included in the file, 
00035  *   2) If the file has been modified in any way, a notice of such
00036  *      modification is conspicuously indicated.
00037  *
00038  * PostScript, Display PostScript, and Adobe are registered trademarks of
00039  * Adobe Systems Incorporated.
00040  * 
00041  * ************************************************************************
00042  * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT
00043  * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS
00044  * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR 
00045  * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY 
00046  * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, 
00047  * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
00048  * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
00049  * ************************************************************************
00050  */
00051 
00052 /* parseAFM.c
00053  * 
00054  * This file is used in conjuction with the parseAFM.h header file.
00055  * This file contains several procedures that are used to parse AFM
00056  * files. It is intended to work with an application program that needs
00057  * font metric information. The program can be used as is by making a
00058  * procedure call to "parseFile" (passing in the expected parameters)
00059  * and having it fill in a data structure with the data from the 
00060  * AFM file, or an application developer may wish to customize this
00061  * code.
00062  *
00063  * There is also a file, parseAFMclient.c, that is a sample application
00064  * showing how to call the "parseFile" procedure and how to use the data
00065  * after "parseFile" has returned.
00066  *
00067  * Please read the comments in parseAFM.h and parseAFMclient.c.
00068  *
00069  * History:
00070  *     original: DSM  Thu Oct 20 17:39:59 PDT 1988
00071  *  modified: DSM  Mon Jul  3 14:17:50 PDT 1989
00072  *    - added 'storageProblem' return code
00073  *       - fixed bug of not allocating extra byte for string duplication
00074  *    - fixed typos
00075  *  modified: DSM  Tue Apr  3 11:18:34 PDT 1990
00076  *    - added free(ident) at end of parseFile routine
00077  *  modified: DSM  Tue Jun 19 10:16:29 PDT 1990
00078  *    - changed (width == 250) to (width = 250) in initializeArray
00079  */
00080 
00081 #include <stdio.h>
00082 #include <stdlib.h>
00083 #include <errno.h>
00084 #include <sys/types.h>  /* this seems to be necessary on some SCO-systems */
00085 #if !defined(_MSC_VER)
00086 #  include <sys/file.h>
00087 #endif
00088 #include <math.h>
00089 #include <string.h>
00090 #include "parseAFM.h"
00091 #include "t1base.h"
00092 #include "t1misc.h"
00093 
00094 
00095 #define lineterm EOL /* line terminating character */
00096 #define normalEOF 1  /* return code from parsing routines used only */
00097                      /* in this module */
00098 #define Space "space"   /* used in string comparison to look for the width */
00099                      /* of the space character to init the widths array */
00100 #define False "false"   /* used in string comparison to check the value of */
00101                      /* boolean keys (e.g. IsFixedPitch)  */
00102 
00103 #define MATCH(A,B)          (strncmp((A),(B), MAX_NAME) == 0)
00104 
00105 
00106 
00107 /* Note: The functions token and linetoken are  extended to be able
00108    to parse AFM files with DOS-style line-ends under UNIX. I don't know
00109    why AFM files which according to Adobe are explicitly intended to
00110    be used under UNIX have MSDOS-style line ends.
00111 
00112    However, we parse the tokens based on the following atoms. This should
00113    work on any system.
00114 */
00115 #define CR       '\r'    /* a carriage return */
00116 #define LF       '\n'    /* a linefeed, which is a newline under UNIX */
00117 #define CTRL_Z   0x1A    /* some AFM files have this characters as an end
00118                          of file indicator. Who know why :) ? */
00119 
00120 
00121 /*************************** GLOBALS ***********************/
00122 
00123 static char *ident = NULL; /* storage buffer for keywords */
00124 
00125 
00126 /* "shorts" for fast case statement 
00127  * The values of each of these enumerated items correspond to an entry in the
00128  * table of strings defined below. Therefore, if you add a new string as 
00129  * new keyword into the keyStrings table, you must also add a corresponding
00130  * parseKey AND it MUST be in the same position!
00131  *
00132  * IMPORTANT: since the sorting algorithm is a binary search, the strings of
00133  * keywords must be placed in lexicographical order, below. [Therefore, the 
00134  * enumerated items are not necessarily in lexicographical order, depending 
00135  * on the name chosen. BUT, they must be placed in the same position as the 
00136  * corresponding key string.] The NOPE shall remain in the last position, 
00137  * since it does not correspond to any key string, and it is used in the 
00138  * "recognize" procedure to calculate how many possible keys there are.
00139  */
00140 
00141 typedef enum {
00142   ASCENDER, CHARBBOX, CODE, COMPCHAR, CAPHEIGHT, CHARACTERSET, COMMENT,
00143   DESCENDER, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES, 
00144   ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN, 
00145   FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME, ISFIXEDPITCH, 
00146   ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, CHARNAME, 
00147   NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES, 
00148   STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS, 
00149   STARTTRACKKERN, STDHW, STDVW, TRACKKERN, UNDERLINEPOSITION,
00150   UNDERLINETHICKNESS, VERSION, XYWIDTH, XWIDTH, WEIGHT, XHEIGHT,
00151   NOPE} PARSEKEY;
00152 
00153 
00154 /* keywords for the system:  
00155  * This a table of all of the current strings that are vaild AFM keys.
00156  * Each entry can be referenced by the appropriate parseKey value (an
00157  * enumerated data type defined above). If you add a new keyword here, 
00158  * a corresponding parseKey MUST be added to the enumerated data type
00159  * defined above, AND it MUST be added in the same position as the 
00160  * string is in this table.
00161  *
00162  * IMPORTANT: since the sorting algorithm is a binary search, the keywords
00163  * must be placed in lexicographical order. And, NULL should remain at the
00164  * end.
00165  */
00166 
00167 static char *keyStrings[] = {
00168   "Ascender", "B", "C", "CC", "CapHeight", "CharacterSet", "Comment",
00169   "Descender", "EncodingScheme", "EndCharMetrics", "EndComposites", 
00170   "EndFontMetrics", "EndKernData", "EndKernPairs", "EndTrackKern", 
00171   "FamilyName", "FontBBox", "FontName", "FullName", "IsFixedPitch", 
00172   "ItalicAngle", "KP", "KPX", "L", "N", 
00173   "Notice", "PCC", "StartCharMetrics", "StartComposites", 
00174   "StartFontMetrics", "StartKernData", "StartKernPairs", 
00175   "StartTrackKern", "StdHW", "StdVW", "TrackKern", "UnderlinePosition",
00176   "UnderlineThickness", "Version", "W", "WX", "Weight", "XHeight",
00177   NULL };
00178   
00179 
00180 /*************************** PARSING ROUTINES **************/ 
00181   
00182 /*************************** token *************************/
00183 
00184 /*  A "AFM File Conventions" tokenizer. That means that it will
00185  *  return the next token delimited by white space.  See also
00186  *  the `linetoken' routine, which does a similar thing but 
00187  *  reads all tokens until the next end-of-line.
00188  */
00189 static char *token(stream) 
00190   FILE *stream;
00191 {
00192     int ch, idx;
00193 
00194     /* skip over white space, instead of the systems EOL-character we check
00195        explicitly for CR and LF as well as for ^Z. */
00196     while ((ch = fgetc(stream)) == ' ' || ch == CR || ch == LF || ch == CTRL_Z || 
00197           ch == ',' || ch == '\t' || ch == ';');
00198     
00199     idx = 0;
00200     
00201     while (ch != EOF && ch != ' ' && ch != CR  && ch != LF &&
00202           ch != CTRL_Z && ch != '\t' && ch != ':' && ch != ';'){
00203       ident[idx++] = ch;
00204       ch = fgetc(stream);
00205     } /* while */
00206     
00207     if (ch == EOF && idx < 1) {
00208       return ((char *)NULL);
00209     }
00210     if (idx >= 1 && ch != ':' ) {
00211       ungetc(ch, stream);
00212     }
00213     if (idx < 1 ) {
00214       ident[idx++] = ch;    /* single-character token */
00215     }
00216     ident[idx] = 0;
00217     
00218     return(ident);   /* returns pointer to the token */
00219 
00220 } /* token */
00221 
00222 
00223 /*************************** linetoken *************************/
00224 
00225 /*  "linetoken" will get read all tokens until the EOL character from
00226  *  the given stream.  This is used to get any arguments that can be
00227  *  more than one word (like Comment lines and FullName).
00228  */
00229 
00230 static char *linetoken(stream)
00231   FILE *stream;
00232 {
00233     int ch, idx;
00234 
00235     while ((ch = fgetc(stream)) == ' ' || ch == '\t' ); 
00236     
00237     idx = 0;
00238     while (ch != EOF && ch != CR  && ch != LF && ch != CTRL_Z) 
00239     {
00240         ident[idx++] = ch;
00241         ch = fgetc(stream);
00242     } /* while */
00243     
00244     ungetc(ch, stream);
00245     ident[idx] = 0;
00246 
00247     return(ident);   /* returns pointer to the token */
00248 
00249 } /* linetoken */
00250 
00251 
00252 /*************************** recognize *************************/
00253 
00254 /*  This function tries to match a string to a known list of
00255  *  valid AFM entries (check the keyStrings array above). 
00256  *  "ident" contains everything from white space through the
00257  *  next space, tab, or ":" character.
00258  *
00259  *  The algorithm is a standard Knuth binary search.
00260  */
00261 
00262 static PARSEKEY recognize(ident)
00263   register char *ident;
00264 {
00265     int lower = 0, upper = (int) NOPE, midpoint=NOPE, cmpvalue;
00266     BOOL found = T1LIB_FALSE;
00267 
00268     while ((upper >= lower) && !found)
00269     {
00270         midpoint = (lower + upper)/2;
00271         if (keyStrings[midpoint] == NULL) break;
00272         cmpvalue = strncmp(ident, keyStrings[midpoint], MAX_NAME);
00273         if (cmpvalue == 0) found = T1LIB_TRUE;
00274         else if (cmpvalue < 0) upper = midpoint - 1;
00275         else lower = midpoint + 1;
00276     } /* while */
00277 
00278     if (found) return (PARSEKEY) midpoint;
00279     else return NOPE;
00280     
00281 } /* recognize */
00282 
00283 
00284 /************************* parseGlobals *****************************/
00285 
00286 /*  This function is called by "parseFile". It will parse the AFM File
00287  *  up to the "StartCharMetrics" keyword, which essentially marks the
00288  *  end of the Global Font Information and the beginning of the character
00289  *  metrics information. 
00290  *
00291  *  If the caller of "parseFile" specified that it wanted the Global
00292  *  Font Information (as defined by the "AFM File Specification"
00293  *  document), then that information will be stored in the returned 
00294  *  data structure.
00295  *
00296  *  Any Global Font Information entries that are not found in a 
00297  *  given file, will have the usual default initialization value
00298  *  for its type (i.e. entries of type int will be 0, etc).
00299  *
00300  *  This function returns an error code specifying whether there was 
00301  *  a premature EOF or a parsing error. This return value is used by 
00302  *  parseFile to determine if there is more file to parse.
00303  */
00304  
00305 static BOOL parseGlobals(fp, gfi)
00306   FILE *fp;
00307   register GlobalFontInfo *gfi;
00308 {  
00309     BOOL cont = T1LIB_TRUE, save = (gfi != NULL);
00310     int error = ok;
00311     register char *keyword;
00312     
00313     while (cont)
00314     {
00315         keyword = token(fp);
00316         
00317         if (keyword == NULL)
00318           /* Have reached an early and unexpected EOF. */
00319           /* Set flag and stop parsing */
00320         {
00321             error = earlyEOF;
00322             break;   /* get out of loop */
00323         }
00324         if (!save)   
00325           /* get tokens until the end of the Global Font info section */
00326           /* without saving any of the data */
00327             switch (recognize(keyword))  
00328             {                      
00329                 case STARTCHARMETRICS:
00330                     cont = T1LIB_FALSE;
00331                     break;
00332                 case ENDFONTMETRICS:      
00333                     cont = T1LIB_FALSE;
00334                     error = normalEOF;
00335                     break;
00336                 default:
00337                     break;
00338             } /* switch */
00339         else
00340           /* otherwise parse entire global font info section, */
00341           /* saving the data */
00342             switch(recognize(keyword))
00343             {
00344                 case STARTFONTMETRICS:
00345                     keyword = token(fp);
00346                     gfi->afmVersion = (char *) malloc(strlen(keyword) + 1);
00347                     strcpy(gfi->afmVersion, keyword);
00348                     break;
00349                 case COMMENT:
00350                 /* We ignore the following keywords. They are only listed
00351                    here in order to prevent from "Unknown Keyword" errors. */
00352                 case CHARACTERSET:
00353                 case STDHW:
00354                 case STDVW:
00355                     keyword = linetoken(fp);
00356                     break;
00357                 case FONTNAME:
00358                     keyword = linetoken(fp);
00359                     gfi->fontName = (char *) malloc(strlen(keyword) + 1);
00360                     strcpy(gfi->fontName, keyword);
00361                     break;
00362                 case ENCODINGSCHEME:
00363                     keyword = linetoken(fp);
00364                     gfi->encodingScheme = (char *) 
00365                      malloc(strlen(keyword) + 1);
00366                     strcpy(gfi->encodingScheme, keyword);
00367                     break; 
00368                 case FULLNAME:
00369                     keyword = linetoken(fp);
00370                     gfi->fullName = (char *) malloc(strlen(keyword) + 1);
00371                     strcpy(gfi->fullName, keyword);
00372                     break; 
00373                 case FAMILYNAME:           
00374                    keyword = linetoken(fp);
00375                     gfi->familyName = (char *) malloc(strlen(keyword) + 1);
00376                     strcpy(gfi->familyName, keyword);
00377                     break; 
00378                 case WEIGHT:
00379                     keyword = linetoken(fp);
00380                     gfi->weight = (char *) malloc(strlen(keyword) + 1);
00381                     strcpy(gfi->weight, keyword);
00382                     break;
00383                 case ITALICANGLE:
00384                     keyword = token(fp);
00385                     gfi->italicAngle = atof(keyword);
00386                     if (errno == ERANGE) error = parseError;
00387                     break;
00388                 case ISFIXEDPITCH:
00389                     keyword = token(fp);
00390                     if (MATCH(keyword, False))
00391                         gfi->isFixedPitch = 0;
00392                     else 
00393                         gfi->isFixedPitch = 1;
00394                     break; 
00395                    case UNDERLINEPOSITION:
00396                     keyword = token(fp);
00397                    gfi->underlinePosition = atoi(keyword);
00398                     break; 
00399                 case UNDERLINETHICKNESS:
00400                     keyword = token(fp);
00401                     gfi->underlineThickness = atoi(keyword);
00402                     break;
00403                 case VERSION:
00404                     keyword = linetoken(fp);
00405                     gfi->version = (char *) malloc(strlen(keyword) + 1);
00406                     strcpy(gfi->version, keyword);
00407                     break; 
00408                 case NOTICE:
00409                     keyword = linetoken(fp);
00410                     gfi->notice = (char *) malloc(strlen(keyword) + 1);
00411                     strcpy(gfi->notice, keyword);
00412                     break; 
00413                 case FONTBBOX:
00414                     keyword = token(fp);
00415                     gfi->fontBBox.llx = atoi(keyword);
00416                     keyword = token(fp);
00417                     gfi->fontBBox.lly = atoi(keyword);
00418                     keyword = token(fp);
00419                     gfi->fontBBox.urx = atoi(keyword);
00420                     keyword = token(fp);
00421                     gfi->fontBBox.ury = atoi(keyword);
00422                     break;
00423                 case CAPHEIGHT:
00424                     keyword = token(fp);
00425                     gfi->capHeight = atoi(keyword);
00426                     break;
00427                 case XHEIGHT:
00428                     keyword = token(fp);
00429                     gfi->xHeight = atoi(keyword);
00430                     break;
00431                 case DESCENDER:
00432                     keyword = token(fp);
00433                     gfi->descender = atoi(keyword);
00434                     break;
00435                 case ASCENDER:
00436                     keyword = token(fp);
00437                     gfi->ascender = atoi(keyword);
00438                     break;
00439                 case STARTCHARMETRICS:
00440                     cont = T1LIB_FALSE;
00441                     break;
00442                 case ENDFONTMETRICS:
00443                     cont = T1LIB_FALSE;
00444                     error = normalEOF;
00445                     break;
00446                 case NOPE:
00447                 default:
00448                     error = parseError;
00449                   T1_PrintLog( "parseGlobals()", "Unknown Keyword: %s", T1LOG_WARNING, keyword);
00450                     break;
00451             } /* switch */
00452     } /* while */
00453     
00454     return(error);
00455     
00456 } /* parseGlobals */    
00457 
00458 
00459 
00460 /************************* initializeArray ************************/
00461 
00462 /*  Unmapped character codes are (at Adobe Systems) assigned the
00463  *  width of the space character (if one exists) else they get the
00464  *  value of 250 ems. This function initializes all entries in the
00465  *  char widths array to have this value. Then any mapped character 
00466  *  codes will be replaced with the width of the appropriate character 
00467  *  when parsing the character metric section.
00468  
00469  *  This function parses the Character Metrics Section looking
00470  *  for a space character (by comparing character names). If found,
00471  *  the width of the space character will be used to initialize the
00472  *  values in the array of character widths. 
00473  *
00474  *  Before returning, the position of the read/write pointer of the
00475  *  file is reset to be where it was upon entering this function.
00476  */
00477 
00478 /* We comment out the following function since it is not needed in t1lib
00479    and we don't want compiler warnings */
00480 /*
00481 static int initializeArray(fp, cwi) 
00482   FILE *fp;
00483   register int *cwi;
00484 {  
00485     BOOL cont = T1LIB_TRUE, found = T1LIB_FALSE;
00486     long opos = ftell(fp);
00487     int code = 0, width = 0, i = 0, error = 0;
00488     register char *keyword;
00489   
00490     while (cont)
00491     {
00492         keyword = token(fp);
00493         if (keyword == NULL)
00494         {
00495            error = earlyEOF;
00496             break; 
00497         }
00498         switch(recognize(keyword))
00499         {
00500             case COMMENT:
00501                 keyword = linetoken(fp);
00502                 break;
00503             case CODE:
00504                 code = atoi(token(fp));
00505                 break;
00506             case XWIDTH:
00507                 width = atoi(token(fp));
00508                 break;
00509             case CHARNAME: 
00510                 keyword = token(fp);
00511                 if (MATCH(keyword, Space))
00512                 {    
00513                     cont = T1LIB_FALSE;
00514                     found = T1LIB_TRUE;
00515                 } 
00516                 break;            
00517             case ENDCHARMETRICS:
00518                 cont = T1LIB_FALSE;
00519                 break; 
00520             case ENDFONTMETRICS:
00521                 cont = T1LIB_FALSE;
00522                 error = normalEOF;
00523                 break;
00524             case NOPE:
00525             default: 
00526                 error = parseError;
00527                 break;
00528         } 
00529     } 
00530     
00531     if (!found)
00532         width = 250;
00533     
00534     for (i = 0; i < 256; ++i)
00535         cwi[i] = width;
00536     
00537     fseek(fp, opos, 0);
00538     
00539     return(error);
00540         
00541 }
00542 */ 
00543 
00544 /************************* parseCharWidths **************************/
00545 
00546 /*  This function is called by "parseFile". It will parse the AFM File
00547  *  up to the "EndCharMetrics" keyword. It will save the character 
00548  *  width info (as opposed to all of the character metric information)
00549  *  if requested by the caller of parseFile. Otherwise, it will just
00550  *  parse through the section without saving any information.
00551  *
00552  *  If data is to be saved, parseCharWidths is passed in a pointer 
00553  *  to an array of widths that has already been initialized by the
00554  *  standard value for unmapped character codes. This function parses
00555  *  the Character Metrics section only storing the width information
00556  *  for the encoded characters into the array using the character code
00557  *  as the index into that array.
00558  *
00559  *  This function returns an error code specifying whether there was 
00560  *  a premature EOF or a parsing error. This return value is used by 
00561  *  parseFile to determine if there is more file to parse.
00562  */
00563  
00564 static int parseCharWidths(fp, cwi)
00565   FILE *fp;
00566   register int *cwi;
00567 {  
00568     BOOL cont = T1LIB_TRUE, save = (cwi != NULL);
00569     int pos = 0, error = ok;
00570     register char *keyword;
00571     
00572     while (cont)
00573     {
00574         keyword = token(fp);
00575           /* Have reached an early and unexpected EOF. */
00576           /* Set flag and stop parsing */
00577         if (keyword == NULL)
00578         {
00579             error = earlyEOF;
00580             break; /* get out of loop */
00581         }
00582         if (!save)   
00583           /* get tokens until the end of the Char Metrics section without */
00584           /* saving any of the data*/
00585             switch (recognize(keyword))  
00586             {                      
00587                 case ENDCHARMETRICS:
00588                     cont = T1LIB_FALSE;
00589                     break; 
00590                 case ENDFONTMETRICS:
00591                     cont = T1LIB_FALSE;
00592                     error = normalEOF;
00593                     break;
00594                 default: 
00595                     break;
00596             } /* switch */
00597         else
00598           /* otherwise parse entire char metrics section, saving */
00599           /* only the char x-width info */
00600             switch(recognize(keyword))
00601             {
00602                 case COMMENT:
00603                     keyword = linetoken(fp);
00604                     break;
00605                 case CODE:
00606                     keyword = token(fp);
00607                     pos = atoi(keyword);
00608                     break;
00609                 case XYWIDTH:
00610                 /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
00611                     keyword = token(fp); keyword = token(fp); /* eat values */
00612                     error = parseError;
00613                     break;
00614                 case XWIDTH:
00615                     keyword = token(fp);
00616                     if (pos >= 0) /* ignore unmapped chars */
00617                         cwi[pos] = atoi(keyword);
00618                     break;
00619                 case ENDCHARMETRICS:
00620                     cont = T1LIB_FALSE;
00621                     break; 
00622                 case ENDFONTMETRICS:
00623                     cont = T1LIB_FALSE;
00624                     error = normalEOF;
00625                     break;
00626                 case CHARNAME:     /* eat values (so doesn't cause parseError) */
00627                     keyword = token(fp); 
00628                     break;
00629               case CHARBBOX: 
00630                     keyword = token(fp); keyword = token(fp);
00631                     keyword = token(fp); keyword = token(fp);
00632                   break;
00633               case LIGATURE:
00634                     keyword = token(fp); keyword = token(fp);
00635                   break;
00636                 case NOPE:
00637                 default: 
00638                     error = parseError;
00639                     break;
00640             } /* switch */
00641     } /* while */
00642     
00643     return(error);
00644     
00645 } /* parseCharWidths */    
00646 
00647 
00648 /************************* parseCharMetrics ************************/
00649 
00650 /*  This function is called by parseFile if the caller of parseFile
00651  *  requested that all character metric information be saved
00652  *  (as opposed to only the character width information).
00653  *
00654  *  parseCharMetrics is passed in a pointer to an array of records
00655  *  to hold information on a per character basis. This function
00656  *  parses the Character Metrics section storing all character
00657  *  metric information for the ALL characters (mapped and unmapped) 
00658  *  into the array.
00659  *
00660  *  This function returns an error code specifying whether there was 
00661  *  a premature EOF or a parsing error. This return value is used by 
00662  *  parseFile to determine if there is more file to parse.
00663  */
00664  
00665 static int parseCharMetrics(fp, fi)
00666   FILE *fp;
00667   register FontInfo *fi;
00668 {  
00669     BOOL cont = T1LIB_TRUE, firstTime = T1LIB_TRUE;
00670     int error = ok, count = 0;
00671     register CharMetricInfo *temp = fi->cmi;
00672     register char *keyword;
00673   
00674     while (cont)
00675     {
00676         keyword = token(fp);
00677         if (keyword == NULL)
00678         {
00679             error = earlyEOF;
00680             break; /* get out of loop */
00681         }
00682         switch(recognize(keyword))
00683         {
00684             case COMMENT:
00685                 keyword = linetoken(fp);
00686                 break; 
00687             case CODE:
00688                 if (count < fi->numOfChars)
00689                 { 
00690                     if (firstTime) firstTime = T1LIB_FALSE;
00691                     else temp++;
00692                     temp->code = atoi(token(fp));
00693                     count++;
00694                 }
00695                 else
00696                 {
00697                     error = parseError;
00698                     cont = T1LIB_FALSE;
00699                 }
00700                 break;
00701             case XYWIDTH:
00702                 temp->wx = atoi(token(fp));
00703                 temp->wy = atoi(token(fp));
00704                 break;                 
00705             case XWIDTH: 
00706                 temp->wx = atoi(token(fp));
00707                 break;
00708             case CHARNAME: 
00709                 keyword = token(fp);
00710                 temp->name = (char *) malloc(strlen(keyword) + 1);
00711                 strcpy(temp->name, keyword);
00712                 break;            
00713             case CHARBBOX: 
00714                 temp->charBBox.llx = atoi(token(fp));
00715                 temp->charBBox.lly = atoi(token(fp));
00716                 temp->charBBox.urx = atoi(token(fp));
00717                 temp->charBBox.ury = atoi(token(fp));
00718                 break;
00719             case LIGATURE: {
00720                 Ligature **tail = &(temp->ligs);
00721                 Ligature *node = *tail;
00722                 
00723                 if (*tail != NULL)
00724                 {
00725                     while (node->next != NULL)
00726                         node = node->next;
00727                     tail = &(node->next); 
00728                 }
00729                 
00730                 *tail = (Ligature *) calloc(1, sizeof(Ligature));
00731                 keyword = token(fp);
00732                 (*tail)->succ = (char *) malloc(strlen(keyword) + 1);
00733                 strcpy((*tail)->succ, keyword);
00734                 keyword = token(fp);
00735                 (*tail)->lig = (char *) malloc(strlen(keyword) + 1);
00736                 strcpy((*tail)->lig, keyword);
00737                 break; }
00738             case ENDCHARMETRICS:
00739                 cont = T1LIB_FALSE;;
00740                 break; 
00741             case ENDFONTMETRICS: 
00742                 cont = T1LIB_FALSE;
00743                 error = normalEOF;
00744                 break; 
00745             case NOPE:
00746             default:
00747                 error = parseError; 
00748                 break; 
00749         } /* switch */
00750     } /* while */
00751     
00752     if ((error == ok) && (count != fi->numOfChars))
00753         error = parseError;
00754     
00755     return(error);
00756     
00757 } /* parseCharMetrics */    
00758 
00759 
00760 
00761 /************************* parseTrackKernData ***********************/
00762 
00763 /*  This function is called by "parseFile". It will parse the AFM File 
00764  *  up to the "EndTrackKern" or "EndKernData" keywords. It will save the
00765  *  track kerning data if requested by the caller of parseFile.
00766  *
00767  *  parseTrackKernData is passed in a pointer to the FontInfo record.
00768  *  If data is to be saved, the FontInfo record will already contain 
00769  *  a valid pointer to storage for the track kerning data.
00770  *
00771  *  This function returns an error code specifying whether there was 
00772  *  a premature EOF or a parsing error. This return value is used by 
00773  *  parseFile to determine if there is more file to parse.
00774  */
00775  
00776 static int parseTrackKernData(fp, fi)
00777   FILE *fp;
00778   register FontInfo *fi;
00779 {  
00780     BOOL cont = T1LIB_TRUE, save = (fi->tkd != NULL);
00781     int pos = 0, error = ok, tcount = 0;
00782     register char *keyword;
00783   
00784     while (cont)
00785     {
00786         keyword = token(fp);
00787         
00788         if (keyword == NULL)
00789         {
00790             error = earlyEOF;
00791             break; /* get out of loop */
00792         }
00793         if (!save)
00794           /* get tokens until the end of the Track Kerning Data */
00795           /* section without saving any of the data */
00796             switch(recognize(keyword))
00797             {
00798                 case ENDTRACKKERN:
00799                 case ENDKERNDATA:
00800                     cont = T1LIB_FALSE;
00801                     break;
00802                 case ENDFONTMETRICS:
00803                     cont = T1LIB_FALSE;
00804                     error = normalEOF;
00805                     break;
00806                 default:
00807                     break;
00808             } /* switch */
00809        else
00810           /* otherwise parse entire Track Kerning Data section, */
00811           /* saving the data */
00812             switch(recognize(keyword))
00813             {
00814                 case COMMENT:
00815                     keyword = linetoken(fp);
00816                     break;
00817                 case TRACKKERN:
00818                     if (tcount < fi->numOfTracks)
00819                     {
00820                         keyword = token(fp);
00821                         fi->tkd[pos].degree = atoi(keyword);
00822                         keyword = token(fp);
00823                         fi->tkd[pos].minPtSize = atof(keyword);
00824                         if (errno == ERANGE) error = parseError;
00825                         keyword = token(fp);
00826                         fi->tkd[pos].minKernAmt = atof(keyword);
00827                         if (errno == ERANGE) error = parseError;
00828                         keyword = token(fp);
00829                         fi->tkd[pos].maxPtSize = atof(keyword);
00830                         if (errno == ERANGE) error = parseError;
00831                         keyword = token(fp);
00832                         fi->tkd[pos++].maxKernAmt = atof(keyword);
00833                         if (errno == ERANGE) error = parseError;
00834                         tcount++;
00835                     }
00836                     else
00837                     {
00838                         error = parseError;
00839                         cont = T1LIB_FALSE;
00840                     }
00841                     break;
00842                 case ENDTRACKKERN:
00843                 case ENDKERNDATA:
00844                     cont = T1LIB_FALSE;
00845                     break;
00846                 case ENDFONTMETRICS:
00847                     cont = T1LIB_FALSE;
00848                     error = normalEOF;
00849                     break;
00850                 case NOPE:
00851                 default:
00852                     error = parseError;
00853                     break;
00854             } /* switch */
00855     } /* while */
00856     
00857     if (error == ok && tcount != fi->numOfTracks)
00858         error = parseError;
00859         
00860     return(error);
00861     
00862 } /* parseTrackKernData */    
00863 
00864 
00865 /************************* parsePairKernData ************************/
00866 
00867 /*  This function is called by "parseFile". It will parse the AFM File 
00868  *  up to the "EndKernPairs" or "EndKernData" keywords. It will save
00869  *  the pair kerning data if requested by the caller of parseFile.
00870  *
00871  *  parsePairKernData is passed in a pointer to the FontInfo record.
00872  *  If data is to be saved, the FontInfo record will already contain 
00873  *  a valid pointer to storage for the pair kerning data.
00874  *
00875  *  This function returns an error code specifying whether there was 
00876  *  a premature EOF or a parsing error. This return value is used by 
00877  *  parseFile to determine if there is more file to parse.
00878  */
00879  
00880 static int parsePairKernData(fp, fi)
00881      FILE *fp;
00882      register FontInfo *fi;
00883 {  
00884   BOOL cont = T1LIB_TRUE, save = (fi->pkd != NULL);
00885   int pos = 0, error = ok, pcount = 0;
00886   register char *keyword;
00887   
00888   while (cont)
00889     {
00890       keyword = token(fp);
00891       
00892       if (keyword == NULL)
00893         {
00894          error = earlyEOF;
00895          break; /* get out of loop */
00896         }
00897       if (!save)
00898        /* get tokens until the end of the Pair Kerning Data */
00899        /* section without saving any of the data */
00900        switch(recognize(keyword))
00901          {
00902          case ENDKERNPAIRS:
00903          case ENDKERNDATA:
00904            cont = T1LIB_FALSE;
00905            break;
00906          case ENDFONTMETRICS:
00907            cont = T1LIB_FALSE;
00908            error = normalEOF;
00909            break;
00910          default:
00911            break;
00912          } /* switch */
00913       else
00914        /* otherwise parse entire Pair Kerning Data section, */
00915        /* saving the data */
00916        switch(recognize(keyword))
00917          {
00918          case COMMENT:
00919            keyword = linetoken(fp);
00920            break;
00921          case KERNPAIR:
00922            if (pcount < fi->numOfPairs)
00923              {
00924               keyword = token(fp);
00925               fi->pkd[pos].name1 = (char *) 
00926                 malloc(strlen(keyword) + 1);
00927               strcpy(fi->pkd[pos].name1, keyword);
00928               keyword = token(fp);
00929               fi->pkd[pos].name2 = (char *) 
00930                 malloc(strlen(keyword) + 1);
00931               strcpy(fi->pkd[pos].name2, keyword);
00932               keyword = token(fp);
00933               fi->pkd[pos].xamt = atoi(keyword);
00934               keyword = token(fp);
00935               fi->pkd[pos++].yamt = atoi(keyword);
00936               pcount++;
00937              }
00938            else
00939              {
00940               error = parseError;
00941               cont = T1LIB_FALSE;
00942              }
00943            break;
00944          case KERNPAIRXAMT:
00945            if (pcount < fi->numOfPairs)
00946              {
00947               keyword = token(fp);
00948               fi->pkd[pos].name1 = (char *) 
00949                 malloc(strlen(keyword) + 1);
00950               strcpy(fi->pkd[pos].name1, keyword);
00951               keyword = token(fp);
00952               fi->pkd[pos].name2 = (char *) 
00953                 malloc(strlen(keyword) + 1);
00954               strcpy(fi->pkd[pos].name2, keyword);
00955               keyword = token(fp);
00956               fi->pkd[pos++].xamt = atoi(keyword);
00957               pcount++;
00958              }
00959            else
00960              {
00961               error = parseError;
00962               cont = T1LIB_FALSE;
00963              }
00964            break;
00965          case ENDKERNPAIRS:
00966          case ENDKERNDATA:
00967            cont = T1LIB_FALSE;
00968            break;
00969          case ENDFONTMETRICS:
00970            cont = T1LIB_FALSE;
00971            error = normalEOF;
00972            break;
00973          case NOPE:
00974          default:
00975            error = parseError;
00976            break;
00977          } /* switch */
00978     } /* while */
00979     
00980   if (error == ok && pcount != fi->numOfPairs)
00981     error = parseError;
00982         
00983   return(error);
00984     
00985 } /* parsePairKernData */    
00986 
00987 
00988 /************************* parseCompCharData **************************/
00989 
00990 /*  This function is called by "parseFile". It will parse the AFM File 
00991  *  up to the "EndComposites" keyword. It will save the composite 
00992  *  character data if requested by the caller of parseFile.
00993  *
00994  *  parseCompCharData is passed in a pointer to the FontInfo record, and 
00995  *  a boolean representing if the data should be saved.
00996  *
00997  *  This function will create the appropriate amount of storage for
00998  *  the composite character data and store a pointer to the storage
00999  *  in the FontInfo record.
01000  *
01001  *  This function returns an error code specifying whether there was 
01002  *  a premature EOF or a parsing error. This return value is used by 
01003  *  parseFile to determine if there is more file to parse.
01004  */
01005  
01006 static int parseCompCharData(fp, fi)
01007   FILE *fp;
01008   register FontInfo *fi;
01009 {  
01010     BOOL cont = T1LIB_TRUE, firstTime = T1LIB_TRUE, save = (fi->ccd != NULL);
01011     int pos = 0, j = 0, error = ok, ccount = 0, pcount = 0;
01012     register char *keyword;
01013   
01014     while (cont)
01015     {
01016         keyword = token(fp);
01017         if (keyword == NULL)
01018           /* Have reached an early and unexpected EOF. */
01019           /* Set flag and stop parsing */
01020         {
01021             error = earlyEOF;
01022             break; /* get out of loop */
01023         }
01024         if (ccount > fi->numOfComps)
01025         {
01026             error = parseError;
01027             break; /* get out of loop */
01028         }
01029         if (!save)
01030           /* get tokens until the end of the Composite Character info */
01031           /* section without saving any of the data */
01032             switch(recognize(keyword))
01033             {
01034                 case ENDCOMPOSITES:
01035                     cont = T1LIB_FALSE;
01036                     break;
01037                 case ENDFONTMETRICS:
01038                     cont = T1LIB_FALSE;
01039                     error = normalEOF;
01040                     break;
01041                 default:
01042                     break;
01043             } /* switch */
01044        else
01045           /* otherwise parse entire Composite Character info section, */
01046           /* saving the data */
01047             switch(recognize(keyword))
01048             {
01049                 case COMMENT:
01050                     keyword = linetoken(fp);
01051                     break;
01052                 case COMPCHAR:
01053                     if (ccount < fi->numOfComps)
01054                     {
01055                         keyword = token(fp);
01056                         if (pcount != fi->ccd[pos].numOfPieces)
01057                             error = parseError;
01058                         pcount = 0;
01059                         if (firstTime) firstTime = T1LIB_FALSE;
01060                         else pos++;
01061                         fi->ccd[pos].ccName = (char *) 
01062                             malloc(strlen(keyword) + 1);
01063                         strcpy(fi->ccd[pos].ccName, keyword);
01064                         keyword = token(fp);
01065                         fi->ccd[pos].numOfPieces = atoi(keyword);
01066                         fi->ccd[pos].pieces = (Pcc *)
01067                             calloc(fi->ccd[pos].numOfPieces, sizeof(Pcc));
01068                         j = 0;
01069                         ccount++;
01070                     }
01071                     else
01072                     {
01073                         error = parseError;
01074                         cont = T1LIB_FALSE;
01075                     }
01076                     break;
01077                 case COMPCHARPIECE:
01078                     if (pcount < fi->ccd[pos].numOfPieces)
01079                     {
01080                         keyword = token(fp);
01081                         fi->ccd[pos].pieces[j].pccName = (char *) 
01082                                 malloc(strlen(keyword) + 1);
01083                         strcpy(fi->ccd[pos].pieces[j].pccName, keyword);
01084                         keyword = token(fp);
01085                         fi->ccd[pos].pieces[j].deltax = atoi(keyword);
01086                         keyword = token(fp);
01087                         fi->ccd[pos].pieces[j++].deltay = atoi(keyword);
01088                         pcount++;
01089                     }
01090                     else
01091                         error = parseError;
01092                     break;
01093                 case ENDCOMPOSITES:
01094                     cont = T1LIB_FALSE;
01095                     break;
01096                 case ENDFONTMETRICS:
01097                     cont = T1LIB_FALSE;
01098                     error = normalEOF;
01099                     break;
01100                 case NOPE:
01101                 default:
01102                     error = parseError;
01103                     break;
01104             } /* switch */
01105     } /* while */
01106     
01107     if (error == ok && ccount != fi->numOfComps)
01108         error = parseError;
01109     
01110     return(error);
01111     
01112 } /* parseCompCharData */    
01113 
01114 
01115 
01116 
01117 /*************************** 'PUBLIC' FUNCTION ********************/ 
01118 
01119 
01120 /*************************** parseFile *****************************/
01121 
01122 /*  parseFile is the only 'public' procedure available. It is called 
01123  *  from an application wishing to get information from an AFM file.
01124  *  The caller of this function is responsible for locating and opening
01125  *  an AFM file and handling all errors associated with that task.
01126  *
01127  *  parseFile expects 3 parameters: a vaild file pointer, a pointer
01128  *  to a (FontInfo *) variable (for which storage will be allocated and
01129  *  the data requested filled in), and a mask specifying which
01130  *  data from the AFM File should be saved in the FontInfo structure.
01131  *
01132  *  The file will be parsed and the requested data will be stored in 
01133  *  a record of type FontInfo (refer to ParseAFM.h).
01134  *
01135  *  parseFile returns an error code as defined in parseAFM.h. 
01136  *
01137  *  The position of the read/write pointer associated with the file 
01138  *  pointer upon return of this function is undefined.
01139  */
01140 
01141 /* Note: function renamed to T1lib_parseFile in order to avoid name clushes
01142    with other libraries that also use the Adobe parseAFM-package (RMz) */
01143 int T1lib_parseFile (fp, fi, flags)
01144   FILE *fp;
01145   FontInfo **fi;
01146   FLAGS flags;
01147 {
01148     
01149     int code = ok;   /* return code from each of the parsing routines */
01150     int error = ok;  /* used as the return code from this function */
01151     
01152     register char *keyword; /* used to store a token */  
01153     
01154                            
01155     /* storage data for the global variable ident */                        
01156     ident = (char *) calloc(MAX_NAME, sizeof(char)); 
01157     if (ident == NULL) {error = storageProblem; return(error);}      
01158   
01159     (*fi) = (FontInfo *) calloc(1, sizeof(FontInfo));
01160     if ((*fi) == NULL) {error = storageProblem; return(error);}      
01161   
01162     if (flags & P_G) 
01163     {
01164         (*fi)->gfi = (GlobalFontInfo *) calloc(1, sizeof(GlobalFontInfo));
01165         if ((*fi)->gfi == NULL) {error = storageProblem; return(error);}      
01166     }
01167     
01168     /* The AFM File begins with Global Font Information. This section */
01169     /* will be parsed whether or not information should be saved. */     
01170     code = parseGlobals(fp, (*fi)->gfi); 
01171     
01172     if (code < 0) error = code;
01173     
01174     /* The Global Font Information is followed by the Character Metrics */
01175     /* section. Which procedure is used to parse this section depends on */
01176     /* how much information should be saved. If all of the metrics info */
01177     /* is wanted, parseCharMetrics is called. If only the character widths */
01178     /* is wanted, parseCharWidths is called. parseCharWidths will also */
01179     /* be called in the case that no character data is to be saved, just */
01180     /* to parse through the section. */
01181   
01182     if ((code != normalEOF) && (code != earlyEOF))
01183     {
01184         (*fi)->numOfChars = atoi(token(fp));
01185            if (flags & (P_M ^ P_W))
01186         {
01187             (*fi)->cmi = (CharMetricInfo *) 
01188                       calloc((*fi)->numOfChars, sizeof(CharMetricInfo));
01189            if ((*fi)->cmi == NULL) {error = storageProblem; return(error);}
01190             code = parseCharMetrics(fp, *fi);             
01191         }
01192         else
01193         {
01194             if (flags & P_W)
01195             { 
01196                 (*fi)->cwi = (int *) calloc(256, sizeof(int)); 
01197                 if ((*fi)->cwi == NULL) 
01198                 {
01199                      error = storageProblem; 
01200                      return(error);
01201                 }
01202             }
01203             /* parse section regardless */
01204             code = parseCharWidths(fp, (*fi)->cwi);
01205         } /* else */
01206     } /* if */
01207     
01208     if ((error != earlyEOF) && (code < 0)) error = code;
01209     
01210     /* The remaining sections of the AFM are optional. This code will */
01211     /* look at the next keyword in the file to determine what section */
01212     /* is next, and then allocate the appropriate amount of storage */
01213     /* for the data (if the data is to be saved) and call the */
01214     /* appropriate parsing routine to parse the section. */
01215     
01216     while ((code != normalEOF) && (code != earlyEOF)) {
01217       keyword = token(fp);
01218       if (keyword == NULL)
01219        /* Have reached an early and unexpected EOF. */
01220        /* Set flag and stop parsing */
01221         {
01222          code = earlyEOF;
01223          break; /* get out of loop */
01224         }
01225       switch(recognize(keyword))
01226         {
01227          /* this case has been added for t1lib because otherwise comment line
01228             between (i.e., outside) the main sections would lead to parse
01229             errors. The Adobe spec does not seem to forbid comments at
01230             such locations (2001-05-14, RMz) */
01231        case COMMENT:
01232          keyword = linetoken(fp);
01233          break;
01234        case STARTKERNDATA:
01235          break;
01236        case ENDKERNDATA:
01237          break;
01238        case STARTTRACKKERN:
01239          keyword = token(fp);
01240          if (flags & P_T)
01241            {
01242              (*fi)->numOfTracks = atoi(keyword);
01243              (*fi)->tkd = (TrackKernData *) 
01244               calloc((*fi)->numOfTracks, sizeof(TrackKernData));
01245              if ((*fi)->tkd == NULL) 
01246               {
01247                 error = storageProblem; 
01248                 return(error);
01249               }
01250            } /* if */
01251          code = parseTrackKernData(fp, *fi);
01252          break;
01253        case STARTKERNPAIRS:
01254          keyword = token(fp);
01255          if (flags & P_P)
01256            {
01257              (*fi)->numOfPairs = atoi(keyword);
01258              (*fi)->pkd = (PairKernData *) 
01259               calloc((*fi)->numOfPairs, sizeof(PairKernData));
01260              if ((*fi)->pkd == NULL) 
01261               {
01262                 error = storageProblem; 
01263                 return(error);
01264               }
01265            } /* if */
01266          code = parsePairKernData(fp, *fi);
01267          break;
01268        case STARTCOMPOSITES:
01269          keyword = token(fp);
01270          if (flags & P_C)
01271            { 
01272              (*fi)->numOfComps = atoi(keyword);
01273              (*fi)->ccd = (CompCharData *) 
01274               calloc((*fi)->numOfComps, sizeof(CompCharData));
01275              if ((*fi)->ccd == NULL) 
01276               {
01277                 error = storageProblem; 
01278                 return(error);
01279               }
01280            } /* if */
01281          code = parseCompCharData(fp, *fi); 
01282          break;    
01283        case ENDFONTMETRICS:
01284          code = normalEOF;
01285          break;
01286        case NOPE:
01287        default:
01288          code = parseError;
01289          break;
01290         } /* switch */
01291       
01292       if ((error != earlyEOF) && (code < 0)) error = code;
01293       
01294     } /* while */
01295     
01296     if ((error != earlyEOF) && (code < 0)) error = code;
01297     
01298     if (ident != NULL) { free(ident); ident = NULL; }
01299         
01300     return(error);
01301   
01302 } /* parseFile */