lilypond-1.4.1
[lilypond.git] / flower / parse-afm.cc
blob3d205f00894dfe4a3c223c5ae24f77c320f80593
1 /* Our mods:
3 1. FontInfo has become AFM_AFM_Font_info to avoid namespace collisions.
4 2. Version is a linetoken because Bitstream Charter has a space in the version.
5 3. Added some necessary #include headers.
6 4. Added AFM_ prefixes to error codes.
7 5. Made it recognize both '\n' and '\r' as line terminators. Sheesh!
8 6. Stopped buffer overflows in token and linetoken.
10 Raph Levien <raph@acm.org> writing on 4 Oct 1998, updating 21 Oct 1998
13 1. parseFileFree function.
14 2. Leak fix in parseFile.
16 Morten Welinder <terra@diku.dk> September 1999.
21 * (C) 1988, 1989, 1990 by Adobe Systems Incorporated. All rights reserved.
23 * This file may be freely copied and redistributed as long as:
24 * 1) This entire notice continues to be included in the file,
25 * 2) If the file has been modified in any way, a notice of such
26 * modification is conspicuously indicated.
28 * PostScript, Display PostScript, and Adobe are registered trademarks of
29 * Adobe Systems Incorporated.
31 * ************************************************************************
32 * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT
33 * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS
34 * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR
35 * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY
36 * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION,
37 * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
38 * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
39 * ************************************************************************
42 /* parseAFM.c
44 * This file is used in conjuction with the parseAFM.h header file.
45 * This file contains several procedures that are used to parse AFM
46 * files. It is intended to work with an application program that needs
47 * font metric information. The program can be used as is by making a
48 * procedure call to "parseFile" (passing in the expected parameters)
49 * and having it fill in a data structure with the data from the
50 * AFM file, or an application developer may wish to customize this
51 * code.
53 * There is also a file, parseAFMclient.c, that is a sample application
54 * showing how to call the "parseFile" procedure and how to use the data
55 * after "parseFile" has returned.
57 * Please read the comments in parseAFM.h and parseAFMclient.c.
59 * History:
60 * original: DSM Thu Oct 20 17:39:59 PDT 1988
61 * modified: DSM Mon Jul 3 14:17:50 PDT 1989
62 * - added 'storageProblem' return code
63 * - fixed bug of not allocating extra byte for string duplication
64 * - fixed typos
65 * modified: DSM Tue Apr 3 11:18:34 PDT 1990
66 * - added free (ident) at end of parseFile routine
67 * modified: DSM Tue Jun 19 10:16:29 PDT 1990
68 * - changed (width == 250) to (width = 250) in initializeArray
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <errno.h>
74 #include <sys/file.h>
75 #include <math.h>
76 #include <string.h>
77 #include "parse-afm.hh"
79 #define lineterm EOL /* line terminating character */
80 #define lineterm_alt '\r' /* alternative line terminating character */
81 #define normalEOF 1 /* return code from parsing routines used only */
82 /* in this module */
83 #define Space "space" /* used in string comparison to look for the width */
84 /* of the space character to init the widths array */
85 #define False "false" /* used in string comparison to check the value of */
86 /* boolean keys (e.g. IsFixedPitch) */
88 #define MATCH(A,B) (strncmp ((A), (B), MAX_NAME) == 0)
92 /*************************** GLOBALS ***********************/
94 static char *ident = NULL; /* storage buffer for keywords */
97 /* "shorts" for fast case statement
98 * The values of each of these enumerated items correspond to an entry in the
99 * table of strings defined below. Therefore, if you add a new string as
100 * new keyword into the keyStrings table, you must also add a corresponding
101 * parseKey AND it MUST be in the same position!
103 * IMPORTANT: since the sorting algorithm is a binary search, the strings of
104 * keywords must be placed in lexicographical order, below. [Therefore, the
105 * enumerated items are not necessarily in lexicographical order, depending
106 * on the name chosen. BUT, they must be placed in the same position as the
107 * corresponding key string.] The NOPE shall remain in the last position,
108 * since it does not correspond to any key string, and it is used in the
109 * "recognize" procedure to calculate how many possible keys there are.
112 enum parseKey {
113 ASCENDER, CHARBBOX, CODE, COMPCHAR, CAPHEIGHT, COMMENT,
114 DESCENDER, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES,
115 ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN,
116 FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME, ISFIXEDPITCH,
117 ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, CHARNAME,
118 NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES,
119 STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS,
120 STARTTRACKKERN, TRACKKERN, UNDERLINEPOSITION,
121 UNDERLINETHICKNESS, VERSION, XYWIDTH, XWIDTH, WEIGHT, XHEIGHT,
122 NOPE };
124 /* keywords for the system:
125 * This a table of all of the current strings that are vaild AFM keys.
126 * Each entry can be referenced by the appropriate parseKey value (an
127 * enumerated data type defined above). If you add a new keyword here,
128 * a corresponding parseKey MUST be added to the enumerated data type
129 * defined above, AND it MUST be added in the same position as the
130 * string is in this table.
132 * IMPORTANT: since the sorting algorithm is a binary search, the keywords
133 * must be placed in lexicographical order. And, NULL should remain at the
134 * end.
137 static char *keyStrings[] = {
138 "Ascender", "B", "C", "CC", "CapHeight", "Comment",
139 "Descender", "EncodingScheme", "EndCharMetrics", "EndComposites",
140 "EndFontMetrics", "EndKernData", "EndKernPairs", "EndTrackKern",
141 "FamilyName", "FontBBox", "FontName", "FullName", "IsFixedPitch",
142 "ItalicAngle", "KP", "KPX", "L", "N",
143 "Notice", "PCC", "StartCharMetrics", "StartComposites",
144 "StartFontMetrics", "StartKernData", "StartKernPairs",
145 "StartTrackKern", "TrackKern", "UnderlinePosition",
146 "UnderlineThickness", "Version", "W", "WX", "Weight", "XHeight",
147 NULL };
149 /*************************** PARSING ROUTINES **************/
151 /*************************** token *************************/
153 /* A "AFM File Conventions" tokenizer. That means that it will
154 * return the next token delimited by white space. See also
155 * the `linetoken' routine, which does a similar thing but
156 * reads all tokens until the next end-of-line.
159 static char *token (FILE *stream)
161 int ch, idx;
163 /* skip over white space */
164 while ((ch = fgetc (stream)) == ' ' || ch == lineterm ||
165 ch == lineterm_alt ||
166 ch == ',' || ch == '\t' || ch == ';');
168 idx = 0;
169 while (idx < MAX_NAME - 1 &&
170 ch != EOF && ch != ' ' && ch != lineterm && ch != lineterm_alt
171 && ch != '\t' && ch != ':' && ch != ';')
173 ident[idx++] = ch;
174 ch = fgetc (stream);
175 } /* while */
177 if (ch == EOF && idx < 1) return ((char *)NULL);
178 if (idx >= 1 && ch != ':' ) ungetc (ch, stream);
179 if (idx < 1 ) ident[idx++] = ch; /* single-character token */
180 ident[idx] = 0;
182 return (ident); /* returns pointer to the token */
184 } /* token */
187 /*************************** linetoken *************************/
189 /* "linetoken" will get read all tokens until the EOL character from
190 * the given stream. This is used to get any arguments that can be
191 * more than one word (like Comment lines and FullName).
194 static char *linetoken (FILE *stream)
196 int ch, idx;
198 while ((ch = fgetc (stream)) == ' ' || ch == '\t' );
200 idx = 0;
201 while (idx < MAX_NAME - 1 &&
202 ch != EOF && ch != lineterm && ch != lineterm_alt)
204 ident[idx++] = ch;
205 ch = fgetc (stream);
206 } /* while */
208 ungetc (ch, stream);
209 ident[idx] = 0;
211 return (ident); /* returns pointer to the token */
213 } /* linetoken */
216 /*************************** recognize *************************/
218 /* This function tries to match a string to a known list of
219 * valid AFM entries (check the keyStrings array above).
220 * "ident" contains everything from white space through the
221 * next space, tab, or ":" character.
223 * The algorithm is a standard Knuth binary search.
226 static enum parseKey recognize ( register char *ident)
228 int lower = 0,
229 upper = (int) NOPE,
230 midpoint = 0,
231 cmpvalue = 0;
232 BOOL found = FALSE;
234 while ((upper >= lower) && !found)
236 midpoint = (lower + upper)/2;
237 if (keyStrings[midpoint] == NULL) break;
238 cmpvalue = strncmp (ident, keyStrings[midpoint], MAX_NAME);
239 if (cmpvalue == 0) found = TRUE;
240 else if (cmpvalue < 0) upper = midpoint - 1;
241 else lower = midpoint + 1;
242 } /* while */
244 if (found) return (enum parseKey) midpoint;
245 else return NOPE;
247 } /* recognize */
250 /************************* parseGlobals *****************************/
252 /* This function is called by "parseFile". It will parse the AFM File
253 * up to the "StartCharMetrics" keyword, which essentially marks the
254 * end of the Global Font Information and the beginning of the character
255 * metrics information.
257 * If the caller of "parseFile" specified that it wanted the Global
258 * Font Information (as defined by the "AFM File Specification"
259 * document), then that information will be stored in the returned
260 * data structure.
262 * Any Global Font Information entries that are not found in a
263 * given file, will have the usual default initialization value
264 * for its type (i.e. entries of type int will be 0, etc).
266 * This function returns an error code specifying whether there was
267 * a premature EOF or a parsing error. This return value is used by
268 * parseFile to determine if there is more file to parse.
271 static BOOL parseGlobals (FILE *fp, register AFM_GlobalFontInfo *gfi)
273 BOOL cont = TRUE, save = (gfi != NULL);
274 int error = AFM_ok;
275 register char *keyword;
277 while (cont)
279 keyword = token (fp);
281 if (keyword == NULL)
282 /* Have reached an early and unexpected EOF. */
283 /* Set flag and stop parsing */
285 error = AFM_earlyEOF;
286 break; /* get out of loop */
288 if (!save)
289 /* get tokens until the end of the Global Font info section */
290 /* without saving any of the data */
291 switch (recognize (keyword))
293 case STARTCHARMETRICS:
294 cont = FALSE;
295 break;
296 case ENDFONTMETRICS:
297 cont = FALSE;
298 error = normalEOF;
299 break;
300 default:
301 break;
302 } /* switch */
303 else
304 /* otherwise parse entire global font info section, */
305 /* saving the data */
306 switch (recognize (keyword))
308 case STARTFONTMETRICS:
309 keyword = token (fp);
310 gfi->afmVersion = (char *) malloc (strlen (keyword) + 1);
311 strcpy (gfi->afmVersion, keyword);
312 break;
313 case COMMENT:
314 keyword = linetoken (fp);
315 break;
316 case FONTNAME:
317 keyword = token (fp);
318 gfi->fontName = (char *) malloc (strlen (keyword) + 1);
319 strcpy (gfi->fontName, keyword);
320 break;
321 case ENCODINGSCHEME:
322 keyword = token (fp);
323 gfi->encodingScheme = (char *)
324 malloc (strlen (keyword) + 1);
325 strcpy (gfi->encodingScheme, keyword);
326 break;
327 case FULLNAME:
328 keyword = linetoken (fp);
329 gfi->fullName = (char *) malloc (strlen (keyword) + 1);
330 strcpy (gfi->fullName, keyword);
331 break;
332 case FAMILYNAME:
333 keyword = linetoken (fp);
334 gfi->familyName = (char *) malloc (strlen (keyword) + 1);
335 strcpy (gfi->familyName, keyword);
336 break;
337 case WEIGHT:
338 keyword = token (fp);
339 gfi->weight = (char *) malloc (strlen (keyword) + 1);
340 strcpy (gfi->weight, keyword);
341 break;
342 case ITALICANGLE:
343 keyword = token (fp);
344 gfi->italicAngle = atof (keyword);
345 if (errno == ERANGE) error = AFM_parseError;
346 break;
347 case ISFIXEDPITCH:
348 keyword = token (fp);
349 if (MATCH (keyword, False))
350 gfi->isFixedPitch = 0;
351 else
352 gfi->isFixedPitch = 1;
353 break;
354 case UNDERLINEPOSITION:
355 keyword = token (fp);
356 gfi->underlinePosition = atoi (keyword);
357 break;
358 case UNDERLINETHICKNESS:
359 keyword = token (fp);
360 gfi->underlineThickness = atoi (keyword);
361 break;
362 case VERSION:
363 keyword = linetoken (fp);
364 gfi->version = (char *) malloc (strlen (keyword) + 1);
365 strcpy (gfi->version, keyword);
366 break;
367 case NOTICE:
368 keyword = linetoken (fp);
369 gfi->notice = (char *) malloc (strlen (keyword) + 1);
370 strcpy (gfi->notice, keyword);
371 break;
372 case FONTBBOX:
373 keyword = token (fp);
374 gfi->fontBBox.llx = atoi (keyword);
375 keyword = token (fp);
376 gfi->fontBBox.lly = atoi (keyword);
377 keyword = token (fp);
378 gfi->fontBBox.urx = atoi (keyword);
379 keyword = token (fp);
380 gfi->fontBBox.ury = atoi (keyword);
381 break;
382 case CAPHEIGHT:
383 keyword = token (fp);
384 gfi->capHeight = atoi (keyword);
385 break;
386 case XHEIGHT:
387 keyword = token (fp);
388 gfi->xHeight = atoi (keyword);
389 break;
390 case DESCENDER:
391 keyword = token (fp);
392 gfi->descender = atoi (keyword);
393 break;
394 case ASCENDER:
395 keyword = token (fp);
396 gfi->ascender = atoi (keyword);
397 break;
398 case STARTCHARMETRICS:
399 cont = FALSE;
400 break;
401 case ENDFONTMETRICS:
402 cont = FALSE;
403 error = normalEOF;
404 break;
405 case NOPE:
406 default:
407 error = AFM_parseError;
408 break;
409 } /* switch */
410 } /* while */
412 return (error);
414 } /* parseGlobals */
418 /************************* initializeArray ************************/
420 /* Unmapped character codes are (at Adobe Systems) assigned the
421 * width of the space character (if one exists) else they get the
422 * value of 250 ems. This function initializes all entries in the
423 * char widths array to have this value. Then any mapped character
424 * codes will be replaced with the width of the appropriate character
425 * when parsing the character metric section.
427 * This function parses the Character Metrics Section looking
428 * for a space character (by comparing character names). If found,
429 * the width of the space character will be used to initialize the
430 * values in the array of character widths.
432 * Before returning, the position of the read/write pointer of the
433 * file is reset to be where it was upon entering this function.
436 static int initializeArray (FILE *fp, register int *cwi)
438 BOOL cont = TRUE, found = FALSE;
439 long opos = ftell (fp);
440 int code = 0, width = 0, i = 0, error = 0;
441 register char *keyword;
443 while (cont)
445 keyword = token (fp);
446 if (keyword == NULL)
448 error = AFM_earlyEOF;
449 break; /* get out of loop */
451 switch (recognize (keyword))
453 case COMMENT:
454 keyword = linetoken (fp);
455 break;
456 case CODE:
457 code = atoi (token (fp));
458 break;
459 case XWIDTH:
460 width = atoi (token (fp));
461 break;
462 case CHARNAME:
463 keyword = token (fp);
464 if (MATCH (keyword, Space))
466 cont = FALSE;
467 found = TRUE;
469 break;
470 case ENDCHARMETRICS:
471 cont = FALSE;
472 break;
473 case ENDFONTMETRICS:
474 cont = FALSE;
475 error = normalEOF;
476 break;
477 case NOPE:
478 default:
479 error = AFM_parseError;
480 break;
481 } /* switch */
482 } /* while */
484 if (!found)
485 width = 250;
487 for (i = 0; i < 256; ++i)
488 cwi[i] = width;
490 fseek (fp, opos, 0);
492 return (error);
494 } /* initializeArray */
497 /************************* parseCharWidths **************************/
499 /* This function is called by "parseFile". It will parse the AFM File
500 * up to the "EndCharMetrics" keyword. It will save the character
501 * width info (as opposed to all of the character metric information)
502 * if requested by the caller of parseFile. Otherwise, it will just
503 * parse through the section without saving any information.
505 * If data is to be saved, parseCharWidths is passed in a pointer
506 * to an array of widths that has already been initialized by the
507 * standard value for unmapped character codes. This function parses
508 * the Character Metrics section only storing the width information
509 * for the encoded characters into the array using the character code
510 * as the index into that array.
512 * This function returns an error code specifying whether there was
513 * a premature EOF or a parsing error. This return value is used by
514 * parseFile to determine if there is more file to parse.
517 static int parseCharWidths (FILE *fp, register int *cwi)
519 BOOL cont = TRUE, save = (cwi != NULL);
520 int pos = 0, error = AFM_ok;
521 register char *keyword;
523 while (cont)
525 keyword = token (fp);
526 /* Have reached an early and unexpected EOF. */
527 /* Set flag and stop parsing */
528 if (keyword == NULL)
530 error = AFM_earlyEOF;
531 break; /* get out of loop */
533 if (!save)
534 /* get tokens until the end of the Char Metrics section without */
535 /* saving any of the data*/
536 switch (recognize (keyword))
538 case ENDCHARMETRICS:
539 cont = FALSE;
540 break;
541 case ENDFONTMETRICS:
542 cont = FALSE;
543 error = normalEOF;
544 break;
545 default:
546 break;
547 } /* switch */
548 else
549 /* otherwise parse entire char metrics section, saving */
550 /* only the char x-width info */
551 switch (recognize (keyword))
553 case COMMENT:
554 keyword = linetoken (fp);
555 break;
556 case CODE:
557 keyword = token (fp);
558 pos = atoi (keyword);
559 break;
560 case XYWIDTH:
561 /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
562 keyword = token (fp); keyword = token (fp); /* eat values */
563 error = AFM_parseError;
564 break;
565 case XWIDTH:
566 keyword = token (fp);
567 if (pos >= 0) /* ignore unmapped chars */
568 cwi[pos] = atoi (keyword);
569 break;
570 case ENDCHARMETRICS:
571 cont = FALSE;
572 break;
573 case ENDFONTMETRICS:
574 cont = FALSE;
575 error = normalEOF;
576 break;
577 case CHARNAME: /* eat values (so doesn't cause AFM_parseError) */
578 keyword = token (fp);
579 break;
580 case CHARBBOX:
581 keyword = token (fp); keyword = token (fp);
582 keyword = token (fp); keyword = token (fp);
583 break;
584 case LIGATURE:
585 keyword = token (fp); keyword = token (fp);
586 break;
587 case NOPE:
588 default:
589 error = AFM_parseError;
590 break;
591 } /* switch */
592 } /* while */
594 return (error);
596 } /* parseCharWidths */
599 /************************* parseCharMetrics ************************/
601 /* This function is called by parseFile if the caller of parseFile
602 * requested that all character metric information be saved
603 * (as opposed to only the character width information).
605 * parseCharMetrics is passed in a pointer to an array of records
606 * to hold information on a per character basis. This function
607 * parses the Character Metrics section storing all character
608 * metric information for the ALL characters (mapped and unmapped)
609 * into the array.
611 * This function returns an error code specifying whether there was
612 * a premature EOF or a parsing error. This return value is used by
613 * parseFile to determine if there is more file to parse.
616 static int parseCharMetrics (FILE *fp, register AFM_Font_info *fi)
618 BOOL cont = TRUE, firstTime = TRUE;
619 int error = AFM_ok, count = 0;
620 register AFM_CharMetricInfo *temp = fi->cmi;
621 register char *keyword;
623 while (cont)
625 keyword = token (fp);
626 if (keyword == NULL)
628 error = AFM_earlyEOF;
629 break; /* get out of loop */
631 switch (recognize (keyword))
633 case COMMENT:
634 keyword = linetoken (fp);
635 break;
636 case CODE:
637 if (count < fi->numOfChars)
639 if (firstTime) firstTime = FALSE;
640 else temp++;
641 temp->code = atoi (token (fp));
642 count++;
644 else
646 error = AFM_parseError;
647 cont = FALSE;
649 break;
650 case XYWIDTH:
651 temp->wx = atoi (token (fp));
652 temp->wy = atoi (token (fp));
653 break;
654 case XWIDTH:
655 temp->wx = atoi (token (fp));
656 break;
657 case CHARNAME:
658 keyword = token (fp);
659 temp->name = (char *) malloc (strlen (keyword) + 1);
660 strcpy (temp->name, keyword);
661 break;
662 case CHARBBOX:
663 temp->charBBox.llx = atoi (token (fp));
664 temp->charBBox.lly = atoi (token (fp));
665 temp->charBBox.urx = atoi (token (fp));
666 temp->charBBox.ury = atoi (token (fp));
667 break;
668 case LIGATURE: {
669 AFM_Ligature **tail = & (temp->ligs);
670 AFM_Ligature *node = *tail;
672 if (*tail != NULL)
674 while (node->next != NULL)
675 node = node->next;
676 tail = & (node->next);
679 *tail = (AFM_Ligature *) calloc (1, sizeof (AFM_Ligature));
680 keyword = token (fp);
681 (*tail)->succ = (char *) malloc (strlen (keyword) + 1);
682 strcpy ((*tail)->succ, keyword);
683 keyword = token (fp);
684 (*tail)->lig = (char *) malloc (strlen (keyword) + 1);
685 strcpy ((*tail)->lig, keyword);
686 break; }
687 case ENDCHARMETRICS:
688 cont = FALSE;;
689 break;
690 case ENDFONTMETRICS:
691 cont = FALSE;
692 error = normalEOF;
693 break;
694 case NOPE:
695 default:
696 error = AFM_parseError;
697 break;
698 } /* switch */
699 } /* while */
701 if ((error == AFM_ok) && (count != fi->numOfChars))
702 error = AFM_parseError;
704 return (error);
706 } /* parseCharMetrics */
710 /************************* parseAFM_TrackKernData ***********************/
712 /* This function is called by "parseFile". It will parse the AFM File
713 * up to the "EndTrackKern" or "EndKernData" keywords. It will save the
714 * track kerning data if requested by the caller of parseFile.
716 * parseAFM_TrackKernData is passed in a pointer to the FontInfo record.
717 * If data is to be saved, the FontInfo record will already contain
718 * a valid pointer to storage for the track kerning data.
720 * This function returns an error code specifying whether there was
721 * a premature EOF or a parsing error. This return value is used by
722 * parseFile to determine if there is more file to parse.
725 static int parseAFM_TrackKernData (FILE *fp, register AFM_Font_info *fi)
727 BOOL cont = TRUE, save = (fi->tkd != NULL);
728 int pos = 0, error = AFM_ok, tcount = 0;
729 register char *keyword;
731 while (cont)
733 keyword = token (fp);
735 if (keyword == NULL)
737 error = AFM_earlyEOF;
738 break; /* get out of loop */
740 if (!save)
741 /* get tokens until the end of the Track Kerning Data */
742 /* section without saving any of the data */
743 switch (recognize (keyword))
745 case ENDTRACKKERN:
746 case ENDKERNDATA:
747 cont = FALSE;
748 break;
749 case ENDFONTMETRICS:
750 cont = FALSE;
751 error = normalEOF;
752 break;
753 default:
754 break;
755 } /* switch */
756 else
757 /* otherwise parse entire Track Kerning Data section, */
758 /* saving the data */
759 switch (recognize (keyword))
761 case COMMENT:
762 keyword = linetoken (fp);
763 break;
764 case TRACKKERN:
765 if (tcount < fi->numOfTracks)
767 keyword = token (fp);
768 fi->tkd[pos].degree = atoi (keyword);
769 keyword = token (fp);
770 fi->tkd[pos].minPtSize = atof (keyword);
771 if (errno == ERANGE) error = AFM_parseError;
772 keyword = token (fp);
773 fi->tkd[pos].minKernAmt = atof (keyword);
774 if (errno == ERANGE) error = AFM_parseError;
775 keyword = token (fp);
776 fi->tkd[pos].maxPtSize = atof (keyword);
777 if (errno == ERANGE) error = AFM_parseError;
778 keyword = token (fp);
779 fi->tkd[pos++].maxKernAmt = atof (keyword);
780 if (errno == ERANGE) error = AFM_parseError;
781 tcount++;
783 else
785 error = AFM_parseError;
786 cont = FALSE;
788 break;
789 case ENDTRACKKERN:
790 case ENDKERNDATA:
791 cont = FALSE;
792 break;
793 case ENDFONTMETRICS:
794 cont = FALSE;
795 error = normalEOF;
796 break;
797 case NOPE:
798 default:
799 error = AFM_parseError;
800 break;
801 } /* switch */
802 } /* while */
804 if (error == AFM_ok && tcount != fi->numOfTracks)
805 error = AFM_parseError;
807 return (error);
809 } /* parseAFM_TrackKernData */
812 /************************* parseAFM_PairKernData ************************/
814 /* This function is called by "parseFile". It will parse the AFM File
815 * up to the "EndKernPairs" or "EndKernData" keywords. It will save
816 * the pair kerning data if requested by the caller of parseFile.
818 * parseAFM_PairKernData is passed in a pointer to the FontInfo record.
819 * If data is to be saved, the FontInfo record will already contain
820 * a valid pointer to storage for the pair kerning data.
822 * This function returns an error code specifying whether there was
823 * a premature EOF or a parsing error. This return value is used by
824 * parseFile to determine if there is more file to parse.
827 static int parseAFM_PairKernData (FILE *fp, register AFM_Font_info *fi)
829 BOOL cont = TRUE, save = (fi->pkd != NULL);
830 int pos = 0, error = AFM_ok, pcount = 0;
831 register char *keyword;
833 while (cont)
835 keyword = token (fp);
837 if (keyword == NULL)
839 error = AFM_earlyEOF;
840 break; /* get out of loop */
842 if (!save)
843 /* get tokens until the end of the Pair Kerning Data */
844 /* section without saving any of the data */
845 switch (recognize (keyword))
847 case ENDKERNPAIRS:
848 case ENDKERNDATA:
849 cont = FALSE;
850 break;
851 case ENDFONTMETRICS:
852 cont = FALSE;
853 error = normalEOF;
854 break;
855 default:
856 break;
857 } /* switch */
858 else
859 /* otherwise parse entire Pair Kerning Data section, */
860 /* saving the data */
861 switch (recognize (keyword))
863 case COMMENT:
864 keyword = linetoken (fp);
865 break;
866 case KERNPAIR:
867 if (pcount < fi->numOfPairs)
869 keyword = token (fp);
870 fi->pkd[pos].name1 = (char *)
871 malloc (strlen (keyword) + 1);
872 strcpy (fi->pkd[pos].name1, keyword);
873 keyword = token (fp);
874 fi->pkd[pos].name2 = (char *)
875 malloc (strlen (keyword) + 1);
876 strcpy (fi->pkd[pos].name2, keyword);
877 keyword = token (fp);
878 fi->pkd[pos].xamt = atoi (keyword);
879 keyword = token (fp);
880 fi->pkd[pos++].yamt = atoi (keyword);
881 pcount++;
883 else
885 error = AFM_parseError;
886 cont = FALSE;
888 break;
889 case KERNPAIRXAMT:
890 if (pcount < fi->numOfPairs)
892 keyword = token (fp);
893 fi->pkd[pos].name1 = (char *)
894 malloc (strlen (keyword) + 1);
895 strcpy (fi->pkd[pos].name1, keyword);
896 keyword = token (fp);
897 fi->pkd[pos].name2 = (char *)
898 malloc (strlen (keyword) + 1);
899 strcpy (fi->pkd[pos].name2, keyword);
900 keyword = token (fp);
901 fi->pkd[pos++].xamt = atoi (keyword);
902 pcount++;
904 else
906 error = AFM_parseError;
907 cont = FALSE;
909 break;
910 case ENDKERNPAIRS:
911 case ENDKERNDATA:
912 cont = FALSE;
913 break;
914 case ENDFONTMETRICS:
915 cont = FALSE;
916 error = normalEOF;
917 break;
918 case NOPE:
919 default:
920 error = AFM_parseError;
921 break;
922 } /* switch */
923 } /* while */
925 if (error == AFM_ok && pcount != fi->numOfPairs)
926 error = AFM_parseError;
928 return (error);
930 } /* parseAFM_PairKernData */
933 /************************* parseAFM_CompCharData **************************/
935 /* This function is called by "parseFile". It will parse the AFM File
936 * up to the "EndComposites" keyword. It will save the composite
937 * character data if requested by the caller of parseFile.
939 * parseAFM_CompCharData is passed in a pointer to the FontInfo record, and
940 * a boolean representing if the data should be saved.
942 * This function will create the appropriate amount of storage for
943 * the composite character data and store a pointer to the storage
944 * in the FontInfo record.
946 * This function returns an error code specifying whether there was
947 * a premature EOF or a parsing error. This return value is used by
948 * parseFile to determine if there is more file to parse.
951 static int parseAFM_CompCharData (FILE *fp, register AFM_Font_info *fi)
953 BOOL cont = TRUE, firstTime = TRUE, save = (fi->ccd != NULL);
954 int pos = 0, j = 0, error = AFM_ok, ccount = 0, pcount = 0;
955 register char *keyword;
957 while (cont)
959 keyword = token (fp);
960 if (keyword == NULL)
961 /* Have reached an early and unexpected EOF. */
962 /* Set flag and stop parsing */
964 error = AFM_earlyEOF;
965 break; /* get out of loop */
967 if (ccount > fi->numOfComps)
969 error = AFM_parseError;
970 break; /* get out of loop */
972 if (!save)
973 /* get tokens until the end of the Composite Character info */
974 /* section without saving any of the data */
975 switch (recognize (keyword))
977 case ENDCOMPOSITES:
978 cont = FALSE;
979 break;
980 case ENDFONTMETRICS:
981 cont = FALSE;
982 error = normalEOF;
983 break;
984 default:
985 break;
986 } /* switch */
987 else
988 /* otherwise parse entire Composite Character info section, */
989 /* saving the data */
990 switch (recognize (keyword))
992 case COMMENT:
993 keyword = linetoken (fp);
994 break;
995 case COMPCHAR:
996 if (ccount < fi->numOfComps)
998 keyword = token (fp);
999 if (pcount != fi->ccd[pos].numOfPieces)
1000 error = AFM_parseError;
1001 pcount = 0;
1002 if (firstTime) firstTime = FALSE;
1003 else pos++;
1004 fi->ccd[pos].ccName = (char *)
1005 malloc (strlen (keyword) + 1);
1006 strcpy (fi->ccd[pos].ccName, keyword);
1007 keyword = token (fp);
1008 fi->ccd[pos].numOfPieces = atoi (keyword);
1009 fi->ccd[pos].pieces = (AFM_Pcc *)
1010 calloc (fi->ccd[pos].numOfPieces, sizeof (AFM_Pcc));
1011 j = 0;
1012 ccount++;
1014 else
1016 error = AFM_parseError;
1017 cont = FALSE;
1019 break;
1020 case COMPCHARPIECE:
1021 if (pcount < fi->ccd[pos].numOfPieces)
1023 keyword = token (fp);
1024 fi->ccd[pos].pieces[j].AFM_PccName = (char *)
1025 malloc (strlen (keyword) + 1);
1026 strcpy (fi->ccd[pos].pieces[j].AFM_PccName, keyword);
1027 keyword = token (fp);
1028 fi->ccd[pos].pieces[j].deltax = atoi (keyword);
1029 keyword = token (fp);
1030 fi->ccd[pos].pieces[j++].deltay = atoi (keyword);
1031 pcount++;
1033 else
1034 error = AFM_parseError;
1035 break;
1036 case ENDCOMPOSITES:
1037 cont = FALSE;
1038 break;
1039 case ENDFONTMETRICS:
1040 cont = FALSE;
1041 error = normalEOF;
1042 break;
1043 case NOPE:
1044 default:
1045 error = AFM_parseError;
1046 break;
1047 } /* switch */
1048 } /* while */
1050 if (error == AFM_ok && ccount != fi->numOfComps)
1051 error = AFM_parseError;
1053 return (error);
1055 } /* parseAFM_CompCharData */
1060 /*************************** 'PUBLIC' FUNCTION ********************/
1063 /*************************** parseFile *****************************/
1065 /* parseFile is the only 'public' procedure available. It is called
1066 * from an application wishing to get information from an AFM file.
1067 * The caller of this function is responsible for locating and opening
1068 * an AFM file and handling all errors associated with that task.
1070 * parseFile expects 3 parameters: a vaild file pointer, a pointer
1071 * to a (FontInfo *) variable (for which storage will be allocated and
1072 * the data requested filled in), and a mask specifying which
1073 * data from the AFM File should be saved in the FontInfo structure.
1075 * The file will be parsed and the requested data will be stored in
1076 * a record of type FontInfo (refer to ParseAFM.h).
1078 * parseFile returns an error code as defined in parseAFM.h.
1080 * The position of the read/write pointer associated with the file
1081 * pointer upon return of this function is undefined.
1084 extern int AFM_parseFile (FILE *fp, AFM_Font_info **fi, int flags)
1087 int code = AFM_ok; /* return code from each of the parsing routines */
1088 int error = AFM_ok; /* used as the return code from this function */
1090 register char *keyword; /* used to store a token */
1093 /* storage data for the global variable ident */
1094 if (!ident)
1095 ident = (char *) calloc (MAX_NAME, sizeof (char));
1096 if (ident == NULL) {error = AFM_storageProblem; return (error);}
1098 (*fi) = (AFM_Font_info *) calloc (1, sizeof (AFM_Font_info));
1099 if ((*fi) == NULL) {error = AFM_storageProblem; return (error);}
1101 if (flags & P_G)
1103 (*fi)->gfi = (AFM_GlobalFontInfo *) calloc (1, sizeof (AFM_GlobalFontInfo));
1104 if ((*fi)->gfi == NULL) {error = AFM_storageProblem; return (error);}
1107 /* The AFM File begins with Global Font Information. This section */
1108 /* will be parsed whether or not information should be saved. */
1109 code = parseGlobals (fp, (*fi)->gfi);
1111 if (code < 0) error = code;
1113 /* The Global Font Information is followed by the Character Metrics */
1114 /* section. Which procedure is used to parse this section depends on */
1115 /* how much information should be saved. If all of the metrics info */
1116 /* is wanted, parseCharMetrics is called. If only the character widths */
1117 /* is wanted, parseCharWidths is called. parseCharWidths will also */
1118 /* be called in the case that no character data is to be saved, just */
1119 /* to parse through the section. */
1121 if ((code != normalEOF) && (code != AFM_earlyEOF))
1123 (*fi)->numOfChars = atoi (token (fp));
1124 if (flags & (P_M ^ P_W))
1126 (*fi)->cmi = (AFM_CharMetricInfo *)
1127 calloc ((*fi)->numOfChars, sizeof (AFM_CharMetricInfo));
1128 if ((*fi)->cmi == NULL) {error = AFM_storageProblem; return (error);}
1129 code = parseCharMetrics (fp, *fi);
1131 else
1133 if (flags & P_W)
1135 (*fi)->cwi = (int *) calloc (256, sizeof (int));
1136 if ((*fi)->cwi == NULL)
1138 error = AFM_storageProblem;
1139 return (error);
1142 /* parse section regardless */
1143 code = parseCharWidths (fp, (*fi)->cwi);
1144 } /* else */
1145 } /* if */
1147 if ((error != AFM_earlyEOF) && (code < 0)) error = code;
1149 /* The remaining sections of the AFM are optional. This code will */
1150 /* look at the next keyword in the file to determine what section */
1151 /* is next, and then allocate the appropriate amount of storage */
1152 /* for the data (if the data is to be saved) and call the */
1153 /* appropriate parsing routine to parse the section. */
1155 while ((code != normalEOF) && (code != AFM_earlyEOF))
1157 keyword = token (fp);
1158 if (keyword == NULL)
1159 /* Have reached an early and unexpected EOF. */
1160 /* Set flag and stop parsing */
1162 code = AFM_earlyEOF;
1163 break; /* get out of loop */
1165 switch (recognize (keyword))
1167 case STARTKERNDATA:
1168 break;
1169 case ENDKERNDATA:
1170 break;
1171 case STARTTRACKKERN:
1172 keyword = token (fp);
1173 if (flags & P_T)
1175 (*fi)->numOfTracks = atoi (keyword);
1176 (*fi)->tkd = (AFM_TrackKernData *)
1177 calloc ((*fi)->numOfTracks, sizeof (AFM_TrackKernData));
1178 if ((*fi)->tkd == NULL)
1180 error = AFM_storageProblem;
1181 return (error);
1183 } /* if */
1184 code = parseAFM_TrackKernData (fp, *fi);
1185 break;
1186 case STARTKERNPAIRS:
1187 keyword = token (fp);
1188 if (flags & P_P)
1190 (*fi)->numOfPairs = atoi (keyword);
1191 (*fi)->pkd = (AFM_PairKernData *)
1192 calloc ((*fi)->numOfPairs, sizeof (AFM_PairKernData));
1193 if ((*fi)->pkd == NULL)
1195 error = AFM_storageProblem;
1196 return (error);
1198 } /* if */
1199 code = parseAFM_PairKernData (fp, *fi);
1200 break;
1201 case STARTCOMPOSITES:
1202 keyword = token (fp);
1203 if (flags & P_C)
1205 (*fi)->numOfComps = atoi (keyword);
1206 (*fi)->ccd = (AFM_CompCharData *)
1207 calloc ((*fi)->numOfComps, sizeof (AFM_CompCharData));
1208 if ((*fi)->ccd == NULL)
1210 error = AFM_storageProblem;
1211 return (error);
1213 } /* if */
1214 code = parseAFM_CompCharData (fp, *fi);
1215 break;
1216 case ENDFONTMETRICS:
1217 code = normalEOF;
1218 break;
1219 case NOPE:
1220 default:
1221 code = AFM_parseError;
1222 break;
1223 } /* switch */
1225 if ((error != AFM_earlyEOF) && (code < 0)) error = code;
1227 } /* while */
1229 if ((error != AFM_earlyEOF) && (code < 0)) error = code;
1231 if (ident != NULL) { free (ident); ident = NULL; }
1233 return (error);
1235 } /* parseFile */
1238 void
1239 AFM_free (AFM_Font_info *fi)
1241 if (fi->gfi) {
1242 free (fi->gfi->afmVersion);
1243 free (fi->gfi->fontName);
1244 free (fi->gfi->fullName);
1245 free (fi->gfi->familyName);
1246 free (fi->gfi->weight);
1247 free (fi->gfi->version);
1248 free (fi->gfi->notice);
1249 free (fi->gfi->encodingScheme);
1250 free (fi->gfi);
1253 /* This contains just scalars. */
1254 free (fi->cwi);
1256 if (fi->cmi) {
1257 int i;
1258 for (i = 0; i < fi->numOfChars; i++) {
1259 free (fi->cmi[i].name);
1260 while (fi->cmi[i].ligs) {
1261 AFM_Ligature *tmp;
1262 tmp = fi->cmi[i].ligs;
1263 free (tmp->succ);
1264 free (tmp->lig);
1265 free (tmp);
1266 fi->cmi[i].ligs = fi->cmi[i].ligs->next;
1269 free (fi->cmi);
1272 /* This contains just scalars. */
1273 free (fi->tkd);
1275 if (fi->pkd) {
1276 int i;
1277 for (i = 0; i < fi->numOfPairs; i++) {
1278 free (fi->pkd[i].name1);
1279 free (fi->pkd[i].name2);
1281 free (fi->pkd);
1284 if (fi->ccd) {
1285 int i, j;
1286 for (i = 0; i < fi->numOfComps; i++) {
1287 free (fi->ccd[i].ccName);
1288 for (j = 0; j < fi->ccd[i].numOfPieces; j++) {
1289 free (fi->ccd[i].pieces[j].AFM_PccName);
1291 free (fi->ccd[i].pieces);
1293 free (fi->ccd);
1296 free (fi);