merge the formfield patch from ooo-build
[ooovba.git] / vcl / unx / source / fontmanager / parseAFM.cxx
blobe6dc5d1f6deecd10e29a3bcc40455f31a7a42230
1 /*
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 $
49 * $Revision: 1.11 $
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"
58 /* parseAFM.c
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
67 * code.
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.
75 * History:
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
80 * - fixed typos
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
87 #include <stdio.h>
88 #include <string.h>
89 #include <stdlib.h>
90 #include <errno.h>
91 #include <sys/file.h>
92 #include <sys/stat.h>
93 #include <math.h>
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 */
102 /* in this module */
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)
110 namespace psp {
112 class FileInputStream
114 char* m_pMemory;
115 unsigned int m_nPos;
116 unsigned int m_nLen;
117 public:
118 FileInputStream( const char* pFilename );
119 ~FileInputStream();
121 int getChar() { return (m_nPos < m_nLen) ? int(m_pMemory[m_nPos++]) : -1; }
122 void ungetChar()
124 if( m_nPos > 0 )
125 m_nPos--;
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
131 { m_nPos = nPos; }
134 FileInputStream::FileInputStream( const char* pFilename ) :
135 m_pMemory( NULL ),
136 m_nPos( 0 ),
137 m_nLen( 0 )
139 struct stat aStat;
140 if( ! stat( pFilename, &aStat ) &&
141 S_ISREG( aStat.st_mode ) &&
142 aStat.st_size > 0
145 FILE* fp = fopen( pFilename, "r" );
146 if( fp )
148 m_pMemory = (char*)rtl_allocateMemory( aStat.st_size );
149 m_nLen = (unsigned int)fread( m_pMemory, 1, aStat.st_size, fp );
150 fclose( 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
178 // this gently
179 enum parseKey {
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,
189 NOPE
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 */
282 int ch, idx;
284 /* skip over white space */
285 // relies on EOF = -1
286 while( is_white_Array[ (ch = stream->getChar()) & 255 ] )
289 idx = 0;
290 while( ch != -1 && ! is_delimiter_Array[ ch & 255 ] && idx < MAX_NAME-1 )
292 ident[idx++] = ch;
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 */
299 ident[idx] = 0;
300 rLen = idx;
302 return(ident); /* returns pointer to the token */
304 } /* 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 */
317 int ch, idx;
319 while ((ch = stream->getChar()) == ' ' || ch == '\t' ) ;
321 idx = 0;
322 while (ch != -1 && ch != lineterm && ch != '\r' && idx < MAX_NAME-1 )
324 ident[idx++] = ch;
325 ch = stream->getChar();
326 } /* while */
328 stream->ungetChar();
329 ident[idx] = 0;
331 return(ident); /* returns pointer to the token */
333 } /* linetoken */
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;
352 } /* recognize */
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
365 * data structure.
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);
379 int error = ok;
380 register char *keyword;
381 int direction = -1;
382 int tokenlen;
384 while (cont)
386 keyword = token(fp, tokenlen);
388 if (keyword == NULL)
389 /* Have reached an early and unexpected EOF. */
390 /* Set flag and stop parsing */
392 error = earlyEOF;
393 break; /* get out of loop */
395 if (!save)
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:
401 cont = false;
402 break;
403 case ENDFONTMETRICS:
404 cont = false;
405 error = normalEOF;
406 break;
407 default:
408 break;
409 } /* switch */
410 else
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 );
418 break;
419 case COMMENT:
420 keyword = linetoken(fp);
421 break;
422 case FONTNAME:
423 keyword = token(fp, tokenlen);
424 gfi->fontName = strdup( keyword );
425 break;
426 case ENCODINGSCHEME:
427 keyword = token(fp, tokenlen);
428 gfi->encodingScheme = strdup( keyword );
429 break;
430 case FULLNAME:
431 keyword = linetoken(fp);
432 gfi->fullName = strdup( keyword );
433 break;
434 case FAMILYNAME:
435 keyword = linetoken(fp);
436 gfi->familyName = strdup( keyword );
437 break;
438 case WEIGHT:
439 keyword = token(fp, tokenlen);
440 gfi->weight = strdup( keyword );
441 break;
442 case ITALICANGLE:
443 keyword = token(fp,tokenlen);
444 gfi->italicAngle = StringToDouble( keyword );
445 break;
446 case ISFIXEDPITCH:
447 keyword = token(fp,tokenlen);
448 if (MATCH(keyword, False))
449 gfi->isFixedPitch = 0;
450 else
451 gfi->isFixedPitch = 1;
452 break;
453 case UNDERLINEPOSITION:
454 keyword = token(fp,tokenlen);
455 gfi->underlinePosition = atoi(keyword);
456 break;
457 case UNDERLINETHICKNESS:
458 keyword = token(fp,tokenlen);
459 gfi->underlineThickness = atoi(keyword);
460 break;
461 case VERSION:
462 keyword = token(fp,tokenlen);
463 gfi->version = strdup( keyword );
464 break;
465 case NOTICE:
466 keyword = linetoken(fp);
467 gfi->notice = strdup( keyword );
468 break;
469 case FONTBBOX:
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);
478 break;
479 case CAPHEIGHT:
480 keyword = token(fp,tokenlen);
481 gfi->capHeight = atoi(keyword);
482 break;
483 case XHEIGHT:
484 keyword = token(fp,tokenlen);
485 gfi->xHeight = atoi(keyword);
486 break;
487 case DESCENT:
488 keyword = token(fp,tokenlen);
489 gfi->descender = -atoi(keyword);
490 break;
491 case DESCENDER:
492 keyword = token(fp,tokenlen);
493 gfi->descender = atoi(keyword);
494 break;
495 case ASCENT:
496 case ASCENDER:
497 keyword = token(fp,tokenlen);
498 gfi->ascender = atoi(keyword);
499 break;
500 case STARTCHARMETRICS:
501 cont = false;
502 break;
503 case ENDFONTMETRICS:
504 cont = false;
505 error = normalEOF;
506 break;
507 case EM:
508 // skip one token
509 keyword = token(fp,tokenlen);
510 break;
511 case STARTDIRECTION:
512 keyword = token(fp,tokenlen);
513 direction = atoi(keyword);
514 break; /* ignore this for now */
515 case ENDDIRECTION:
516 break; /* ignore this for now */
517 case MAPPINGSCHEME:
518 keyword = token(fp,tokenlen);
519 break; /* ignore this for now */
520 case CHARACTERS:
521 keyword = token(fp,tokenlen);
522 break; /* ignore this for now */
523 case ISBASEFONT:
524 keyword = token(fp,tokenlen);
525 break; /* ignore this for now */
526 case CHARACTERSET:
527 keyword=token(fp,tokenlen); //ignore
528 break;
529 case STDHW:
530 keyword=token(fp,tokenlen); //ignore
531 break;
532 case STDVW:
533 keyword=token(fp,tokenlen); //ignore
534 break;
535 case CHARWIDTH:
536 keyword = token(fp,tokenlen);
537 if (direction == 0)
538 gfi->charwidth = atoi(keyword);
539 keyword = token(fp,tokenlen);
540 /* ignore y-width for now */
541 break;
542 case METRICSSETS:
543 keyword = token(fp,tokenlen);
544 break; /* ignore this for now */
545 case NOPE:
546 default:
547 error = parseError;
548 break;
549 } /* switch */
550 } /* while */
552 return(error);
554 } /* parseGlobals */
557 #if 0
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;
583 while (cont)
585 keyword = token(fp,tokenlen);
586 if (keyword == NULL)
588 error = earlyEOF;
589 break; /* get out of loop */
591 switch(recognize(keyword,tokenlen))
593 case COMMENT:
594 keyword = linetoken(fp);
595 break;
596 case CODE:
597 code = atoi(token(fp,tokenlen));
598 break;
599 case CODEHEX:
600 sscanf(token(fp,tokenlen),"<%x>", &code);
601 break;
602 case XWIDTH:
603 width = atoi(token(fp,tokenlen));
604 break;
605 case X0WIDTH:
606 (void) token(fp,tokenlen);
607 break;
608 case CHARNAME:
609 keyword = token(fp,tokenlen);
610 if (MATCH(keyword, Space))
612 cont = false;
613 found = true;
615 break;
616 case ENDCHARMETRICS:
617 cont = false;
618 break;
619 case ENDFONTMETRICS:
620 cont = false;
621 error = normalEOF;
622 break;
623 case NOPE:
624 default:
625 error = parseError;
626 break;
627 } /* switch */
628 } /* while */
630 if (!found)
631 width = 250;
633 for (i = 0; i < 256; ++i)
634 cwi[i] = width;
636 fp->seek(opos);
638 return(error);
640 } /* initializeArray */
641 #endif
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;
669 while (cont)
671 keyword = token(fp,tokenlen);
672 /* Have reached an early and unexpected EOF. */
673 /* Set flag and stop parsing */
674 if (keyword == NULL)
676 error = earlyEOF;
677 break; /* get out of loop */
679 if (!save)
680 /* get tokens until the end of the Char Metrics section without */
681 /* saving any of the data*/
682 switch (recognize(keyword,tokenlen))
684 case ENDCHARMETRICS:
685 cont = false;
686 break;
687 case ENDFONTMETRICS:
688 cont = false;
689 error = normalEOF;
690 break;
691 default:
692 break;
693 } /* switch */
694 else
695 /* otherwise parse entire char metrics section, saving */
696 /* only the char x-width info */
697 switch(recognize(keyword,tokenlen))
699 case COMMENT:
700 keyword = linetoken(fp);
701 break;
702 case CODE:
703 keyword = token(fp,tokenlen);
704 pos = atoi(keyword);
705 break;
706 case XYWIDTH:
707 /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
708 keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); /* eat values */
709 error = parseError;
710 break;
711 case CODEHEX:
712 keyword = token(fp,tokenlen);
713 sscanf(keyword, "<%x>", &pos);
714 break;
715 case X0WIDTH:
716 (void) token(fp,tokenlen);
717 break;
718 case XWIDTH:
719 keyword = token(fp,tokenlen);
720 if (pos >= 0) /* ignore unmapped chars */
721 cwi[pos] = atoi(keyword);
722 break;
723 case ENDCHARMETRICS:
724 cont = false;
725 break;
726 case ENDFONTMETRICS:
727 cont = false;
728 error = normalEOF;
729 break;
730 case CHARNAME: /* eat values (so doesn't cause parseError) */
731 keyword = token(fp,tokenlen);
732 break;
733 case CHARBBOX:
734 keyword = token(fp,tokenlen); keyword = token(fp,tokenlen);
735 keyword = token(fp,tokenlen); keyword = token(fp,tokenlen);
736 break;
737 case LIGATURE:
738 keyword = token(fp,tokenlen); keyword = token(fp,tokenlen);
739 break;
740 case VVECTOR:
741 keyword = token(fp,tokenlen);
742 keyword = token(fp,tokenlen);
743 break;
744 case NOPE:
745 default:
746 error = parseError;
747 break;
748 } /* switch */
749 } /* while */
751 return(error);
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
761 static int
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)
770 return ok;
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;
786 return ok;
789 static unsigned int
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;
796 return n_newcount;
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)
809 * into the array.
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;
823 while (cont)
825 keyword = token(fp,tokenlen);
826 if (keyword == NULL)
828 error = earlyEOF;
829 break; /* get out of loop */
831 switch(recognize(keyword,tokenlen))
833 case COMMENT:
834 keyword = linetoken(fp);
835 break;
836 case CODE:
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;
847 else temp++;
848 temp->code = atoi(token(fp,tokenlen));
849 if (fi->gfi && fi->gfi->charwidth)
850 temp->wx = fi->gfi->charwidth;
851 count++;
853 else
855 error = parseError;
856 cont = false;
858 break;
859 case CODEHEX:
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) {
868 if (firstTime)
869 firstTime = false;
870 else
871 temp++;
872 sscanf(token(fp,tokenlen),"<%x>", &temp->code);
873 if (fi->gfi && fi->gfi->charwidth)
874 temp->wx = fi->gfi->charwidth;
875 count++;
877 else {
878 error = parseError;
879 cont = false;
881 break;
882 case XYWIDTH:
883 temp->wx = atoi(token(fp,tokenlen));
884 temp->wy = atoi(token(fp,tokenlen));
885 break;
886 case X0WIDTH:
887 temp->wx = atoi(token(fp,tokenlen));
888 break;
889 case XWIDTH:
890 temp->wx = atoi(token(fp,tokenlen));
891 break;
892 case CHARNAME:
893 keyword = token(fp,tokenlen);
894 temp->name = (char *)strdup(keyword);
895 break;
896 case CHARBBOX:
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));
901 break;
902 case LIGATURE: {
903 Ligature **tail = &(temp->ligs);
904 Ligature *node = *tail;
906 if (*tail != NULL)
908 while (node->next != NULL)
909 node = node->next;
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);
918 break; }
919 case ENDCHARMETRICS:
920 cont = false;;
921 break;
922 case ENDFONTMETRICS:
923 cont = false;
924 error = normalEOF;
925 break;
926 case VVECTOR:
927 keyword = token(fp,tokenlen);
928 keyword = token(fp,tokenlen);
929 break;
930 case NOPE:
931 default:
932 error = parseError;
933 break;
934 } /* switch */
935 } /* while */
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))
942 error = parseError;
944 return(error);
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;
971 while (cont)
973 keyword = token(fp,tokenlen);
975 if (keyword == NULL)
977 error = earlyEOF;
978 break; /* get out of loop */
980 if (!save)
981 /* get tokens until the end of the Track Kerning Data */
982 /* section without saving any of the data */
983 switch(recognize(keyword,tokenlen))
985 case ENDTRACKKERN:
986 case ENDKERNDATA:
987 cont = false;
988 break;
989 case ENDFONTMETRICS:
990 cont = false;
991 error = normalEOF;
992 break;
993 default:
994 break;
995 } /* switch */
996 else
997 /* otherwise parse entire Track Kerning Data section, */
998 /* saving the data */
999 switch(recognize(keyword,tokenlen))
1001 case COMMENT:
1002 keyword = linetoken(fp);
1003 break;
1004 case TRACKKERN:
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);
1023 tcount++;
1025 else
1027 error = parseError;
1028 cont = false;
1030 break;
1031 case ENDTRACKKERN:
1032 case ENDKERNDATA:
1033 cont = false;
1034 break;
1035 case ENDFONTMETRICS:
1036 cont = false;
1037 error = normalEOF;
1038 break;
1039 case NOPE:
1040 default:
1041 error = parseError;
1042 break;
1043 } /* switch */
1044 } /* while */
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)
1051 error = parseError;
1053 return(error);
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;
1079 while (cont)
1081 keyword = token(fp,tokenlen);
1083 if (keyword == NULL)
1085 error = earlyEOF;
1086 break; /* get out of loop */
1088 if (!save)
1089 /* get tokens until the end of the Pair Kerning Data */
1090 /* section without saving any of the data */
1091 switch(recognize(keyword,tokenlen))
1093 case ENDKERNPAIRS:
1094 case ENDKERNDATA:
1095 cont = false;
1096 break;
1097 case ENDFONTMETRICS:
1098 cont = false;
1099 error = normalEOF;
1100 break;
1101 default:
1102 break;
1103 } /* switch */
1104 else
1105 /* otherwise parse entire Pair Kerning Data section, */
1106 /* saving the data */
1107 switch(recognize(keyword,tokenlen))
1109 case COMMENT:
1110 keyword = linetoken(fp);
1111 break;
1112 case KERNPAIR:
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);
1128 pcount++;
1130 else
1132 error = parseError;
1133 cont = false;
1135 break;
1136 case KERNPAIRXAMT:
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);
1150 pcount++;
1152 else
1154 error = parseError;
1155 cont = false;
1157 break;
1158 case ENDKERNPAIRS:
1159 case ENDKERNDATA:
1160 cont = false;
1161 break;
1162 case ENDFONTMETRICS:
1163 cont = false;
1164 error = normalEOF;
1165 break;
1166 case NOPE:
1167 default:
1168 error = parseError;
1169 break;
1170 } /* switch */
1171 } /* while */
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)
1178 error = parseError;
1180 return(error);
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;
1209 while (cont)
1211 keyword = token(fp,tokenlen);
1212 if (keyword == NULL)
1213 /* Have reached an early and unexpected EOF. */
1214 /* Set flag and stop parsing */
1216 error = earlyEOF;
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)
1226 error = parseError;
1227 break; /* get out of loop */
1229 if (!save)
1230 /* get tokens until the end of the Composite Character info */
1231 /* section without saving any of the data */
1232 switch(recognize(keyword,tokenlen))
1234 case ENDCOMPOSITES:
1235 cont = false;
1236 break;
1237 case ENDFONTMETRICS:
1238 cont = false;
1239 error = normalEOF;
1240 break;
1241 case COMMENT:
1242 case COMPCHAR:
1243 keyword = linetoken(fp);
1244 break;
1245 default:
1246 break;
1247 } /* switch */
1248 else
1249 /* otherwise parse entire Composite Character info section, */
1250 /* saving the data */
1251 switch(recognize(keyword,tokenlen))
1253 case COMMENT:
1254 keyword = linetoken(fp);
1255 break;
1256 case COMPCHAR:
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)
1266 error = parseError;
1267 pcount = 0;
1268 if (firstTime) firstTime = false;
1269 else pos++;
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));
1275 j = 0;
1276 ccount++;
1278 else
1280 error = parseError;
1281 cont = false;
1283 break;
1284 case COMPCHARPIECE:
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);
1293 pcount++;
1295 else
1296 error = parseError;
1297 break;
1298 case ENDCOMPOSITES:
1299 cont = false;
1300 break;
1301 case ENDFONTMETRICS:
1302 cont = false;
1303 error = normalEOF;
1304 break;
1305 case NOPE:
1306 default:
1307 error = parseError;
1308 break;
1309 } /* switch */
1310 } /* while */
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)
1317 error = parseError;
1319 return(error);
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 */
1356 int tokenlen;
1358 register char *keyword; /* used to store a token */
1361 (*fi) = (FontInfo *) calloc(1, sizeof(FontInfo));
1362 if ((*fi) == NULL) {error = storageProblem; return(error);}
1364 if (flags & P_G)
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);
1394 else
1396 if (flags & P_W)
1398 (*fi)->cwi = (int *) calloc(256, sizeof(int));
1399 if ((*fi)->cwi == NULL)
1401 error = storageProblem;
1402 return(error);
1405 /* parse section regardless */
1406 code = parseCharWidths(&aFile, (*fi)->cwi);
1407 } /* else */
1408 } /* if */
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 */
1425 code = earlyEOF;
1426 break; /* get out of loop */
1428 switch(recognize(keyword,tokenlen))
1430 case STARTKERNDATA:
1431 break;
1432 case ENDKERNDATA:
1433 break;
1434 case STARTTRACKKERN:
1435 keyword = token(&aFile,tokenlen);
1436 if (flags & P_T)
1438 (*fi)->numOfTracks = atoi(keyword);
1439 (*fi)->tkd = (TrackKernData *)
1440 calloc((*fi)->numOfTracks, sizeof(TrackKernData));
1441 if ((*fi)->tkd == NULL)
1443 error = storageProblem;
1444 return(error);
1446 } /* if */
1447 code = parseTrackKernData(&aFile, *fi);
1448 break;
1449 case STARTKERNPAIRS:
1450 keyword = token(&aFile,tokenlen);
1451 if (flags & P_P)
1453 (*fi)->numOfPairs = atoi(keyword);
1454 (*fi)->pkd = (PairKernData *)
1455 calloc((*fi)->numOfPairs, sizeof(PairKernData));
1456 if ((*fi)->pkd == NULL)
1458 error = storageProblem;
1459 return(error);
1461 } /* if */
1462 code = parsePairKernData(&aFile, *fi);
1463 break;
1464 case STARTCOMPOSITES:
1465 keyword = token(&aFile,tokenlen);
1466 if (flags & P_C)
1468 (*fi)->numOfComps = atoi(keyword);
1469 (*fi)->ccd = (CompCharData *)
1470 calloc((*fi)->numOfComps, sizeof(CompCharData));
1471 if ((*fi)->ccd == NULL)
1473 error = storageProblem;
1474 return(error);
1476 } /* if */
1477 code = parseCompCharData(&aFile, *fi);
1478 break;
1479 case ENDFONTMETRICS:
1480 code = normalEOF;
1481 break;
1482 case COMMENT:
1483 linetoken(&aFile);
1484 break;
1485 case NOPE:
1486 default:
1487 code = parseError;
1488 break;
1489 } /* switch */
1491 if ((error != earlyEOF) && (code < 0)) error = code;
1493 } /* while */
1495 if ((error != earlyEOF) && (code < 0)) error = code;
1497 return(error);
1499 } /* parseFile */
1501 void
1502 freeFontInfo (FontInfo *fi)
1504 int i, j;
1506 if (fi->gfi)
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);
1516 free (fi->gfi);
1519 free (fi->cwi);
1521 if (fi->cmi)
1523 for (i = 0; i < fi->numOfChars; i++)
1525 Ligature *ligs;
1526 free (fi->cmi[i].name);
1527 ligs = fi->cmi[i].ligs;
1528 while (ligs)
1530 Ligature *tmp;
1531 tmp = ligs;
1532 ligs = ligs->next;
1533 free (tmp->succ);
1534 free (tmp->lig);
1535 free (tmp);
1538 free (fi->cmi);
1541 free (fi->tkd);
1543 if (fi->pkd)
1545 for ( i = 0; i < fi->numOfPairs; i++)
1547 free (fi->pkd[i].name1);
1548 free (fi->pkd[i].name2);
1550 free (fi->pkd);
1553 if (fi->ccd)
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);
1563 free (fi->ccd);
1566 free (fi);
1569 } // namspace