home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD2.iso / Misc / FTREE0.3.LHA / ftree / src / RCS / parseAFM.c,v < prev    next >
Encoding:
Text File  |  1994-04-27  |  45.0 KB  |  1,335 lines

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