2 * (C) 1988, 1989, 1990 by Adobe Systems Incorporated. All rights reserved.
4 * This file may be freely copied and redistributed as long as:
5 * 1) This entire notice continues to be included in the file,
6 * 2) If the file has been modified in any way, a notice of such
7 * modification is conspicuously indicated.
9 * PostScript, Display PostScript, and Adobe are registered trademarks of
10 * Adobe Systems Incorporated.
12 * ************************************************************************
13 * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT
14 * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS
15 * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR
16 * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY
17 * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION,
18 * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
20 * ************************************************************************
24 * Changes made for OpenOffice.org
26 * 10/24/2000 pl - changed code to compile with c++-compilers
27 * - added namespace to avoid symbol clashes
28 * - replaced BOOL by bool
29 * - added function to free space allocated by parseFile
30 * 10/26/2000 pl - added additional keys
31 * - added ability to parse slightly broken files
32 * - added charwidth member to GlobalFontInfo
33 * 04/26/2001 pl - added OpenOffice header
34 * 10/19/2005 pl - performance increase:
35 * - fread file in one pass
36 * - replace file io by buffer access
37 * 10/20/2005 pl - performance increase:
38 * - use one table lookup in token() routine
39 * instead of many conditions
40 * - return token length in toke() routine
41 * - use hash lookup instead of binary search
42 * in recognize() routine
45 /*************************************************************************
47 * $RCSfile: parseAFM.cxx,v $
51 * last change: $Author: rt $ $Date: 2008-01-29 16:08:31 $
53 ************************************************************************/
55 // MARKER(update_precomp.py): autogen include statement, do not remove
56 #include "precompiled_vcl.hxx"
60 * This file is used in conjuction with the parseAFM.h header file.
61 * This file contains several procedures that are used to parse AFM
62 * files. It is intended to work with an application program that needs
63 * font metric information. The program can be used as is by making a
64 * procedure call to "parseFile" (passing in the expected parameters)
65 * and having it fill in a data structure with the data from the
66 * AFM file, or an application developer may wish to customize this
69 * There is also a file, parseAFMclient.c, that is a sample application
70 * showing how to call the "parseFile" procedure and how to use the data
71 * after "parseFile" has returned.
73 * Please read the comments in parseAFM.h and parseAFMclient.c.
76 * original: DSM Thu Oct 20 17:39:59 PDT 1988
77 * modified: DSM Mon Jul 3 14:17:50 PDT 1989
78 * - added 'storageProblem' return code
79 * - fixed bug of not allocating extra byte for string duplication
81 * modified: DSM Tue Apr 3 11:18:34 PDT 1990
82 * - added free(ident) at end of parseFile routine
83 * modified: DSM Tue Jun 19 10:16:29 PDT 1990
84 * - changed (width == 250) to (width = 250) in initializeArray
95 #include "parseAFM.hxx"
96 #include "vcl/strhelper.hxx"
98 #include "rtl/alloc.h"
100 #define lineterm EOL /* line terminating character */
101 #define normalEOF 1 /* return code from parsing routines used only */
103 #define Space "space" /* used in string comparison to look for the width */
104 /* of the space character to init the widths array */
105 #define False "false" /* used in string comparison to check the value of */
106 /* boolean keys (e.g. IsFixedPitch) */
108 #define MATCH(A,B) (strncmp((A),(B), MAX_NAME) == 0)
112 class FileInputStream
118 FileInputStream( const char* pFilename
);
121 int getChar() { return (m_nPos
< m_nLen
) ? int(m_pMemory
[m_nPos
++]) : -1; }
127 unsigned int tell() const { return m_nPos
; }
128 void seek( unsigned int nPos
)
129 // NOTE: do not check input data since only results of tell()
130 // get seek()ed in this file
134 FileInputStream::FileInputStream( const char* pFilename
) :
140 if( ! stat( pFilename
, &aStat
) &&
141 S_ISREG( aStat
.st_mode
) &&
145 FILE* fp
= fopen( pFilename
, "r" );
148 m_pMemory
= (char*)rtl_allocateMemory( aStat
.st_size
);
149 m_nLen
= (unsigned int)fread( m_pMemory
, 1, aStat
.st_size
, fp
);
155 FileInputStream::~FileInputStream()
157 rtl_freeMemory( m_pMemory
);
160 /*************************** GLOBALS ***********************/
161 /* "shorts" for fast case statement
162 * The values of each of these enumerated items correspond to an entry in the
163 * table of strings defined below. Therefore, if you add a new string as
164 * new keyword into the keyStrings table, you must also add a corresponding
165 * parseKey AND it MUST be in the same position!
167 * IMPORTANT: since the sorting algorithm is a binary search, the strings of
168 * keywords must be placed in lexicographical order, below. [Therefore, the
169 * enumerated items are not necessarily in lexicographical order, depending
170 * on the name chosen. BUT, they must be placed in the same position as the
171 * corresponding key string.] The NOPE shall remain in the last position,
172 * since it does not correspond to any key string, and it is used in the
173 * "recognize" procedure to calculate how many possible keys there are.
176 // some metrics have Ascent, Descent instead Ascender, Descender or Em
177 // which is not allowed per afm spcification, but let us handle
180 ASCENDER
, ASCENT
, CHARBBOX
, CODE
, COMPCHAR
, CODEHEX
, CAPHEIGHT
, CHARWIDTH
, CHARACTERSET
, CHARACTERS
, COMMENT
,
181 DESCENDER
, DESCENT
, EM
, ENCODINGSCHEME
, ENDCHARMETRICS
, ENDCOMPOSITES
, ENDDIRECTION
,
182 ENDFONTMETRICS
, ENDKERNDATA
, ENDKERNPAIRS
, ENDTRACKKERN
,
183 FAMILYNAME
, FONTBBOX
, FONTNAME
, FULLNAME
, ISBASEFONT
, ISFIXEDPITCH
,
184 ITALICANGLE
, KERNPAIR
, KERNPAIRXAMT
, LIGATURE
, MAPPINGSCHEME
, METRICSSETS
, CHARNAME
,
185 NOTICE
, COMPCHARPIECE
, STARTCHARMETRICS
, STARTCOMPOSITES
, STARTDIRECTION
,
186 STARTFONTMETRICS
, STARTKERNDATA
, STARTKERNPAIRS
,
187 STARTTRACKKERN
, STDHW
, STDVW
, TRACKKERN
, UNDERLINEPOSITION
,
188 UNDERLINETHICKNESS
, VVECTOR
, VERSION
, XYWIDTH
, X0WIDTH
, XWIDTH
, WEIGHT
, XHEIGHT
,
192 /*************************** PARSING ROUTINES **************/
194 /*************************** token *************************/
196 /* A "AFM file Conventions" tokenizer. That means that it will
197 * return the next token delimited by white space. See also
198 * the `linetoken' routine, which does a similar thing but
199 * reads all tokens until the next end-of-line.
202 // token white space is ' ', '\n', '\r', ',', '\t', ';'
203 static const bool is_white_Array
[ 256 ] =
204 { false, false, false, false, false, false, false, false, // 0-7
205 false, true, true, false, false, true, false, false, // 8-15
206 false, false, false, false, false, false, false, false, // 16-23
207 false, false, false, false, false, false, false, false, // 24-31
208 true, false, false, false, false, false, false, false, // 32-39
209 false, false, false, false, true, false, false, false, // 40-47
210 false, false, false, false, false, false, false, false, // 48-55
211 false, false, false, true, false, false, false, false, // 56-63
213 false, false, false, false, false, false, false, false, // 64 -
214 false, false, false, false, false, false, false, false,
215 false, false, false, false, false, false, false, false,
216 false, false, false, false, false, false, false, false,
217 false, false, false, false, false, false, false, false,
218 false, false, false, false, false, false, false, false,
219 false, false, false, false, false, false, false, false,
220 false, false, false, false, false, false, false, false, // 127
222 false, false, false, false, false, false, false, false, // 128 -
223 false, false, false, false, false, false, false, false,
224 false, false, false, false, false, false, false, false,
225 false, false, false, false, false, false, false, false,
226 false, false, false, false, false, false, false, false,
227 false, false, false, false, false, false, false, false,
228 false, false, false, false, false, false, false, false,
229 false, false, false, false, false, false, false, false, // 191
231 false, false, false, false, false, false, false, false, // 192 -
232 false, false, false, false, false, false, false, false,
233 false, false, false, false, false, false, false, false,
234 false, false, false, false, false, false, false, false,
235 false, false, false, false, false, false, false, false,
236 false, false, false, false, false, false, false, false,
237 false, false, false, false, false, false, false, false,
238 false, false, false, false, false, false, false, false, // 255
240 // token delimiters are ' ', '\n', '\r', '\t', ':', ';'
241 static const bool is_delimiter_Array
[ 256 ] =
242 { false, false, false, false, false, false, false, false, // 0-7
243 false, true, true, false, false, true, false, false, // 8-15
244 false, false, false, false, false, false, false, false, // 16-23
245 false, false, false, false, false, false, false, false, // 24-31
246 true, false, false, false, false, false, false, false, // 32-39
247 false, false, false, false, false, false, false, false, // 40-47
248 false, false, false, false, false, false, false, false, // 48-55
249 false, false, true, true, false, false, false, false, // 56-63
251 false, false, false, false, false, false, false, false, // 64 -
252 false, false, false, false, false, false, false, false,
253 false, false, false, false, false, false, false, false,
254 false, false, false, false, false, false, false, false,
255 false, false, false, false, false, false, false, false,
256 false, false, false, false, false, false, false, false,
257 false, false, false, false, false, false, false, false,
258 false, false, false, false, false, false, false, false, // 127
260 false, false, false, false, false, false, false, false, // 128 -
261 false, false, false, false, false, false, false, false,
262 false, false, false, false, false, false, false, false,
263 false, false, false, false, false, false, false, false,
264 false, false, false, false, false, false, false, false,
265 false, false, false, false, false, false, false, false,
266 false, false, false, false, false, false, false, false,
267 false, false, false, false, false, false, false, false, // 191
269 false, false, false, false, false, false, false, false, // 192 -
270 false, false, false, false, false, false, false, false,
271 false, false, false, false, false, false, false, false,
272 false, false, false, false, false, false, false, false,
273 false, false, false, false, false, false, false, false,
274 false, false, false, false, false, false, false, false,
275 false, false, false, false, false, false, false, false,
276 false, false, false, false, false, false, false, false, // 255
278 static char *token( FileInputStream
* stream
, int& rLen
)
280 static char ident
[MAX_NAME
]; /* storage buffer for keywords */
284 /* skip over white space */
285 // relies on EOF = -1
286 while( is_white_Array
[ (ch
= stream
->getChar()) & 255 ] )
290 while( ch
!= -1 && ! is_delimiter_Array
[ ch
& 255 ] && idx
< MAX_NAME
-1 )
293 ch
= stream
->getChar();
296 if (ch
== -1 && idx
< 1) return ((char *)NULL
);
297 if (idx
>= 1 && ch
!= ':' && ch
!= -1) stream
->ungetChar();
298 if (idx
< 1 ) ident
[idx
++] = ch
; /* single-character token */
302 return(ident
); /* returns pointer to the token */
307 /*************************** linetoken *************************/
309 /* "linetoken" will get read all tokens until the EOL character from
310 * the given stream. This is used to get any arguments that can be
311 * more than one word (like Comment lines and FullName).
314 static char *linetoken( FileInputStream
* stream
)
316 static char ident
[MAX_NAME
]; /* storage buffer for keywords */
319 while ((ch
= stream
->getChar()) == ' ' || ch
== '\t' ) ;
322 while (ch
!= -1 && ch
!= lineterm
&& ch
!= '\r' && idx
< MAX_NAME
-1 )
325 ch
= stream
->getChar();
331 return(ident
); /* returns pointer to the token */
336 /*************************** recognize *************************/
338 /* This function tries to match a string to a known list of
339 * valid AFM entries (check the keyStrings array above).
340 * "ident" contains everything from white space through the
341 * next space, tab, or ":" character.
343 * The algorithm is a standard Knuth binary search.
345 #include "afm_hash.cpp"
347 static inline enum parseKey
recognize( register char* ident
, int len
)
349 const hash_entry
* pEntry
= AfmKeywordHash::in_word_set( ident
, len
);
350 return pEntry
? pEntry
->eKey
: NOPE
;
355 /************************* parseGlobals *****************************/
357 /* This function is called by "parseFile". It will parse the AFM file
358 * up to the "StartCharMetrics" keyword, which essentially marks the
359 * end of the Global Font Information and the beginning of the character
360 * metrics information.
362 * If the caller of "parseFile" specified that it wanted the Global
363 * Font Information (as defined by the "AFM file Specification"
364 * document), then that information will be stored in the returned
367 * Any Global Font Information entries that are not found in a
368 * given file, will have the usual default initialization value
369 * for its type (i.e. entries of type int will be 0, etc).
371 * This function returns an error code specifying whether there was
372 * a premature EOF or a parsing error. This return value is used by
373 * parseFile to determine if there is more file to parse.
376 static int parseGlobals( FileInputStream
* fp
, register GlobalFontInfo
* gfi
)
378 bool cont
= true, save
= (gfi
!= NULL
);
380 register char *keyword
;
386 keyword
= token(fp
, tokenlen
);
389 /* Have reached an early and unexpected EOF. */
390 /* Set flag and stop parsing */
393 break; /* get out of loop */
396 /* get tokens until the end of the Global Font info section */
397 /* without saving any of the data */
398 switch (recognize(keyword
, tokenlen
))
400 case STARTCHARMETRICS
:
411 /* otherwise parse entire global font info section, */
412 /* saving the data */
413 switch(recognize(keyword
, tokenlen
))
415 case STARTFONTMETRICS
:
416 keyword
= token(fp
,tokenlen
);
417 gfi
->afmVersion
= strdup( keyword
);
420 keyword
= linetoken(fp
);
423 keyword
= token(fp
, tokenlen
);
424 gfi
->fontName
= strdup( keyword
);
427 keyword
= token(fp
, tokenlen
);
428 gfi
->encodingScheme
= strdup( keyword
);
431 keyword
= linetoken(fp
);
432 gfi
->fullName
= strdup( keyword
);
435 keyword
= linetoken(fp
);
436 gfi
->familyName
= strdup( keyword
);
439 keyword
= token(fp
, tokenlen
);
440 gfi
->weight
= strdup( keyword
);
443 keyword
= token(fp
,tokenlen
);
444 gfi
->italicAngle
= StringToDouble( keyword
);
447 keyword
= token(fp
,tokenlen
);
448 if (MATCH(keyword
, False
))
449 gfi
->isFixedPitch
= 0;
451 gfi
->isFixedPitch
= 1;
453 case UNDERLINEPOSITION
:
454 keyword
= token(fp
,tokenlen
);
455 gfi
->underlinePosition
= atoi(keyword
);
457 case UNDERLINETHICKNESS
:
458 keyword
= token(fp
,tokenlen
);
459 gfi
->underlineThickness
= atoi(keyword
);
462 keyword
= token(fp
,tokenlen
);
463 gfi
->version
= strdup( keyword
);
466 keyword
= linetoken(fp
);
467 gfi
->notice
= strdup( keyword
);
470 keyword
= token(fp
,tokenlen
);
471 gfi
->fontBBox
.llx
= atoi(keyword
);
472 keyword
= token(fp
,tokenlen
);
473 gfi
->fontBBox
.lly
= atoi(keyword
);
474 keyword
= token(fp
,tokenlen
);
475 gfi
->fontBBox
.urx
= atoi(keyword
);
476 keyword
= token(fp
,tokenlen
);
477 gfi
->fontBBox
.ury
= atoi(keyword
);
480 keyword
= token(fp
,tokenlen
);
481 gfi
->capHeight
= atoi(keyword
);
484 keyword
= token(fp
,tokenlen
);
485 gfi
->xHeight
= atoi(keyword
);
488 keyword
= token(fp
,tokenlen
);
489 gfi
->descender
= -atoi(keyword
);
492 keyword
= token(fp
,tokenlen
);
493 gfi
->descender
= atoi(keyword
);
497 keyword
= token(fp
,tokenlen
);
498 gfi
->ascender
= atoi(keyword
);
500 case STARTCHARMETRICS
:
509 keyword
= token(fp
,tokenlen
);
512 keyword
= token(fp
,tokenlen
);
513 direction
= atoi(keyword
);
514 break; /* ignore this for now */
516 break; /* ignore this for now */
518 keyword
= token(fp
,tokenlen
);
519 break; /* ignore this for now */
521 keyword
= token(fp
,tokenlen
);
522 break; /* ignore this for now */
524 keyword
= token(fp
,tokenlen
);
525 break; /* ignore this for now */
527 keyword
=token(fp
,tokenlen
); //ignore
530 keyword
=token(fp
,tokenlen
); //ignore
533 keyword
=token(fp
,tokenlen
); //ignore
536 keyword
= token(fp
,tokenlen
);
538 gfi
->charwidth
= atoi(keyword
);
539 keyword
= token(fp
,tokenlen
);
540 /* ignore y-width for now */
543 keyword
= token(fp
,tokenlen
);
544 break; /* ignore this for now */
558 /************************* initializeArray ************************/
560 /* Unmapped character codes are (at Adobe Systems) assigned the
561 * width of the space character (if one exists) else they get the
562 * value of 250 ems. This function initializes all entries in the
563 * char widths array to have this value. Then any mapped character
564 * codes will be replaced with the width of the appropriate character
565 * when parsing the character metric section.
567 * This function parses the Character Metrics Section looking
568 * for a space character (by comparing character names). If found,
569 * the width of the space character will be used to initialize the
570 * values in the array of character widths.
572 * Before returning, the position of the read/write pointer of the
573 * FileInputStream is reset to be where it was upon entering this function.
576 static int initializeArray( FileInputStream
* fp
, register int* cwi
)
578 bool cont
= true, found
= false;
579 unsigned int opos
= fp
->tell();
580 int code
= 0, width
= 0, i
= 0, error
= 0, tokenlen
;
581 register char *keyword
;
585 keyword
= token(fp
,tokenlen
);
589 break; /* get out of loop */
591 switch(recognize(keyword
,tokenlen
))
594 keyword
= linetoken(fp
);
597 code
= atoi(token(fp
,tokenlen
));
600 sscanf(token(fp
,tokenlen
),"<%x>", &code
);
603 width
= atoi(token(fp
,tokenlen
));
606 (void) token(fp
,tokenlen
);
609 keyword
= token(fp
,tokenlen
);
610 if (MATCH(keyword
, Space
))
633 for (i
= 0; i
< 256; ++i
)
640 } /* initializeArray */
643 /************************* parseCharWidths **************************/
645 /* This function is called by "parseFile". It will parse the AFM file
646 * up to the "EndCharMetrics" keyword. It will save the character
647 * width info (as opposed to all of the character metric information)
648 * if requested by the caller of parseFile. Otherwise, it will just
649 * parse through the section without saving any information.
651 * If data is to be saved, parseCharWidths is passed in a pointer
652 * to an array of widths that has already been initialized by the
653 * standard value for unmapped character codes. This function parses
654 * the Character Metrics section only storing the width information
655 * for the encoded characters into the array using the character code
656 * as the index into that array.
658 * This function returns an error code specifying whether there was
659 * a premature EOF or a parsing error. This return value is used by
660 * parseFile to determine if there is more file to parse.
663 static int parseCharWidths( FileInputStream
* fp
, register int* cwi
)
665 bool cont
= true, save
= (cwi
!= NULL
);
666 int pos
= 0, error
= ok
, tokenlen
;
667 register char *keyword
;
671 keyword
= token(fp
,tokenlen
);
672 /* Have reached an early and unexpected EOF. */
673 /* Set flag and stop parsing */
677 break; /* get out of loop */
680 /* get tokens until the end of the Char Metrics section without */
681 /* saving any of the data*/
682 switch (recognize(keyword
,tokenlen
))
695 /* otherwise parse entire char metrics section, saving */
696 /* only the char x-width info */
697 switch(recognize(keyword
,tokenlen
))
700 keyword
= linetoken(fp
);
703 keyword
= token(fp
,tokenlen
);
707 /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
708 keyword
= token(fp
,tokenlen
); keyword
= token(fp
,tokenlen
); /* eat values */
712 keyword
= token(fp
,tokenlen
);
713 sscanf(keyword
, "<%x>", &pos
);
716 (void) token(fp
,tokenlen
);
719 keyword
= token(fp
,tokenlen
);
720 if (pos
>= 0) /* ignore unmapped chars */
721 cwi
[pos
] = atoi(keyword
);
730 case CHARNAME
: /* eat values (so doesn't cause parseError) */
731 keyword
= token(fp
,tokenlen
);
734 keyword
= token(fp
,tokenlen
); keyword
= token(fp
,tokenlen
);
735 keyword
= token(fp
,tokenlen
); keyword
= token(fp
,tokenlen
);
738 keyword
= token(fp
,tokenlen
); keyword
= token(fp
,tokenlen
);
741 keyword
= token(fp
,tokenlen
);
742 keyword
= token(fp
,tokenlen
);
753 } /* parseCharWidths */
757 * number of char metrics is almost allways inaccurate, so be gentle and try to
758 * adapt our internal storage by adjusting the allocated list
762 reallocFontMetrics( void **pp_fontmetrics
, int *p_oldcount
, int n_newcount
, unsigned int n_size
)
764 char *p_tmpmetrics
= NULL
;
766 if ((pp_fontmetrics
== NULL
) || (*pp_fontmetrics
== NULL
))
767 return storageProblem
;
769 if (*p_oldcount
== n_newcount
)
772 p_tmpmetrics
= (char*)realloc(*pp_fontmetrics
, n_newcount
* n_size
);
773 if (p_tmpmetrics
== NULL
)
774 return storageProblem
;
776 if ( n_newcount
> *p_oldcount
)
778 char *p_inimetrics
= p_tmpmetrics
+ n_size
* *p_oldcount
;
779 int n_inimetrics
= n_size
* (n_newcount
- *p_oldcount
);
780 memset( p_inimetrics
, 0, n_inimetrics
);
783 *pp_fontmetrics
= p_tmpmetrics
;
784 *p_oldcount
= n_newcount
;
790 enlargeCount( unsigned int n_oldcount
)
792 unsigned int n_newcount
= n_oldcount
+ n_oldcount
/ 5;
793 if (n_oldcount
== n_newcount
)
794 n_newcount
= n_oldcount
+ 5;
799 /************************* parseCharMetrics ************************/
801 /* This function is called by parseFile if the caller of parseFile
802 * requested that all character metric information be saved
803 * (as opposed to only the character width information).
805 * parseCharMetrics is passed in a pointer to an array of records
806 * to hold information on a per character basis. This function
807 * parses the Character Metrics section storing all character
808 * metric information for the ALL characters (mapped and unmapped)
811 * This function returns an error code specifying whether there was
812 * a premature EOF or a parsing error. This return value is used by
813 * parseFile to determine if there is more file to parse.
816 static int parseCharMetrics( FileInputStream
* fp
, register FontInfo
* fi
)
818 bool cont
= true, firstTime
= true;
819 int error
= ok
, count
= 0, tokenlen
;
820 register CharMetricInfo
*temp
= fi
->cmi
;
821 register char *keyword
;
825 keyword
= token(fp
,tokenlen
);
829 break; /* get out of loop */
831 switch(recognize(keyword
,tokenlen
))
834 keyword
= linetoken(fp
);
837 if (!(count
< fi
->numOfChars
))
839 reallocFontMetrics( (void**)&(fi
->cmi
),
840 &(fi
->numOfChars
), enlargeCount(fi
->numOfChars
),
841 sizeof(CharMetricInfo
) );
842 temp
= &(fi
->cmi
[ count
- 1 ]);
844 if (count
< fi
->numOfChars
)
846 if (firstTime
) firstTime
= false;
848 temp
->code
= atoi(token(fp
,tokenlen
));
849 if (fi
->gfi
&& fi
->gfi
->charwidth
)
850 temp
->wx
= fi
->gfi
->charwidth
;
860 if (!(count
< fi
->numOfChars
))
862 reallocFontMetrics( (void**)&(fi
->cmi
),
863 &(fi
->numOfChars
), enlargeCount(fi
->numOfChars
),
864 sizeof(CharMetricInfo
) );
865 temp
= &(fi
->cmi
[ count
- 1 ]);
867 if (count
< fi
->numOfChars
) {
872 sscanf(token(fp
,tokenlen
),"<%x>", &temp
->code
);
873 if (fi
->gfi
&& fi
->gfi
->charwidth
)
874 temp
->wx
= fi
->gfi
->charwidth
;
883 temp
->wx
= atoi(token(fp
,tokenlen
));
884 temp
->wy
= atoi(token(fp
,tokenlen
));
887 temp
->wx
= atoi(token(fp
,tokenlen
));
890 temp
->wx
= atoi(token(fp
,tokenlen
));
893 keyword
= token(fp
,tokenlen
);
894 temp
->name
= (char *)strdup(keyword
);
897 temp
->charBBox
.llx
= atoi(token(fp
,tokenlen
));
898 temp
->charBBox
.lly
= atoi(token(fp
,tokenlen
));
899 temp
->charBBox
.urx
= atoi(token(fp
,tokenlen
));
900 temp
->charBBox
.ury
= atoi(token(fp
,tokenlen
));
903 Ligature
**tail
= &(temp
->ligs
);
904 Ligature
*node
= *tail
;
908 while (node
->next
!= NULL
)
910 tail
= &(node
->next
);
913 *tail
= (Ligature
*) calloc(1, sizeof(Ligature
));
914 keyword
= token(fp
,tokenlen
);
915 (*tail
)->succ
= (char *)strdup(keyword
);
916 keyword
= token(fp
,tokenlen
);
917 (*tail
)->lig
= (char *)strdup(keyword
);
927 keyword
= token(fp
,tokenlen
);
928 keyword
= token(fp
,tokenlen
);
937 if ((error
== ok
) && (count
!= fi
->numOfChars
))
938 error
= reallocFontMetrics( (void**)&(fi
->cmi
), &(fi
->numOfChars
),
939 count
, sizeof(CharMetricInfo
) );
941 if ((error
== ok
) && (count
!= fi
->numOfChars
))
946 } /* parseCharMetrics */
950 /************************* parseTrackKernData ***********************/
952 /* This function is called by "parseFile". It will parse the AFM file
953 * up to the "EndTrackKern" or "EndKernData" keywords. It will save the
954 * track kerning data if requested by the caller of parseFile.
956 * parseTrackKernData is passed in a pointer to the FontInfo record.
957 * If data is to be saved, the FontInfo record will already contain
958 * a valid pointer to storage for the track kerning data.
960 * This function returns an error code specifying whether there was
961 * a premature EOF or a parsing error. This return value is used by
962 * parseFile to determine if there is more file to parse.
965 static int parseTrackKernData( FileInputStream
* fp
, register FontInfo
* fi
)
967 bool cont
= true, save
= (fi
->tkd
!= NULL
);
968 int pos
= 0, error
= ok
, tcount
= 0, tokenlen
;
969 register char *keyword
;
973 keyword
= token(fp
,tokenlen
);
978 break; /* get out of loop */
981 /* get tokens until the end of the Track Kerning Data */
982 /* section without saving any of the data */
983 switch(recognize(keyword
,tokenlen
))
997 /* otherwise parse entire Track Kerning Data section, */
998 /* saving the data */
999 switch(recognize(keyword
,tokenlen
))
1002 keyword
= linetoken(fp
);
1005 if (!(tcount
< fi
->numOfTracks
))
1007 reallocFontMetrics( (void**)&(fi
->tkd
), &(fi
->numOfTracks
),
1008 enlargeCount(fi
->numOfTracks
), sizeof(TrackKernData
) );
1011 if (tcount
< fi
->numOfTracks
)
1013 keyword
= token(fp
,tokenlen
);
1014 fi
->tkd
[pos
].degree
= atoi(keyword
);
1015 keyword
= token(fp
,tokenlen
);
1016 fi
->tkd
[pos
].minPtSize
= StringToDouble(keyword
);
1017 keyword
= token(fp
,tokenlen
);
1018 fi
->tkd
[pos
].minKernAmt
= StringToDouble(keyword
);
1019 keyword
= token(fp
,tokenlen
);
1020 fi
->tkd
[pos
].maxPtSize
= StringToDouble(keyword
);
1021 keyword
= token(fp
,tokenlen
);
1022 fi
->tkd
[pos
++].maxKernAmt
= StringToDouble(keyword
);
1035 case ENDFONTMETRICS
:
1046 if (error
== ok
&& tcount
!= fi
->numOfTracks
)
1047 error
= reallocFontMetrics( (void**)&(fi
->tkd
), &(fi
->numOfTracks
),
1048 tcount
, sizeof(TrackKernData
) );
1050 if (error
== ok
&& tcount
!= fi
->numOfTracks
)
1055 } /* parseTrackKernData */
1058 /************************* parsePairKernData ************************/
1060 /* This function is called by "parseFile". It will parse the AFM file
1061 * up to the "EndKernPairs" or "EndKernData" keywords. It will save
1062 * the pair kerning data if requested by the caller of parseFile.
1064 * parsePairKernData is passed in a pointer to the FontInfo record.
1065 * If data is to be saved, the FontInfo record will already contain
1066 * a valid pointer to storage for the pair kerning data.
1068 * This function returns an error code specifying whether there was
1069 * a premature EOF or a parsing error. This return value is used by
1070 * parseFile to determine if there is more file to parse.
1073 static int parsePairKernData( FileInputStream
* fp
, register FontInfo
* fi
)
1075 bool cont
= true, save
= (fi
->pkd
!= NULL
);
1076 int pos
= 0, error
= ok
, pcount
= 0, tokenlen
;
1077 register char *keyword
;
1081 keyword
= token(fp
,tokenlen
);
1083 if (keyword
== NULL
)
1086 break; /* get out of loop */
1089 /* get tokens until the end of the Pair Kerning Data */
1090 /* section without saving any of the data */
1091 switch(recognize(keyword
,tokenlen
))
1097 case ENDFONTMETRICS
:
1105 /* otherwise parse entire Pair Kerning Data section, */
1106 /* saving the data */
1107 switch(recognize(keyword
,tokenlen
))
1110 keyword
= linetoken(fp
);
1113 if (!(pcount
< fi
->numOfPairs
))
1115 reallocFontMetrics( (void**)&(fi
->pkd
), &(fi
->numOfPairs
),
1116 enlargeCount(fi
->numOfPairs
), sizeof(PairKernData
) );
1118 if (pcount
< fi
->numOfPairs
)
1120 keyword
= token(fp
,tokenlen
);
1121 fi
->pkd
[pos
].name1
= strdup( keyword
);
1122 keyword
= token(fp
,tokenlen
);
1123 fi
->pkd
[pos
].name2
= strdup( keyword
);
1124 keyword
= token(fp
,tokenlen
);
1125 fi
->pkd
[pos
].xamt
= atoi(keyword
);
1126 keyword
= token(fp
,tokenlen
);
1127 fi
->pkd
[pos
++].yamt
= atoi(keyword
);
1137 if (!(pcount
< fi
->numOfPairs
))
1139 reallocFontMetrics( (void**)&(fi
->pkd
), &(fi
->numOfPairs
),
1140 enlargeCount(fi
->numOfPairs
), sizeof(PairKernData
) );
1142 if (pcount
< fi
->numOfPairs
)
1144 keyword
= token(fp
,tokenlen
);
1145 fi
->pkd
[pos
].name1
= strdup( keyword
);
1146 keyword
= token(fp
,tokenlen
);
1147 fi
->pkd
[pos
].name2
= strdup( keyword
);
1148 keyword
= token(fp
,tokenlen
);
1149 fi
->pkd
[pos
++].xamt
= atoi(keyword
);
1162 case ENDFONTMETRICS
:
1173 if ((error
== ok
) && (pcount
!= fi
->numOfPairs
))
1174 error
= reallocFontMetrics( (void**)&(fi
->pkd
), &(fi
->numOfPairs
),
1175 pcount
, sizeof(PairKernData
) );
1177 if (error
== ok
&& pcount
!= fi
->numOfPairs
)
1182 } /* parsePairKernData */
1185 /************************* parseCompCharData **************************/
1187 /* This function is called by "parseFile". It will parse the AFM file
1188 * up to the "EndComposites" keyword. It will save the composite
1189 * character data if requested by the caller of parseFile.
1191 * parseCompCharData is passed in a pointer to the FontInfo record, and
1192 * a boolean representing if the data should be saved.
1194 * This function will create the appropriate amount of storage for
1195 * the composite character data and store a pointer to the storage
1196 * in the FontInfo record.
1198 * This function returns an error code specifying whether there was
1199 * a premature EOF or a parsing error. This return value is used by
1200 * parseFile to determine if there is more file to parse.
1203 static int parseCompCharData( FileInputStream
* fp
, register FontInfo
* fi
)
1205 bool cont
= true, firstTime
= true, save
= (fi
->ccd
!= NULL
);
1206 int pos
= 0, j
= 0, error
= ok
, ccount
= 0, pcount
= 0, tokenlen
;
1207 register char *keyword
;
1211 keyword
= token(fp
,tokenlen
);
1212 if (keyword
== NULL
)
1213 /* Have reached an early and unexpected EOF. */
1214 /* Set flag and stop parsing */
1217 break; /* get out of loop */
1219 if (ccount
> fi
->numOfComps
)
1221 reallocFontMetrics( (void**)&(fi
->ccd
), &(fi
->numOfComps
),
1222 enlargeCount(fi
->numOfComps
), sizeof(CompCharData
) );
1224 if (ccount
> fi
->numOfComps
)
1227 break; /* get out of loop */
1230 /* get tokens until the end of the Composite Character info */
1231 /* section without saving any of the data */
1232 switch(recognize(keyword
,tokenlen
))
1237 case ENDFONTMETRICS
:
1243 keyword
= linetoken(fp
);
1249 /* otherwise parse entire Composite Character info section, */
1250 /* saving the data */
1251 switch(recognize(keyword
,tokenlen
))
1254 keyword
= linetoken(fp
);
1257 if (!(ccount
< fi
->numOfComps
))
1259 reallocFontMetrics( (void**)&(fi
->ccd
), &(fi
->numOfComps
),
1260 enlargeCount(fi
->numOfComps
), sizeof(CompCharData
) );
1262 if (ccount
< fi
->numOfComps
)
1264 keyword
= token(fp
,tokenlen
);
1265 if (pcount
!= fi
->ccd
[pos
].numOfPieces
)
1268 if (firstTime
) firstTime
= false;
1270 fi
->ccd
[pos
].ccName
= strdup( keyword
);
1271 keyword
= token(fp
,tokenlen
);
1272 fi
->ccd
[pos
].numOfPieces
= atoi(keyword
);
1273 fi
->ccd
[pos
].pieces
= (Pcc
*)
1274 calloc(fi
->ccd
[pos
].numOfPieces
, sizeof(Pcc
));
1285 if (pcount
< fi
->ccd
[pos
].numOfPieces
)
1287 keyword
= token(fp
,tokenlen
);
1288 fi
->ccd
[pos
].pieces
[j
].pccName
= strdup( keyword
);
1289 keyword
= token(fp
,tokenlen
);
1290 fi
->ccd
[pos
].pieces
[j
].deltax
= atoi(keyword
);
1291 keyword
= token(fp
,tokenlen
);
1292 fi
->ccd
[pos
].pieces
[j
++].deltay
= atoi(keyword
);
1301 case ENDFONTMETRICS
:
1312 if (error
== ok
&& ccount
!= fi
->numOfComps
)
1313 reallocFontMetrics( (void**)&(fi
->ccd
), &(fi
->numOfComps
),
1314 ccount
, sizeof(CompCharData
) );
1316 if (error
== ok
&& ccount
!= fi
->numOfComps
)
1321 } /* parseCompCharData */
1326 /*************************** 'PUBLIC' FUNCTION ********************/
1329 /*************************** parseFile *****************************/
1331 /* parseFile is the only 'public' procedure available. It is called
1332 * from an application wishing to get information from an AFM file.
1333 * The caller of this function is responsible for locating and opening
1334 * an AFM file and handling all errors associated with that task.
1336 * parseFile expects 3 parameters: a filename pointer, a pointer
1337 * to a (FontInfo *) variable (for which storage will be allocated and
1338 * the data requested filled in), and a mask specifying which
1339 * data from the AFM file should be saved in the FontInfo structure.
1341 * The file will be parsed and the requested data will be stored in
1342 * a record of type FontInfo (refer to ParseAFM.h).
1344 * parseFile returns an error code as defined in parseAFM.h.
1346 * The position of the read/write pointer associated with the file
1347 * pointer upon return of this function is undefined.
1350 int parseFile( const char* pFilename
, FontInfo
** fi
, FLAGS flags
)
1352 FileInputStream
aFile( pFilename
);
1354 int code
= ok
; /* return code from each of the parsing routines */
1355 int error
= ok
; /* used as the return code from this function */
1358 register char *keyword
; /* used to store a token */
1361 (*fi
) = (FontInfo
*) calloc(1, sizeof(FontInfo
));
1362 if ((*fi
) == NULL
) {error
= storageProblem
; return(error
);}
1366 (*fi
)->gfi
= (GlobalFontInfo
*) calloc(1, sizeof(GlobalFontInfo
));
1367 if ((*fi
)->gfi
== NULL
) {error
= storageProblem
; return(error
);}
1370 /* The AFM file begins with Global Font Information. This section */
1371 /* will be parsed whether or not information should be saved. */
1372 code
= parseGlobals(&aFile
, (*fi
)->gfi
);
1374 if (code
< 0) error
= code
;
1376 /* The Global Font Information is followed by the Character Metrics */
1377 /* section. Which procedure is used to parse this section depends on */
1378 /* how much information should be saved. If all of the metrics info */
1379 /* is wanted, parseCharMetrics is called. If only the character widths */
1380 /* is wanted, parseCharWidths is called. parseCharWidths will also */
1381 /* be called in the case that no character data is to be saved, just */
1382 /* to parse through the section. */
1384 if ((code
!= normalEOF
) && (code
!= earlyEOF
))
1386 (*fi
)->numOfChars
= atoi(token(&aFile
,tokenlen
));
1387 if (flags
& (P_M
^ P_W
))
1389 (*fi
)->cmi
= (CharMetricInfo
*)
1390 calloc((*fi
)->numOfChars
, sizeof(CharMetricInfo
));
1391 if ((*fi
)->cmi
== NULL
) {error
= storageProblem
; return(error
);}
1392 code
= parseCharMetrics(&aFile
, *fi
);
1398 (*fi
)->cwi
= (int *) calloc(256, sizeof(int));
1399 if ((*fi
)->cwi
== NULL
)
1401 error
= storageProblem
;
1405 /* parse section regardless */
1406 code
= parseCharWidths(&aFile
, (*fi
)->cwi
);
1410 if ((error
!= earlyEOF
) && (code
< 0)) error
= code
;
1412 /* The remaining sections of the AFM are optional. This code will */
1413 /* look at the next keyword in the file to determine what section */
1414 /* is next, and then allocate the appropriate amount of storage */
1415 /* for the data (if the data is to be saved) and call the */
1416 /* appropriate parsing routine to parse the section. */
1418 while ((code
!= normalEOF
) && (code
!= earlyEOF
))
1420 keyword
= token(&aFile
,tokenlen
);
1421 if (keyword
== NULL
)
1422 /* Have reached an early and unexpected EOF. */
1423 /* Set flag and stop parsing */
1426 break; /* get out of loop */
1428 switch(recognize(keyword
,tokenlen
))
1434 case STARTTRACKKERN
:
1435 keyword
= token(&aFile
,tokenlen
);
1438 (*fi
)->numOfTracks
= atoi(keyword
);
1439 (*fi
)->tkd
= (TrackKernData
*)
1440 calloc((*fi
)->numOfTracks
, sizeof(TrackKernData
));
1441 if ((*fi
)->tkd
== NULL
)
1443 error
= storageProblem
;
1447 code
= parseTrackKernData(&aFile
, *fi
);
1449 case STARTKERNPAIRS
:
1450 keyword
= token(&aFile
,tokenlen
);
1453 (*fi
)->numOfPairs
= atoi(keyword
);
1454 (*fi
)->pkd
= (PairKernData
*)
1455 calloc((*fi
)->numOfPairs
, sizeof(PairKernData
));
1456 if ((*fi
)->pkd
== NULL
)
1458 error
= storageProblem
;
1462 code
= parsePairKernData(&aFile
, *fi
);
1464 case STARTCOMPOSITES
:
1465 keyword
= token(&aFile
,tokenlen
);
1468 (*fi
)->numOfComps
= atoi(keyword
);
1469 (*fi
)->ccd
= (CompCharData
*)
1470 calloc((*fi
)->numOfComps
, sizeof(CompCharData
));
1471 if ((*fi
)->ccd
== NULL
)
1473 error
= storageProblem
;
1477 code
= parseCompCharData(&aFile
, *fi
);
1479 case ENDFONTMETRICS
:
1491 if ((error
!= earlyEOF
) && (code
< 0)) error
= code
;
1495 if ((error
!= earlyEOF
) && (code
< 0)) error
= code
;
1502 freeFontInfo (FontInfo
*fi
)
1508 free (fi
->gfi
->afmVersion
);
1509 free (fi
->gfi
->fontName
);
1510 free (fi
->gfi
->fullName
);
1511 free (fi
->gfi
->familyName
);
1512 free (fi
->gfi
->weight
);
1513 free (fi
->gfi
->version
);
1514 free (fi
->gfi
->notice
);
1515 free (fi
->gfi
->encodingScheme
);
1523 for (i
= 0; i
< fi
->numOfChars
; i
++)
1526 free (fi
->cmi
[i
].name
);
1527 ligs
= fi
->cmi
[i
].ligs
;
1545 for ( i
= 0; i
< fi
->numOfPairs
; i
++)
1547 free (fi
->pkd
[i
].name1
);
1548 free (fi
->pkd
[i
].name2
);
1555 for (i
= 0; i
< fi
->numOfComps
; i
++)
1557 free (fi
->ccd
[i
].ccName
);
1558 for (j
= 0; j
< fi
->ccd
[i
].numOfPieces
; j
++)
1559 free (fi
->ccd
[i
].pieces
[j
].pccName
);
1561 free (fi
->ccd
[i
].pieces
);