| /* |
| * (C) 1988, 1989, 1990 by Adobe Systems Incorporated. All rights reserved. |
| * |
| * This file may be freely copied and redistributed as long as: |
| * 1) This entire notice continues to be included in the file, |
| * 2) If the file has been modified in any way, a notice of such |
| * modification is conspicuously indicated. |
| * |
| * PostScript, Display PostScript, and Adobe are registered trademarks of |
| * Adobe Systems Incorporated. |
| * |
| * ************************************************************************ |
| * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT |
| * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS |
| * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR |
| * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY |
| * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, |
| * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. |
| * ************************************************************************ |
| */ |
| |
| /* |
| * Changes made for OpenOffice.org |
| * |
| * 10/24/2000 pl - changed code to compile with c++-compilers |
| * - added namespace to avoid symbol clashes |
| * - replaced BOOL by bool |
| * - added function to free space allocated by parseFile |
| * 10/26/2000 pl - added additional keys |
| * - added ability to parse slightly broken files |
| * - added charwidth member to GlobalFontInfo |
| * 04/26/2001 pl - added OpenOffice header |
| * 10/19/2005 pl - performance increase: |
| * - fread file in one pass |
| * - replace file io by buffer access |
| * 10/20/2005 pl - performance increase: |
| * - use one table lookup in token() routine |
| * instead of many conditions |
| * - return token length in toke() routine |
| * - use hash lookup instead of binary search |
| * in recognize() routine |
| */ |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_vcl.hxx" |
| |
| /* parseAFM.c |
| * |
| * This file is used in conjuction with the parseAFM.h header file. |
| * This file contains several procedures that are used to parse AFM |
| * files. It is intended to work with an application program that needs |
| * font metric information. The program can be used as is by making a |
| * procedure call to "parseFile" (passing in the expected parameters) |
| * and having it fill in a data structure with the data from the |
| * AFM file, or an application developer may wish to customize this |
| * code. |
| * |
| * There is also a file, parseAFMclient.c, that is a sample application |
| * showing how to call the "parseFile" procedure and how to use the data |
| * after "parseFile" has returned. |
| * |
| * Please read the comments in parseAFM.h and parseAFMclient.c. |
| * |
| * History: |
| * original: DSM Thu Oct 20 17:39:59 PDT 1988 |
| * modified: DSM Mon Jul 3 14:17:50 PDT 1989 |
| * - added 'storageProblem' return code |
| * - fixed bug of not allocating extra byte for string duplication |
| * - fixed typos |
| * modified: DSM Tue Apr 3 11:18:34 PDT 1990 |
| * - added free(ident) at end of parseFile routine |
| * modified: DSM Tue Jun 19 10:16:29 PDT 1990 |
| * - changed (width == 250) to (width = 250) in initializeArray |
| */ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <sys/file.h> |
| #include <sys/stat.h> |
| #include <math.h> |
| |
| #include "parseAFM.hxx" |
| #include "vcl/strhelper.hxx" |
| |
| #include "rtl/alloc.h" |
| |
| #define lineterm EOL /* line terminating character */ |
| #define normalEOF 1 /* return code from parsing routines used only */ |
| /* in this module */ |
| #define Space "space" /* used in string comparison to look for the width */ |
| /* of the space character to init the widths array */ |
| #define False "false" /* used in string comparison to check the value of */ |
| /* boolean keys (e.g. IsFixedPitch) */ |
| |
| #define MATCH(A,B) (strncmp((A),(B), MAX_NAME) == 0) |
| |
| namespace psp { |
| |
| class FileInputStream |
| { |
| char* m_pMemory; |
| unsigned int m_nPos; |
| unsigned int m_nLen; |
| public: |
| FileInputStream( const char* pFilename ); |
| ~FileInputStream(); |
| |
| int getChar() { return (m_nPos < m_nLen) ? int(m_pMemory[m_nPos++]) : -1; } |
| void ungetChar() |
| { |
| if( m_nPos > 0 ) |
| m_nPos--; |
| } |
| unsigned int tell() const { return m_nPos; } |
| void seek( unsigned int nPos ) |
| // NOTE: do not check input data since only results of tell() |
| // get seek()ed in this file |
| { m_nPos = nPos; } |
| }; |
| |
| FileInputStream::FileInputStream( const char* pFilename ) : |
| m_pMemory( NULL ), |
| m_nPos( 0 ), |
| m_nLen( 0 ) |
| { |
| struct stat aStat; |
| if( ! stat( pFilename, &aStat ) && |
| S_ISREG( aStat.st_mode ) && |
| aStat.st_size > 0 |
| ) |
| { |
| FILE* fp = fopen( pFilename, "r" ); |
| if( fp ) |
| { |
| m_pMemory = (char*)rtl_allocateMemory( aStat.st_size ); |
| m_nLen = (unsigned int)fread( m_pMemory, 1, aStat.st_size, fp ); |
| fclose( fp ); |
| } |
| } |
| } |
| |
| FileInputStream::~FileInputStream() |
| { |
| rtl_freeMemory( m_pMemory ); |
| } |
| |
| /*************************** GLOBALS ***********************/ |
| /* "shorts" for fast case statement |
| * The values of each of these enumerated items correspond to an entry in the |
| * table of strings defined below. Therefore, if you add a new string as |
| * new keyword into the keyStrings table, you must also add a corresponding |
| * parseKey AND it MUST be in the same position! |
| * |
| * IMPORTANT: since the sorting algorithm is a binary search, the strings of |
| * keywords must be placed in lexicographical order, below. [Therefore, the |
| * enumerated items are not necessarily in lexicographical order, depending |
| * on the name chosen. BUT, they must be placed in the same position as the |
| * corresponding key string.] The NOPE shall remain in the last position, |
| * since it does not correspond to any key string, and it is used in the |
| * "recognize" procedure to calculate how many possible keys there are. |
| */ |
| |
| // some metrics have Ascent, Descent instead Ascender, Descender or Em |
| // which is not allowed per afm spcification, but let us handle |
| // this gently |
| enum parseKey { |
| ASCENDER, ASCENT, CHARBBOX, CODE, COMPCHAR, CODEHEX, CAPHEIGHT, CHARWIDTH, CHARACTERSET, CHARACTERS, COMMENT, |
| DESCENDER, DESCENT, EM, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES, ENDDIRECTION, |
| ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN, |
| FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME, ISBASEFONT, ISFIXEDPITCH, |
| ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, MAPPINGSCHEME, METRICSSETS, CHARNAME, |
| NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES, STARTDIRECTION, |
| STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS, |
| STARTTRACKKERN, STDHW, STDVW, TRACKKERN, UNDERLINEPOSITION, |
| UNDERLINETHICKNESS, VVECTOR, VERSION, XYWIDTH, X0WIDTH, XWIDTH, WEIGHT, XHEIGHT, |
| NOPE |
| }; |
| |
| /*************************** PARSING ROUTINES **************/ |
| |
| /*************************** token *************************/ |
| |
| /* A "AFM file Conventions" tokenizer. That means that it will |
| * return the next token delimited by white space. See also |
| * the `linetoken' routine, which does a similar thing but |
| * reads all tokens until the next end-of-line. |
| */ |
| |
| // token white space is ' ', '\n', '\r', ',', '\t', ';' |
| static const bool is_white_Array[ 256 ] = |
| { false, false, false, false, false, false, false, false, // 0-7 |
| false, true, true, false, false, true, false, false, // 8-15 |
| false, false, false, false, false, false, false, false, // 16-23 |
| false, false, false, false, false, false, false, false, // 24-31 |
| true, false, false, false, false, false, false, false, // 32-39 |
| false, false, false, false, true, false, false, false, // 40-47 |
| false, false, false, false, false, false, false, false, // 48-55 |
| false, false, false, true, false, false, false, false, // 56-63 |
| |
| false, false, false, false, false, false, false, false, // 64 - |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, // 127 |
| |
| false, false, false, false, false, false, false, false, // 128 - |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, // 191 |
| |
| false, false, false, false, false, false, false, false, // 192 - |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, // 255 |
| }; |
| // token delimiters are ' ', '\n', '\r', '\t', ':', ';' |
| static const bool is_delimiter_Array[ 256 ] = |
| { false, false, false, false, false, false, false, false, // 0-7 |
| false, true, true, false, false, true, false, false, // 8-15 |
| false, false, false, false, false, false, false, false, // 16-23 |
| false, false, false, false, false, false, false, false, // 24-31 |
| true, false, false, false, false, false, false, false, // 32-39 |
| false, false, false, false, false, false, false, false, // 40-47 |
| false, false, false, false, false, false, false, false, // 48-55 |
| false, false, true, true, false, false, false, false, // 56-63 |
| |
| false, false, false, false, false, false, false, false, // 64 - |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, // 127 |
| |
| false, false, false, false, false, false, false, false, // 128 - |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, // 191 |
| |
| false, false, false, false, false, false, false, false, // 192 - |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, |
| false, false, false, false, false, false, false, false, // 255 |
| }; |
| static char *token( FileInputStream* stream, int& rLen ) |
| { |
| static char ident[MAX_NAME]; /* storage buffer for keywords */ |
| |
| int ch, idx; |
| |
| /* skip over white space */ |
| // relies on EOF = -1 |
| while( is_white_Array[ (ch = stream->getChar()) & 255 ] ) |
| ; |
| |
| idx = 0; |
| while( ch != -1 && ! is_delimiter_Array[ ch & 255 ] && idx < MAX_NAME-1 ) |
| { |
| ident[idx++] = ch; |
| ch = stream->getChar(); |
| } |
| |
| if (ch == -1 && idx < 1) return ((char *)NULL); |
| if (idx >= 1 && ch != ':' && ch != -1) stream->ungetChar(); |
| if (idx < 1 ) ident[idx++] = ch; /* single-character token */ |
| ident[idx] = 0; |
| rLen = idx; |
| |
| return(ident); /* returns pointer to the token */ |
| |
| } /* token */ |
| |
| |
| /*************************** linetoken *************************/ |
| |
| /* "linetoken" will get read all tokens until the EOL character from |
| * the given stream. This is used to get any arguments that can be |
| * more than one word (like Comment lines and FullName). |
| */ |
| |
| static char *linetoken( FileInputStream* stream ) |
| { |
| static char ident[MAX_NAME]; /* storage buffer for keywords */ |
| int ch, idx; |
| |
| while ((ch = stream->getChar()) == ' ' || ch == '\t' ) ; |
| |
| idx = 0; |
| while (ch != -1 && ch != lineterm && ch != '\r' && idx < MAX_NAME-1 ) |
| { |
| ident[idx++] = ch; |
| ch = stream->getChar(); |
| } /* while */ |
| |
| stream->ungetChar(); |
| ident[idx] = 0; |
| |
| return(ident); /* returns pointer to the token */ |
| |
| } /* linetoken */ |
| |
| |
| /*************************** recognize *************************/ |
| |
| /* This function tries to match a string to a known list of |
| * valid AFM entries (check the keyStrings array above). |
| * "ident" contains everything from white space through the |
| * next space, tab, or ":" character. |
| * |
| * The algorithm is a standard Knuth binary search. |
| */ |
| #include "afm_hash.cpp" |
| |
| static inline enum parseKey recognize( register char* ident, int len) |
| { |
| const hash_entry* pEntry = AfmKeywordHash::in_word_set( ident, len ); |
| return pEntry ? pEntry->eKey : NOPE; |
| |
| } /* recognize */ |
| |
| |
| /************************* parseGlobals *****************************/ |
| |
| /* This function is called by "parseFile". It will parse the AFM file |
| * up to the "StartCharMetrics" keyword, which essentially marks the |
| * end of the Global Font Information and the beginning of the character |
| * metrics information. |
| * |
| * If the caller of "parseFile" specified that it wanted the Global |
| * Font Information (as defined by the "AFM file Specification" |
| * document), then that information will be stored in the returned |
| * data structure. |
| * |
| * Any Global Font Information entries that are not found in a |
| * given file, will have the usual default initialization value |
| * for its type (i.e. entries of type int will be 0, etc). |
| * |
| * This function returns an error code specifying whether there was |
| * a premature EOF or a parsing error. This return value is used by |
| * parseFile to determine if there is more file to parse. |
| */ |
| |
| static int parseGlobals( FileInputStream* fp, register GlobalFontInfo* gfi ) |
| { |
| bool cont = true, save = (gfi != NULL); |
| int error = ok; |
| register char *keyword; |
| int direction = -1; |
| int tokenlen; |
| |
| while (cont) |
| { |
| keyword = token(fp, tokenlen); |
| |
| if (keyword == NULL) |
| /* Have reached an early and unexpected EOF. */ |
| /* Set flag and stop parsing */ |
| { |
| error = earlyEOF; |
| break; /* get out of loop */ |
| } |
| if (!save) |
| /* get tokens until the end of the Global Font info section */ |
| /* without saving any of the data */ |
| switch (recognize(keyword, tokenlen)) |
| { |
| case STARTCHARMETRICS: |
| cont = false; |
| break; |
| case ENDFONTMETRICS: |
| cont = false; |
| error = normalEOF; |
| break; |
| default: |
| break; |
| } /* switch */ |
| else |
| /* otherwise parse entire global font info section, */ |
| /* saving the data */ |
| switch(recognize(keyword, tokenlen)) |
| { |
| case STARTFONTMETRICS: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->afmVersion = strdup( keyword ); |
| break; |
| case COMMENT: |
| keyword = linetoken(fp); |
| break; |
| case FONTNAME: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->fontName = strdup( keyword ); |
| break; |
| case ENCODINGSCHEME: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->encodingScheme = strdup( keyword ); |
| break; |
| case FULLNAME: |
| if ((keyword = linetoken(fp)) != NULL) |
| gfi->fullName = strdup( keyword ); |
| break; |
| case FAMILYNAME: |
| if ((keyword = linetoken(fp)) != NULL) |
| gfi->familyName = strdup( keyword ); |
| break; |
| case WEIGHT: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->weight = strdup( keyword ); |
| break; |
| case ITALICANGLE: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->italicAngle = StringToDouble( keyword ); |
| break; |
| case ISFIXEDPITCH: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| { |
| if (MATCH(keyword, False)) |
| gfi->isFixedPitch = 0; |
| else |
| gfi->isFixedPitch = 1; |
| } |
| break; |
| case UNDERLINEPOSITION: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->underlinePosition = atoi(keyword); |
| break; |
| case UNDERLINETHICKNESS: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->underlineThickness = atoi(keyword); |
| break; |
| case VERSION: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->version = strdup( keyword ); |
| break; |
| case NOTICE: |
| if ((keyword = linetoken(fp)) != NULL) |
| gfi->notice = strdup( keyword ); |
| break; |
| case FONTBBOX: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->fontBBox.llx = atoi(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->fontBBox.lly = atoi(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->fontBBox.urx = atoi(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->fontBBox.ury = atoi(keyword); |
| break; |
| case CAPHEIGHT: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->capHeight = atoi(keyword); |
| break; |
| case XHEIGHT: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->xHeight = atoi(keyword); |
| break; |
| case DESCENT: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->descender = -atoi(keyword); |
| break; |
| case DESCENDER: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->descender = atoi(keyword); |
| break; |
| case ASCENT: |
| case ASCENDER: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| gfi->ascender = atoi(keyword); |
| break; |
| case STARTCHARMETRICS: |
| cont = false; |
| break; |
| case ENDFONTMETRICS: |
| cont = false; |
| error = normalEOF; |
| break; |
| case EM: |
| // skip one token |
| keyword = token(fp,tokenlen); |
| break; |
| case STARTDIRECTION: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| direction = atoi(keyword); |
| break; /* ignore this for now */ |
| case ENDDIRECTION: |
| break; /* ignore this for now */ |
| case MAPPINGSCHEME: |
| keyword = token(fp,tokenlen); |
| break; /* ignore this for now */ |
| case CHARACTERS: |
| keyword = token(fp,tokenlen); |
| break; /* ignore this for now */ |
| case ISBASEFONT: |
| keyword = token(fp,tokenlen); |
| break; /* ignore this for now */ |
| case CHARACTERSET: |
| keyword=token(fp,tokenlen); //ignore |
| break; |
| case STDHW: |
| keyword=token(fp,tokenlen); //ignore |
| break; |
| case STDVW: |
| keyword=token(fp,tokenlen); //ignore |
| break; |
| case CHARWIDTH: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| { |
| if (direction == 0) |
| gfi->charwidth = atoi(keyword); |
| } |
| keyword = token(fp,tokenlen); |
| /* ignore y-width for now */ |
| break; |
| case METRICSSETS: |
| keyword = token(fp,tokenlen); |
| break; /* ignore this for now */ |
| case NOPE: |
| default: |
| error = parseError; |
| break; |
| } /* switch */ |
| } /* while */ |
| |
| return(error); |
| |
| } /* parseGlobals */ |
| |
| |
| #if 0 |
| /************************* initializeArray ************************/ |
| |
| /* Unmapped character codes are (at Adobe Systems) assigned the |
| * width of the space character (if one exists) else they get the |
| * value of 250 ems. This function initializes all entries in the |
| * char widths array to have this value. Then any mapped character |
| * codes will be replaced with the width of the appropriate character |
| * when parsing the character metric section. |
| |
| * This function parses the Character Metrics Section looking |
| * for a space character (by comparing character names). If found, |
| * the width of the space character will be used to initialize the |
| * values in the array of character widths. |
| * |
| * Before returning, the position of the read/write pointer of the |
| * FileInputStream is reset to be where it was upon entering this function. |
| */ |
| |
| static int initializeArray( FileInputStream* fp, register int* cwi) |
| { |
| bool cont = true, found = false; |
| unsigned int opos = fp->tell(); |
| int code = 0, width = 0, i = 0, error = 0, tokenlen; |
| register char *keyword; |
| |
| while (cont) |
| { |
| keyword = token(fp,tokenlen); |
| if (keyword == NULL) |
| { |
| error = earlyEOF; |
| break; /* get out of loop */ |
| } |
| switch(recognize(keyword,tokenlen)) |
| { |
| case COMMENT: |
| keyword = linetoken(fp); |
| break; |
| case CODE: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| code = atoi(keyword); |
| break; |
| case CODEHEX: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| sscanf(keyword,"<%x>", &code); |
| break; |
| case XWIDTH: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| width = atoi(keyword); |
| break; |
| case X0WIDTH: |
| (void) token(fp,tokenlen); |
| break; |
| case CHARNAME: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| if (MATCH(keyword, Space)) |
| { |
| cont = false; |
| found = true; |
| } |
| break; |
| case ENDCHARMETRICS: |
| cont = false; |
| break; |
| case ENDFONTMETRICS: |
| cont = false; |
| error = normalEOF; |
| break; |
| case NOPE: |
| default: |
| error = parseError; |
| break; |
| } /* switch */ |
| } /* while */ |
| |
| if (!found) |
| width = 250; |
| |
| for (i = 0; i < 256; ++i) |
| cwi[i] = width; |
| |
| fp->seek(opos); |
| |
| return(error); |
| |
| } /* initializeArray */ |
| #endif |
| |
| /************************* parseCharWidths **************************/ |
| |
| /* This function is called by "parseFile". It will parse the AFM file |
| * up to the "EndCharMetrics" keyword. It will save the character |
| * width info (as opposed to all of the character metric information) |
| * if requested by the caller of parseFile. Otherwise, it will just |
| * parse through the section without saving any information. |
| * |
| * If data is to be saved, parseCharWidths is passed in a pointer |
| * to an array of widths that has already been initialized by the |
| * standard value for unmapped character codes. This function parses |
| * the Character Metrics section only storing the width information |
| * for the encoded characters into the array using the character code |
| * as the index into that array. |
| * |
| * This function returns an error code specifying whether there was |
| * a premature EOF or a parsing error. This return value is used by |
| * parseFile to determine if there is more file to parse. |
| */ |
| |
| static int parseCharWidths( FileInputStream* fp, register int* cwi) |
| { |
| bool cont = true, save = (cwi != NULL); |
| int pos = 0, error = ok, tokenlen; |
| register char *keyword; |
| |
| while (cont) |
| { |
| keyword = token(fp,tokenlen); |
| /* Have reached an early and unexpected EOF. */ |
| /* Set flag and stop parsing */ |
| if (keyword == NULL) |
| { |
| error = earlyEOF; |
| break; /* get out of loop */ |
| } |
| if (!save) |
| /* get tokens until the end of the Char Metrics section without */ |
| /* saving any of the data*/ |
| switch (recognize(keyword,tokenlen)) |
| { |
| case ENDCHARMETRICS: |
| cont = false; |
| break; |
| case ENDFONTMETRICS: |
| cont = false; |
| error = normalEOF; |
| break; |
| default: |
| break; |
| } /* switch */ |
| else |
| /* otherwise parse entire char metrics section, saving */ |
| /* only the char x-width info */ |
| switch(recognize(keyword,tokenlen)) |
| { |
| case COMMENT: |
| keyword = linetoken(fp); |
| break; |
| case CODE: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| pos = atoi(keyword); |
| break; |
| case XYWIDTH: |
| /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */ |
| keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); /* eat values */ |
| error = parseError; |
| break; |
| case CODEHEX: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| sscanf(keyword, "<%x>", &pos); |
| break; |
| case X0WIDTH: |
| (void) token(fp,tokenlen); |
| break; |
| case XWIDTH: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| if (pos >= 0) /* ignore unmapped chars */ |
| cwi[pos] = atoi(keyword); |
| break; |
| case ENDCHARMETRICS: |
| cont = false; |
| break; |
| case ENDFONTMETRICS: |
| cont = false; |
| error = normalEOF; |
| break; |
| case CHARNAME: /* eat values (so doesn't cause parseError) */ |
| keyword = token(fp,tokenlen); |
| break; |
| case CHARBBOX: |
| keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); |
| keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); |
| break; |
| case LIGATURE: |
| keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); |
| break; |
| case VVECTOR: |
| keyword = token(fp,tokenlen); |
| keyword = token(fp,tokenlen); |
| break; |
| case NOPE: |
| default: |
| error = parseError; |
| break; |
| } /* switch */ |
| } /* while */ |
| |
| return(error); |
| |
| } /* parseCharWidths */ |
| |
| |
| /* |
| * number of char metrics is almost allways inaccurate, so be gentle and try to |
| * adapt our internal storage by adjusting the allocated list |
| */ |
| |
| static int |
| reallocFontMetrics( void **pp_fontmetrics, int *p_oldcount, int n_newcount, unsigned int n_size ) |
| { |
| char *p_tmpmetrics = NULL; |
| |
| if ((pp_fontmetrics == NULL) || (*pp_fontmetrics == NULL)) |
| return storageProblem; |
| |
| if (*p_oldcount == n_newcount) |
| return ok; |
| |
| p_tmpmetrics = (char*)realloc(*pp_fontmetrics, n_newcount * n_size); |
| if (p_tmpmetrics == NULL) |
| return storageProblem; |
| |
| if ( n_newcount > *p_oldcount ) |
| { |
| char *p_inimetrics = p_tmpmetrics + n_size * *p_oldcount; |
| int n_inimetrics = n_size * (n_newcount - *p_oldcount); |
| memset( p_inimetrics, 0, n_inimetrics ); |
| } |
| |
| *pp_fontmetrics = p_tmpmetrics; |
| *p_oldcount = n_newcount; |
| |
| return ok; |
| } |
| |
| static unsigned int |
| enlargeCount( unsigned int n_oldcount ) |
| { |
| unsigned int n_newcount = n_oldcount + n_oldcount / 5; |
| if (n_oldcount == n_newcount ) |
| n_newcount = n_oldcount + 5; |
| |
| return n_newcount; |
| } |
| |
| /************************* parseCharMetrics ************************/ |
| |
| /* This function is called by parseFile if the caller of parseFile |
| * requested that all character metric information be saved |
| * (as opposed to only the character width information). |
| * |
| * parseCharMetrics is passed in a pointer to an array of records |
| * to hold information on a per character basis. This function |
| * parses the Character Metrics section storing all character |
| * metric information for the ALL characters (mapped and unmapped) |
| * into the array. |
| * |
| * This function returns an error code specifying whether there was |
| * a premature EOF or a parsing error. This return value is used by |
| * parseFile to determine if there is more file to parse. |
| */ |
| |
| static int parseCharMetrics( FileInputStream* fp, register FontInfo* fi) |
| { |
| bool cont = true, firstTime = true; |
| int error = ok, count = 0, tokenlen; |
| register CharMetricInfo *temp = fi->cmi; |
| register char *keyword; |
| |
| while (cont) |
| { |
| keyword = token(fp,tokenlen); |
| if (keyword == NULL) |
| { |
| error = earlyEOF; |
| break; /* get out of loop */ |
| } |
| switch(recognize(keyword,tokenlen)) |
| { |
| case COMMENT: |
| keyword = linetoken(fp); |
| break; |
| case CODE: |
| if (!(count < fi->numOfChars)) |
| { |
| reallocFontMetrics( (void**)&(fi->cmi), |
| &(fi->numOfChars), enlargeCount(fi->numOfChars), |
| sizeof(CharMetricInfo) ); |
| temp = &(fi->cmi[ count - 1 ]); |
| } |
| if (count < fi->numOfChars) |
| { |
| if (firstTime) firstTime = false; |
| else temp++; |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| temp->code = atoi(keyword); |
| if (fi->gfi && fi->gfi->charwidth) |
| temp->wx = fi->gfi->charwidth; |
| count++; |
| } |
| else |
| { |
| error = parseError; |
| cont = false; |
| } |
| break; |
| case CODEHEX: |
| if (!(count < fi->numOfChars )) |
| { |
| reallocFontMetrics( (void**)&(fi->cmi), |
| &(fi->numOfChars), enlargeCount(fi->numOfChars), |
| sizeof(CharMetricInfo) ); |
| temp = &(fi->cmi[ count - 1 ]); |
| } |
| if (count < fi->numOfChars) { |
| if (firstTime) |
| firstTime = false; |
| else |
| temp++; |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| sscanf(keyword,"<%x>", &temp->code); |
| if (fi->gfi && fi->gfi->charwidth) |
| temp->wx = fi->gfi->charwidth; |
| count++; |
| } |
| else { |
| error = parseError; |
| cont = false; |
| } |
| break; |
| case XYWIDTH: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| temp->wx = atoi(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| temp->wy = atoi(keyword); |
| break; |
| case X0WIDTH: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| temp->wx = atoi(keyword); |
| break; |
| case XWIDTH: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| temp->wx = atoi(keyword); |
| break; |
| case CHARNAME: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| temp->name = (char *)strdup(keyword); |
| break; |
| case CHARBBOX: |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| temp->charBBox.llx = atoi(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| temp->charBBox.lly = atoi(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| temp->charBBox.urx = atoi(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| temp->charBBox.ury = atoi(keyword); |
| break; |
| case LIGATURE: { |
| Ligature **tail = &(temp->ligs); |
| Ligature *node = *tail; |
| |
| if (*tail != NULL) |
| { |
| while (node->next != NULL) |
| node = node->next; |
| tail = &(node->next); |
| } |
| |
| *tail = (Ligature *) calloc(1, sizeof(Ligature)); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| (*tail)->succ = (char *)strdup(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| (*tail)->lig = (char *)strdup(keyword); |
| break; } |
| case ENDCHARMETRICS: |
| cont = false;; |
| break; |
| case ENDFONTMETRICS: |
| cont = false; |
| error = normalEOF; |
| break; |
| case VVECTOR: |
| keyword = token(fp,tokenlen); |
| keyword = token(fp,tokenlen); |
| break; |
| case NOPE: |
| default: |
| error = parseError; |
| break; |
| } /* switch */ |
| } /* while */ |
| |
| if ((error == ok) && (count != fi->numOfChars)) |
| error = reallocFontMetrics( (void**)&(fi->cmi), &(fi->numOfChars), |
| count, sizeof(CharMetricInfo) ); |
| |
| if ((error == ok) && (count != fi->numOfChars)) |
| error = parseError; |
| |
| return(error); |
| |
| } /* parseCharMetrics */ |
| |
| |
| |
| /************************* parseTrackKernData ***********************/ |
| |
| /* This function is called by "parseFile". It will parse the AFM file |
| * up to the "EndTrackKern" or "EndKernData" keywords. It will save the |
| * track kerning data if requested by the caller of parseFile. |
| * |
| * parseTrackKernData is passed in a pointer to the FontInfo record. |
| * If data is to be saved, the FontInfo record will already contain |
| * a valid pointer to storage for the track kerning data. |
| * |
| * This function returns an error code specifying whether there was |
| * a premature EOF or a parsing error. This return value is used by |
| * parseFile to determine if there is more file to parse. |
| */ |
| |
| static int parseTrackKernData( FileInputStream* fp, register FontInfo* fi) |
| { |
| bool cont = true, save = (fi->tkd != NULL); |
| int pos = 0, error = ok, tcount = 0, tokenlen; |
| register char *keyword; |
| |
| while (cont) |
| { |
| keyword = token(fp,tokenlen); |
| |
| if (keyword == NULL) |
| { |
| error = earlyEOF; |
| break; /* get out of loop */ |
| } |
| if (!save) |
| /* get tokens until the end of the Track Kerning Data */ |
| /* section without saving any of the data */ |
| switch(recognize(keyword,tokenlen)) |
| { |
| case ENDTRACKKERN: |
| case ENDKERNDATA: |
| cont = false; |
| break; |
| case ENDFONTMETRICS: |
| cont = false; |
| error = normalEOF; |
| break; |
| default: |
| break; |
| } /* switch */ |
| else |
| /* otherwise parse entire Track Kerning Data section, */ |
| /* saving the data */ |
| switch(recognize(keyword,tokenlen)) |
| { |
| case COMMENT: |
| keyword = linetoken(fp); |
| break; |
| case TRACKKERN: |
| if (!(tcount < fi->numOfTracks)) |
| { |
| reallocFontMetrics( (void**)&(fi->tkd), &(fi->numOfTracks), |
| enlargeCount(fi->numOfTracks), sizeof(TrackKernData) ); |
| } |
| |
| if (tcount < fi->numOfTracks) |
| { |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->tkd[pos].degree = atoi(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->tkd[pos].minPtSize = StringToDouble(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->tkd[pos].minKernAmt = StringToDouble(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->tkd[pos].maxPtSize = StringToDouble(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->tkd[pos++].maxKernAmt = StringToDouble(keyword); |
| tcount++; |
| } |
| else |
| { |
| error = parseError; |
| cont = false; |
| } |
| break; |
| case ENDTRACKKERN: |
| case ENDKERNDATA: |
| cont = false; |
| break; |
| case ENDFONTMETRICS: |
| cont = false; |
| error = normalEOF; |
| break; |
| case NOPE: |
| default: |
| error = parseError; |
| break; |
| } /* switch */ |
| } /* while */ |
| |
| if (error == ok && tcount != fi->numOfTracks) |
| error = reallocFontMetrics( (void**)&(fi->tkd), &(fi->numOfTracks), |
| tcount, sizeof(TrackKernData) ); |
| |
| if (error == ok && tcount != fi->numOfTracks) |
| error = parseError; |
| |
| return(error); |
| |
| } /* parseTrackKernData */ |
| |
| |
| /************************* parsePairKernData ************************/ |
| |
| /* This function is called by "parseFile". It will parse the AFM file |
| * up to the "EndKernPairs" or "EndKernData" keywords. It will save |
| * the pair kerning data if requested by the caller of parseFile. |
| * |
| * parsePairKernData is passed in a pointer to the FontInfo record. |
| * If data is to be saved, the FontInfo record will already contain |
| * a valid pointer to storage for the pair kerning data. |
| * |
| * This function returns an error code specifying whether there was |
| * a premature EOF or a parsing error. This return value is used by |
| * parseFile to determine if there is more file to parse. |
| */ |
| |
| static int parsePairKernData( FileInputStream* fp, register FontInfo* fi) |
| { |
| bool cont = true, save = (fi->pkd != NULL); |
| int pos = 0, error = ok, pcount = 0, tokenlen; |
| register char *keyword; |
| |
| while (cont) |
| { |
| keyword = token(fp,tokenlen); |
| |
| if (keyword == NULL) |
| { |
| error = earlyEOF; |
| break; /* get out of loop */ |
| } |
| if (!save) |
| /* get tokens until the end of the Pair Kerning Data */ |
| /* section without saving any of the data */ |
| switch(recognize(keyword,tokenlen)) |
| { |
| case ENDKERNPAIRS: |
| case ENDKERNDATA: |
| cont = false; |
| break; |
| case ENDFONTMETRICS: |
| cont = false; |
| error = normalEOF; |
| break; |
| default: |
| break; |
| } /* switch */ |
| else |
| /* otherwise parse entire Pair Kerning Data section, */ |
| /* saving the data */ |
| switch(recognize(keyword,tokenlen)) |
| { |
| case COMMENT: |
| keyword = linetoken(fp); |
| break; |
| case KERNPAIR: |
| if (!(pcount < fi->numOfPairs)) |
| { |
| reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs), |
| enlargeCount(fi->numOfPairs), sizeof(PairKernData) ); |
| } |
| if (pcount < fi->numOfPairs) |
| { |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->pkd[pos].name1 = strdup( keyword ); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->pkd[pos].name2 = strdup( keyword ); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->pkd[pos].xamt = atoi(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->pkd[pos++].yamt = atoi(keyword); |
| pcount++; |
| } |
| else |
| { |
| error = parseError; |
| cont = false; |
| } |
| break; |
| case KERNPAIRXAMT: |
| if (!(pcount < fi->numOfPairs)) |
| { |
| reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs), |
| enlargeCount(fi->numOfPairs), sizeof(PairKernData) ); |
| } |
| if (pcount < fi->numOfPairs) |
| { |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->pkd[pos].name1 = strdup( keyword ); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->pkd[pos].name2 = strdup( keyword ); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->pkd[pos++].xamt = atoi(keyword); |
| pcount++; |
| } |
| else |
| { |
| error = parseError; |
| cont = false; |
| } |
| break; |
| case ENDKERNPAIRS: |
| case ENDKERNDATA: |
| cont = false; |
| break; |
| case ENDFONTMETRICS: |
| cont = false; |
| error = normalEOF; |
| break; |
| case NOPE: |
| default: |
| error = parseError; |
| break; |
| } /* switch */ |
| } /* while */ |
| |
| if ((error == ok) && (pcount != fi->numOfPairs)) |
| error = reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs), |
| pcount, sizeof(PairKernData) ); |
| |
| if (error == ok && pcount != fi->numOfPairs) |
| error = parseError; |
| |
| return(error); |
| |
| } /* parsePairKernData */ |
| |
| |
| /************************* parseCompCharData **************************/ |
| |
| /* This function is called by "parseFile". It will parse the AFM file |
| * up to the "EndComposites" keyword. It will save the composite |
| * character data if requested by the caller of parseFile. |
| * |
| * parseCompCharData is passed in a pointer to the FontInfo record, and |
| * a boolean representing if the data should be saved. |
| * |
| * This function will create the appropriate amount of storage for |
| * the composite character data and store a pointer to the storage |
| * in the FontInfo record. |
| * |
| * This function returns an error code specifying whether there was |
| * a premature EOF or a parsing error. This return value is used by |
| * parseFile to determine if there is more file to parse. |
| */ |
| |
| static int parseCompCharData( FileInputStream* fp, register FontInfo* fi) |
| { |
| bool cont = true, firstTime = true, save = (fi->ccd != NULL); |
| int pos = 0, j = 0, error = ok, ccount = 0, pcount = 0, tokenlen; |
| register char *keyword; |
| |
| while (cont) |
| { |
| keyword = token(fp,tokenlen); |
| if (keyword == NULL) |
| /* Have reached an early and unexpected EOF. */ |
| /* Set flag and stop parsing */ |
| { |
| error = earlyEOF; |
| break; /* get out of loop */ |
| } |
| if (ccount > fi->numOfComps) |
| { |
| reallocFontMetrics( (void**)&(fi->ccd), &(fi->numOfComps), |
| enlargeCount(fi->numOfComps), sizeof(CompCharData) ); |
| } |
| if (ccount > fi->numOfComps) |
| { |
| error = parseError; |
| break; /* get out of loop */ |
| } |
| if (!save) |
| /* get tokens until the end of the Composite Character info */ |
| /* section without saving any of the data */ |
| switch(recognize(keyword,tokenlen)) |
| { |
| case ENDCOMPOSITES: |
| cont = false; |
| break; |
| case ENDFONTMETRICS: |
| cont = false; |
| error = normalEOF; |
| break; |
| case COMMENT: |
| case COMPCHAR: |
| keyword = linetoken(fp); |
| break; |
| default: |
| break; |
| } /* switch */ |
| else |
| /* otherwise parse entire Composite Character info section, */ |
| /* saving the data */ |
| switch(recognize(keyword,tokenlen)) |
| { |
| case COMMENT: |
| keyword = linetoken(fp); |
| break; |
| case COMPCHAR: |
| if (!(ccount < fi->numOfComps)) |
| { |
| reallocFontMetrics( (void**)&(fi->ccd), &(fi->numOfComps), |
| enlargeCount(fi->numOfComps), sizeof(CompCharData) ); |
| } |
| if (ccount < fi->numOfComps) |
| { |
| keyword = token(fp,tokenlen); |
| if (pcount != fi->ccd[pos].numOfPieces) |
| error = parseError; |
| pcount = 0; |
| if (firstTime) firstTime = false; |
| else pos++; |
| fi->ccd[pos].ccName = strdup( keyword ); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->ccd[pos].numOfPieces = atoi(keyword); |
| fi->ccd[pos].pieces = (Pcc *) |
| calloc(fi->ccd[pos].numOfPieces, sizeof(Pcc)); |
| j = 0; |
| ccount++; |
| } |
| else |
| { |
| error = parseError; |
| cont = false; |
| } |
| break; |
| case COMPCHARPIECE: |
| if (pcount < fi->ccd[pos].numOfPieces) |
| { |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->ccd[pos].pieces[j].pccName = strdup( keyword ); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->ccd[pos].pieces[j].deltax = atoi(keyword); |
| if ((keyword = token(fp,tokenlen)) != NULL) |
| fi->ccd[pos].pieces[j++].deltay = atoi(keyword); |
| pcount++; |
| } |
| else |
| error = parseError; |
| break; |
| case ENDCOMPOSITES: |
| cont = false; |
| break; |
| case ENDFONTMETRICS: |
| cont = false; |
| error = normalEOF; |
| break; |
| case NOPE: |
| default: |
| error = parseError; |
| break; |
| } /* switch */ |
| } /* while */ |
| |
| if (error == ok && ccount != fi->numOfComps) |
| reallocFontMetrics( (void**)&(fi->ccd), &(fi->numOfComps), |
| ccount, sizeof(CompCharData) ); |
| |
| if (error == ok && ccount != fi->numOfComps) |
| error = parseError; |
| |
| return(error); |
| |
| } /* parseCompCharData */ |
| |
| |
| |
| |
| /*************************** 'PUBLIC' FUNCTION ********************/ |
| |
| |
| /*************************** parseFile *****************************/ |
| |
| /* parseFile is the only 'public' procedure available. It is called |
| * from an application wishing to get information from an AFM file. |
| * The caller of this function is responsible for locating and opening |
| * an AFM file and handling all errors associated with that task. |
| * |
| * parseFile expects 3 parameters: a filename pointer, a pointer |
| * to a (FontInfo *) variable (for which storage will be allocated and |
| * the data requested filled in), and a mask specifying which |
| * data from the AFM file should be saved in the FontInfo structure. |
| * |
| * The file will be parsed and the requested data will be stored in |
| * a record of type FontInfo (refer to ParseAFM.h). |
| * |
| * parseFile returns an error code as defined in parseAFM.h. |
| * |
| * The position of the read/write pointer associated with the file |
| * pointer upon return of this function is undefined. |
| */ |
| |
| int parseFile( const char* pFilename, FontInfo** fi, FLAGS flags) |
| { |
| FileInputStream aFile( pFilename ); |
| |
| int code = ok; /* return code from each of the parsing routines */ |
| int error = ok; /* used as the return code from this function */ |
| int tokenlen; |
| |
| register char *keyword; /* used to store a token */ |
| |
| |
| (*fi) = (FontInfo *) calloc(1, sizeof(FontInfo)); |
| if ((*fi) == NULL) {error = storageProblem; return(error);} |
| |
| if (flags & P_G) |
| { |
| (*fi)->gfi = (GlobalFontInfo *) calloc(1, sizeof(GlobalFontInfo)); |
| if ((*fi)->gfi == NULL) {error = storageProblem; return(error);} |
| } |
| |
| /* The AFM file begins with Global Font Information. This section */ |
| /* will be parsed whether or not information should be saved. */ |
| code = parseGlobals(&aFile, (*fi)->gfi); |
| |
| if (code < 0) error = code; |
| |
| /* The Global Font Information is followed by the Character Metrics */ |
| /* section. Which procedure is used to parse this section depends on */ |
| /* how much information should be saved. If all of the metrics info */ |
| /* is wanted, parseCharMetrics is called. If only the character widths */ |
| /* is wanted, parseCharWidths is called. parseCharWidths will also */ |
| /* be called in the case that no character data is to be saved, just */ |
| /* to parse through the section. */ |
| |
| if ((code != normalEOF) && (code != earlyEOF)) |
| { |
| if ((keyword = token(&aFile,tokenlen)) != NULL) |
| (*fi)->numOfChars = atoi(keyword); |
| if (flags & (P_M ^ P_W)) |
| { |
| (*fi)->cmi = (CharMetricInfo *) |
| calloc((*fi)->numOfChars, sizeof(CharMetricInfo)); |
| if ((*fi)->cmi == NULL) {error = storageProblem; return(error);} |
| code = parseCharMetrics(&aFile, *fi); |
| } |
| else |
| { |
| if (flags & P_W) |
| { |
| (*fi)->cwi = (int *) calloc(256, sizeof(int)); |
| if ((*fi)->cwi == NULL) |
| { |
| error = storageProblem; |
| return(error); |
| } |
| } |
| /* parse section regardless */ |
| code = parseCharWidths(&aFile, (*fi)->cwi); |
| } /* else */ |
| } /* if */ |
| |
| if ((error != earlyEOF) && (code < 0)) error = code; |
| |
| /* The remaining sections of the AFM are optional. This code will */ |
| /* look at the next keyword in the file to determine what section */ |
| /* is next, and then allocate the appropriate amount of storage */ |
| /* for the data (if the data is to be saved) and call the */ |
| /* appropriate parsing routine to parse the section. */ |
| |
| while ((code != normalEOF) && (code != earlyEOF)) |
| { |
| keyword = token(&aFile,tokenlen); |
| if (keyword == NULL) |
| /* Have reached an early and unexpected EOF. */ |
| /* Set flag and stop parsing */ |
| { |
| code = earlyEOF; |
| break; /* get out of loop */ |
| } |
| switch(recognize(keyword,tokenlen)) |
| { |
| case STARTKERNDATA: |
| break; |
| case ENDKERNDATA: |
| break; |
| case STARTTRACKKERN: |
| keyword = token(&aFile,tokenlen); |
| if ((flags & P_T) && keyword) |
| { |
| (*fi)->numOfTracks = atoi(keyword); |
| (*fi)->tkd = (TrackKernData *) |
| calloc((*fi)->numOfTracks, sizeof(TrackKernData)); |
| if ((*fi)->tkd == NULL) |
| { |
| error = storageProblem; |
| return(error); |
| } |
| } /* if */ |
| code = parseTrackKernData(&aFile, *fi); |
| break; |
| case STARTKERNPAIRS: |
| keyword = token(&aFile,tokenlen); |
| if ((flags & P_P) && keyword) |
| { |
| (*fi)->numOfPairs = atoi(keyword); |
| (*fi)->pkd = (PairKernData *) |
| calloc((*fi)->numOfPairs, sizeof(PairKernData)); |
| if ((*fi)->pkd == NULL) |
| { |
| error = storageProblem; |
| return(error); |
| } |
| } /* if */ |
| code = parsePairKernData(&aFile, *fi); |
| break; |
| case STARTCOMPOSITES: |
| keyword = token(&aFile,tokenlen); |
| if ((flags & P_C) && keyword) |
| { |
| (*fi)->numOfComps = atoi(keyword); |
| (*fi)->ccd = (CompCharData *) |
| calloc((*fi)->numOfComps, sizeof(CompCharData)); |
| if ((*fi)->ccd == NULL) |
| { |
| error = storageProblem; |
| return(error); |
| } |
| } /* if */ |
| code = parseCompCharData(&aFile, *fi); |
| break; |
| case ENDFONTMETRICS: |
| code = normalEOF; |
| break; |
| case COMMENT: |
| linetoken(&aFile); |
| break; |
| case NOPE: |
| default: |
| code = parseError; |
| break; |
| } /* switch */ |
| |
| if ((error != earlyEOF) && (code < 0)) error = code; |
| |
| } /* while */ |
| |
| if ((error != earlyEOF) && (code < 0)) error = code; |
| |
| return(error); |
| |
| } /* parseFile */ |
| |
| void |
| freeFontInfo (FontInfo *fi) |
| { |
| int i, j; |
| |
| if (fi->gfi) |
| { |
| free (fi->gfi->afmVersion); |
| free (fi->gfi->fontName); |
| free (fi->gfi->fullName); |
| free (fi->gfi->familyName); |
| free (fi->gfi->weight); |
| free (fi->gfi->version); |
| free (fi->gfi->notice); |
| free (fi->gfi->encodingScheme); |
| free (fi->gfi); |
| } |
| |
| free (fi->cwi); |
| |
| if (fi->cmi) |
| { |
| for (i = 0; i < fi->numOfChars; i++) |
| { |
| Ligature *ligs; |
| free (fi->cmi[i].name); |
| ligs = fi->cmi[i].ligs; |
| while (ligs) |
| { |
| Ligature *tmp; |
| tmp = ligs; |
| ligs = ligs->next; |
| free (tmp->succ); |
| free (tmp->lig); |
| free (tmp); |
| } |
| } |
| free (fi->cmi); |
| } |
| |
| free (fi->tkd); |
| |
| if (fi->pkd) |
| { |
| for ( i = 0; i < fi->numOfPairs; i++) |
| { |
| free (fi->pkd[i].name1); |
| free (fi->pkd[i].name2); |
| } |
| free (fi->pkd); |
| } |
| |
| if (fi->ccd) |
| { |
| for (i = 0; i < fi->numOfComps; i++) |
| { |
| free (fi->ccd[i].ccName); |
| for (j = 0; j < fi->ccd[i].numOfPieces; j++) |
| free (fi->ccd[i].pieces[j].pccName); |
| |
| free (fi->ccd[i].pieces); |
| } |
| free (fi->ccd); |
| } |
| |
| free (fi); |
| } |
| |
| } // namspace |