lilypond-1.3.144
[lilypond.git] / flower / parse-afm.cc
blob39b9c0d7d989f062c315b2035549f638a95bcfeb
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, upper = (int) NOPE, midpoint, cmpvalue;
229 BOOL found = FALSE;
231 while ((upper >= lower) && !found)
233 midpoint = (lower + upper)/2;
234 if (keyStrings[midpoint] == NULL) break;
235 cmpvalue = strncmp (ident, keyStrings[midpoint], MAX_NAME);
236 if (cmpvalue == 0) found = TRUE;
237 else if (cmpvalue < 0) upper = midpoint - 1;
238 else lower = midpoint + 1;
239 } /* while */
241 if (found) return (enum parseKey) midpoint;
242 else return NOPE;
244 } /* recognize */
247 /************************* parseGlobals *****************************/
249 /* This function is called by "parseFile". It will parse the AFM File
250 * up to the "StartCharMetrics" keyword, which essentially marks the
251 * end of the Global Font Information and the beginning of the character
252 * metrics information.
254 * If the caller of "parseFile" specified that it wanted the Global
255 * Font Information (as defined by the "AFM File Specification"
256 * document), then that information will be stored in the returned
257 * data structure.
259 * Any Global Font Information entries that are not found in a
260 * given file, will have the usual default initialization value
261 * for its type (i.e. entries of type int will be 0, etc).
263 * This function returns an error code specifying whether there was
264 * a premature EOF or a parsing error. This return value is used by
265 * parseFile to determine if there is more file to parse.
268 static BOOL parseGlobals (FILE *fp, register AFM_GlobalFontInfo *gfi)
270 BOOL cont = TRUE, save = (gfi != NULL);
271 int error = AFM_ok;
272 register char *keyword;
274 while (cont)
276 keyword = token (fp);
278 if (keyword == NULL)
279 /* Have reached an early and unexpected EOF. */
280 /* Set flag and stop parsing */
282 error = AFM_earlyEOF;
283 break; /* get out of loop */
285 if (!save)
286 /* get tokens until the end of the Global Font info section */
287 /* without saving any of the data */
288 switch (recognize (keyword))
290 case STARTCHARMETRICS:
291 cont = FALSE;
292 break;
293 case ENDFONTMETRICS:
294 cont = FALSE;
295 error = normalEOF;
296 break;
297 default:
298 break;
299 } /* switch */
300 else
301 /* otherwise parse entire global font info section, */
302 /* saving the data */
303 switch (recognize (keyword))
305 case STARTFONTMETRICS:
306 keyword = token (fp);
307 gfi->afmVersion = (char *) malloc (strlen (keyword) + 1);
308 strcpy (gfi->afmVersion, keyword);
309 break;
310 case COMMENT:
311 keyword = linetoken (fp);
312 break;
313 case FONTNAME:
314 keyword = token (fp);
315 gfi->fontName = (char *) malloc (strlen (keyword) + 1);
316 strcpy (gfi->fontName, keyword);
317 break;
318 case ENCODINGSCHEME:
319 keyword = token (fp);
320 gfi->encodingScheme = (char *)
321 malloc (strlen (keyword) + 1);
322 strcpy (gfi->encodingScheme, keyword);
323 break;
324 case FULLNAME:
325 keyword = linetoken (fp);
326 gfi->fullName = (char *) malloc (strlen (keyword) + 1);
327 strcpy (gfi->fullName, keyword);
328 break;
329 case FAMILYNAME:
330 keyword = linetoken (fp);
331 gfi->familyName = (char *) malloc (strlen (keyword) + 1);
332 strcpy (gfi->familyName, keyword);
333 break;
334 case WEIGHT:
335 keyword = token (fp);
336 gfi->weight = (char *) malloc (strlen (keyword) + 1);
337 strcpy (gfi->weight, keyword);
338 break;
339 case ITALICANGLE:
340 keyword = token (fp);
341 gfi->italicAngle = atof (keyword);
342 if (errno == ERANGE) error = AFM_parseError;
343 break;
344 case ISFIXEDPITCH:
345 keyword = token (fp);
346 if (MATCH (keyword, False))
347 gfi->isFixedPitch = 0;
348 else
349 gfi->isFixedPitch = 1;
350 break;
351 case UNDERLINEPOSITION:
352 keyword = token (fp);
353 gfi->underlinePosition = atoi (keyword);
354 break;
355 case UNDERLINETHICKNESS:
356 keyword = token (fp);
357 gfi->underlineThickness = atoi (keyword);
358 break;
359 case VERSION:
360 keyword = linetoken (fp);
361 gfi->version = (char *) malloc (strlen (keyword) + 1);
362 strcpy (gfi->version, keyword);
363 break;
364 case NOTICE:
365 keyword = linetoken (fp);
366 gfi->notice = (char *) malloc (strlen (keyword) + 1);
367 strcpy (gfi->notice, keyword);
368 break;
369 case FONTBBOX:
370 keyword = token (fp);
371 gfi->fontBBox.llx = atoi (keyword);
372 keyword = token (fp);
373 gfi->fontBBox.lly = atoi (keyword);
374 keyword = token (fp);
375 gfi->fontBBox.urx = atoi (keyword);
376 keyword = token (fp);
377 gfi->fontBBox.ury = atoi (keyword);
378 break;
379 case CAPHEIGHT:
380 keyword = token (fp);
381 gfi->capHeight = atoi (keyword);
382 break;
383 case XHEIGHT:
384 keyword = token (fp);
385 gfi->xHeight = atoi (keyword);
386 break;
387 case DESCENDER:
388 keyword = token (fp);
389 gfi->descender = atoi (keyword);
390 break;
391 case ASCENDER:
392 keyword = token (fp);
393 gfi->ascender = atoi (keyword);
394 break;
395 case STARTCHARMETRICS:
396 cont = FALSE;
397 break;
398 case ENDFONTMETRICS:
399 cont = FALSE;
400 error = normalEOF;
401 break;
402 case NOPE:
403 default:
404 error = AFM_parseError;
405 break;
406 } /* switch */
407 } /* while */
409 return (error);
411 } /* parseGlobals */
415 /************************* initializeArray ************************/
417 /* Unmapped character codes are (at Adobe Systems) assigned the
418 * width of the space character (if one exists) else they get the
419 * value of 250 ems. This function initializes all entries in the
420 * char widths array to have this value. Then any mapped character
421 * codes will be replaced with the width of the appropriate character
422 * when parsing the character metric section.
424 * This function parses the Character Metrics Section looking
425 * for a space character (by comparing character names). If found,
426 * the width of the space character will be used to initialize the
427 * values in the array of character widths.
429 * Before returning, the position of the read/write pointer of the
430 * file is reset to be where it was upon entering this function.
433 static int initializeArray (FILE *fp, register int *cwi)
435 BOOL cont = TRUE, found = FALSE;
436 long opos = ftell (fp);
437 int code = 0, width = 0, i = 0, error = 0;
438 register char *keyword;
440 while (cont)
442 keyword = token (fp);
443 if (keyword == NULL)
445 error = AFM_earlyEOF;
446 break; /* get out of loop */
448 switch (recognize (keyword))
450 case COMMENT:
451 keyword = linetoken (fp);
452 break;
453 case CODE:
454 code = atoi (token (fp));
455 break;
456 case XWIDTH:
457 width = atoi (token (fp));
458 break;
459 case CHARNAME:
460 keyword = token (fp);
461 if (MATCH (keyword, Space))
463 cont = FALSE;
464 found = TRUE;
466 break;
467 case ENDCHARMETRICS:
468 cont = FALSE;
469 break;
470 case ENDFONTMETRICS:
471 cont = FALSE;
472 error = normalEOF;
473 break;
474 case NOPE:
475 default:
476 error = AFM_parseError;
477 break;
478 } /* switch */
479 } /* while */
481 if (!found)
482 width = 250;
484 for (i = 0; i < 256; ++i)
485 cwi[i] = width;
487 fseek (fp, opos, 0);
489 return (error);
491 } /* initializeArray */
494 /************************* parseCharWidths **************************/
496 /* This function is called by "parseFile". It will parse the AFM File
497 * up to the "EndCharMetrics" keyword. It will save the character
498 * width info (as opposed to all of the character metric information)
499 * if requested by the caller of parseFile. Otherwise, it will just
500 * parse through the section without saving any information.
502 * If data is to be saved, parseCharWidths is passed in a pointer
503 * to an array of widths that has already been initialized by the
504 * standard value for unmapped character codes. This function parses
505 * the Character Metrics section only storing the width information
506 * for the encoded characters into the array using the character code
507 * as the index into that array.
509 * This function returns an error code specifying whether there was
510 * a premature EOF or a parsing error. This return value is used by
511 * parseFile to determine if there is more file to parse.
514 static int parseCharWidths (FILE *fp, register int *cwi)
516 BOOL cont = TRUE, save = (cwi != NULL);
517 int pos = 0, error = AFM_ok;
518 register char *keyword;
520 while (cont)
522 keyword = token (fp);
523 /* Have reached an early and unexpected EOF. */
524 /* Set flag and stop parsing */
525 if (keyword == NULL)
527 error = AFM_earlyEOF;
528 break; /* get out of loop */
530 if (!save)
531 /* get tokens until the end of the Char Metrics section without */
532 /* saving any of the data*/
533 switch (recognize (keyword))
535 case ENDCHARMETRICS:
536 cont = FALSE;
537 break;
538 case ENDFONTMETRICS:
539 cont = FALSE;
540 error = normalEOF;
541 break;
542 default:
543 break;
544 } /* switch */
545 else
546 /* otherwise parse entire char metrics section, saving */
547 /* only the char x-width info */
548 switch (recognize (keyword))
550 case COMMENT:
551 keyword = linetoken (fp);
552 break;
553 case CODE:
554 keyword = token (fp);
555 pos = atoi (keyword);
556 break;
557 case XYWIDTH:
558 /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
559 keyword = token (fp); keyword = token (fp); /* eat values */
560 error = AFM_parseError;
561 break;
562 case XWIDTH:
563 keyword = token (fp);
564 if (pos >= 0) /* ignore unmapped chars */
565 cwi[pos] = atoi (keyword);
566 break;
567 case ENDCHARMETRICS:
568 cont = FALSE;
569 break;
570 case ENDFONTMETRICS:
571 cont = FALSE;
572 error = normalEOF;
573 break;
574 case CHARNAME: /* eat values (so doesn't cause AFM_parseError) */
575 keyword = token (fp);
576 break;
577 case CHARBBOX:
578 keyword = token (fp); keyword = token (fp);
579 keyword = token (fp); keyword = token (fp);
580 break;
581 case LIGATURE:
582 keyword = token (fp); keyword = token (fp);
583 break;
584 case NOPE:
585 default:
586 error = AFM_parseError;
587 break;
588 } /* switch */
589 } /* while */
591 return (error);
593 } /* parseCharWidths */
596 /************************* parseCharMetrics ************************/
598 /* This function is called by parseFile if the caller of parseFile
599 * requested that all character metric information be saved
600 * (as opposed to only the character width information).
602 * parseCharMetrics is passed in a pointer to an array of records
603 * to hold information on a per character basis. This function
604 * parses the Character Metrics section storing all character
605 * metric information for the ALL characters (mapped and unmapped)
606 * into the array.
608 * This function returns an error code specifying whether there was
609 * a premature EOF or a parsing error. This return value is used by
610 * parseFile to determine if there is more file to parse.
613 static int parseCharMetrics (FILE *fp, register AFM_Font_info *fi)
615 BOOL cont = TRUE, firstTime = TRUE;
616 int error = AFM_ok, count = 0;
617 register AFM_CharMetricInfo *temp = fi->cmi;
618 register char *keyword;
620 while (cont)
622 keyword = token (fp);
623 if (keyword == NULL)
625 error = AFM_earlyEOF;
626 break; /* get out of loop */
628 switch (recognize (keyword))
630 case COMMENT:
631 keyword = linetoken (fp);
632 break;
633 case CODE:
634 if (count < fi->numOfChars)
636 if (firstTime) firstTime = FALSE;
637 else temp++;
638 temp->code = atoi (token (fp));
639 count++;
641 else
643 error = AFM_parseError;
644 cont = FALSE;
646 break;
647 case XYWIDTH:
648 temp->wx = atoi (token (fp));
649 temp->wy = atoi (token (fp));
650 break;
651 case XWIDTH:
652 temp->wx = atoi (token (fp));
653 break;
654 case CHARNAME:
655 keyword = token (fp);
656 temp->name = (char *) malloc (strlen (keyword) + 1);
657 strcpy (temp->name, keyword);
658 break;
659 case CHARBBOX:
660 temp->charBBox.llx = atoi (token (fp));
661 temp->charBBox.lly = atoi (token (fp));
662 temp->charBBox.urx = atoi (token (fp));
663 temp->charBBox.ury = atoi (token (fp));
664 break;
665 case LIGATURE: {
666 AFM_Ligature **tail = & (temp->ligs);
667 AFM_Ligature *node = *tail;
669 if (*tail != NULL)
671 while (node->next != NULL)
672 node = node->next;
673 tail = & (node->next);
676 *tail = (AFM_Ligature *) calloc (1, sizeof (AFM_Ligature));
677 keyword = token (fp);
678 (*tail)->succ = (char *) malloc (strlen (keyword) + 1);
679 strcpy ((*tail)->succ, keyword);
680 keyword = token (fp);
681 (*tail)->lig = (char *) malloc (strlen (keyword) + 1);
682 strcpy ((*tail)->lig, keyword);
683 break; }
684 case ENDCHARMETRICS:
685 cont = FALSE;;
686 break;
687 case ENDFONTMETRICS:
688 cont = FALSE;
689 error = normalEOF;
690 break;
691 case NOPE:
692 default:
693 error = AFM_parseError;
694 break;
695 } /* switch */
696 } /* while */
698 if ((error == AFM_ok) && (count != fi->numOfChars))
699 error = AFM_parseError;
701 return (error);
703 } /* parseCharMetrics */
707 /************************* parseAFM_TrackKernData ***********************/
709 /* This function is called by "parseFile". It will parse the AFM File
710 * up to the "EndTrackKern" or "EndKernData" keywords. It will save the
711 * track kerning data if requested by the caller of parseFile.
713 * parseAFM_TrackKernData is passed in a pointer to the FontInfo record.
714 * If data is to be saved, the FontInfo record will already contain
715 * a valid pointer to storage for the track kerning data.
717 * This function returns an error code specifying whether there was
718 * a premature EOF or a parsing error. This return value is used by
719 * parseFile to determine if there is more file to parse.
722 static int parseAFM_TrackKernData (FILE *fp, register AFM_Font_info *fi)
724 BOOL cont = TRUE, save = (fi->tkd != NULL);
725 int pos = 0, error = AFM_ok, tcount = 0;
726 register char *keyword;
728 while (cont)
730 keyword = token (fp);
732 if (keyword == NULL)
734 error = AFM_earlyEOF;
735 break; /* get out of loop */
737 if (!save)
738 /* get tokens until the end of the Track Kerning Data */
739 /* section without saving any of the data */
740 switch (recognize (keyword))
742 case ENDTRACKKERN:
743 case ENDKERNDATA:
744 cont = FALSE;
745 break;
746 case ENDFONTMETRICS:
747 cont = FALSE;
748 error = normalEOF;
749 break;
750 default:
751 break;
752 } /* switch */
753 else
754 /* otherwise parse entire Track Kerning Data section, */
755 /* saving the data */
756 switch (recognize (keyword))
758 case COMMENT:
759 keyword = linetoken (fp);
760 break;
761 case TRACKKERN:
762 if (tcount < fi->numOfTracks)
764 keyword = token (fp);
765 fi->tkd[pos].degree = atoi (keyword);
766 keyword = token (fp);
767 fi->tkd[pos].minPtSize = atof (keyword);
768 if (errno == ERANGE) error = AFM_parseError;
769 keyword = token (fp);
770 fi->tkd[pos].minKernAmt = atof (keyword);
771 if (errno == ERANGE) error = AFM_parseError;
772 keyword = token (fp);
773 fi->tkd[pos].maxPtSize = atof (keyword);
774 if (errno == ERANGE) error = AFM_parseError;
775 keyword = token (fp);
776 fi->tkd[pos++].maxKernAmt = atof (keyword);
777 if (errno == ERANGE) error = AFM_parseError;
778 tcount++;
780 else
782 error = AFM_parseError;
783 cont = FALSE;
785 break;
786 case ENDTRACKKERN:
787 case ENDKERNDATA:
788 cont = FALSE;
789 break;
790 case ENDFONTMETRICS:
791 cont = FALSE;
792 error = normalEOF;
793 break;
794 case NOPE:
795 default:
796 error = AFM_parseError;
797 break;
798 } /* switch */
799 } /* while */
801 if (error == AFM_ok && tcount != fi->numOfTracks)
802 error = AFM_parseError;
804 return (error);
806 } /* parseAFM_TrackKernData */
809 /************************* parseAFM_PairKernData ************************/
811 /* This function is called by "parseFile". It will parse the AFM File
812 * up to the "EndKernPairs" or "EndKernData" keywords. It will save
813 * the pair kerning data if requested by the caller of parseFile.
815 * parseAFM_PairKernData is passed in a pointer to the FontInfo record.
816 * If data is to be saved, the FontInfo record will already contain
817 * a valid pointer to storage for the pair kerning data.
819 * This function returns an error code specifying whether there was
820 * a premature EOF or a parsing error. This return value is used by
821 * parseFile to determine if there is more file to parse.
824 static int parseAFM_PairKernData (FILE *fp, register AFM_Font_info *fi)
826 BOOL cont = TRUE, save = (fi->pkd != NULL);
827 int pos = 0, error = AFM_ok, pcount = 0;
828 register char *keyword;
830 while (cont)
832 keyword = token (fp);
834 if (keyword == NULL)
836 error = AFM_earlyEOF;
837 break; /* get out of loop */
839 if (!save)
840 /* get tokens until the end of the Pair Kerning Data */
841 /* section without saving any of the data */
842 switch (recognize (keyword))
844 case ENDKERNPAIRS:
845 case ENDKERNDATA:
846 cont = FALSE;
847 break;
848 case ENDFONTMETRICS:
849 cont = FALSE;
850 error = normalEOF;
851 break;
852 default:
853 break;
854 } /* switch */
855 else
856 /* otherwise parse entire Pair Kerning Data section, */
857 /* saving the data */
858 switch (recognize (keyword))
860 case COMMENT:
861 keyword = linetoken (fp);
862 break;
863 case KERNPAIR:
864 if (pcount < fi->numOfPairs)
866 keyword = token (fp);
867 fi->pkd[pos].name1 = (char *)
868 malloc (strlen (keyword) + 1);
869 strcpy (fi->pkd[pos].name1, keyword);
870 keyword = token (fp);
871 fi->pkd[pos].name2 = (char *)
872 malloc (strlen (keyword) + 1);
873 strcpy (fi->pkd[pos].name2, keyword);
874 keyword = token (fp);
875 fi->pkd[pos].xamt = atoi (keyword);
876 keyword = token (fp);
877 fi->pkd[pos++].yamt = atoi (keyword);
878 pcount++;
880 else
882 error = AFM_parseError;
883 cont = FALSE;
885 break;
886 case KERNPAIRXAMT:
887 if (pcount < fi->numOfPairs)
889 keyword = token (fp);
890 fi->pkd[pos].name1 = (char *)
891 malloc (strlen (keyword) + 1);
892 strcpy (fi->pkd[pos].name1, keyword);
893 keyword = token (fp);
894 fi->pkd[pos].name2 = (char *)
895 malloc (strlen (keyword) + 1);
896 strcpy (fi->pkd[pos].name2, keyword);
897 keyword = token (fp);
898 fi->pkd[pos++].xamt = atoi (keyword);
899 pcount++;
901 else
903 error = AFM_parseError;
904 cont = FALSE;
906 break;
907 case ENDKERNPAIRS:
908 case ENDKERNDATA:
909 cont = FALSE;
910 break;
911 case ENDFONTMETRICS:
912 cont = FALSE;
913 error = normalEOF;
914 break;
915 case NOPE:
916 default:
917 error = AFM_parseError;
918 break;
919 } /* switch */
920 } /* while */
922 if (error == AFM_ok && pcount != fi->numOfPairs)
923 error = AFM_parseError;
925 return (error);
927 } /* parseAFM_PairKernData */
930 /************************* parseAFM_CompCharData **************************/
932 /* This function is called by "parseFile". It will parse the AFM File
933 * up to the "EndComposites" keyword. It will save the composite
934 * character data if requested by the caller of parseFile.
936 * parseAFM_CompCharData is passed in a pointer to the FontInfo record, and
937 * a boolean representing if the data should be saved.
939 * This function will create the appropriate amount of storage for
940 * the composite character data and store a pointer to the storage
941 * in the FontInfo record.
943 * This function returns an error code specifying whether there was
944 * a premature EOF or a parsing error. This return value is used by
945 * parseFile to determine if there is more file to parse.
948 static int parseAFM_CompCharData (FILE *fp, register AFM_Font_info *fi)
950 BOOL cont = TRUE, firstTime = TRUE, save = (fi->ccd != NULL);
951 int pos = 0, j = 0, error = AFM_ok, ccount = 0, pcount = 0;
952 register char *keyword;
954 while (cont)
956 keyword = token (fp);
957 if (keyword == NULL)
958 /* Have reached an early and unexpected EOF. */
959 /* Set flag and stop parsing */
961 error = AFM_earlyEOF;
962 break; /* get out of loop */
964 if (ccount > fi->numOfComps)
966 error = AFM_parseError;
967 break; /* get out of loop */
969 if (!save)
970 /* get tokens until the end of the Composite Character info */
971 /* section without saving any of the data */
972 switch (recognize (keyword))
974 case ENDCOMPOSITES:
975 cont = FALSE;
976 break;
977 case ENDFONTMETRICS:
978 cont = FALSE;
979 error = normalEOF;
980 break;
981 default:
982 break;
983 } /* switch */
984 else
985 /* otherwise parse entire Composite Character info section, */
986 /* saving the data */
987 switch (recognize (keyword))
989 case COMMENT:
990 keyword = linetoken (fp);
991 break;
992 case COMPCHAR:
993 if (ccount < fi->numOfComps)
995 keyword = token (fp);
996 if (pcount != fi->ccd[pos].numOfPieces)
997 error = AFM_parseError;
998 pcount = 0;
999 if (firstTime) firstTime = FALSE;
1000 else pos++;
1001 fi->ccd[pos].ccName = (char *)
1002 malloc (strlen (keyword) + 1);
1003 strcpy (fi->ccd[pos].ccName, keyword);
1004 keyword = token (fp);
1005 fi->ccd[pos].numOfPieces = atoi (keyword);
1006 fi->ccd[pos].pieces = (AFM_Pcc *)
1007 calloc (fi->ccd[pos].numOfPieces, sizeof (AFM_Pcc));
1008 j = 0;
1009 ccount++;
1011 else
1013 error = AFM_parseError;
1014 cont = FALSE;
1016 break;
1017 case COMPCHARPIECE:
1018 if (pcount < fi->ccd[pos].numOfPieces)
1020 keyword = token (fp);
1021 fi->ccd[pos].pieces[j].AFM_PccName = (char *)
1022 malloc (strlen (keyword) + 1);
1023 strcpy (fi->ccd[pos].pieces[j].AFM_PccName, keyword);
1024 keyword = token (fp);
1025 fi->ccd[pos].pieces[j].deltax = atoi (keyword);
1026 keyword = token (fp);
1027 fi->ccd[pos].pieces[j++].deltay = atoi (keyword);
1028 pcount++;
1030 else
1031 error = AFM_parseError;
1032 break;
1033 case ENDCOMPOSITES:
1034 cont = FALSE;
1035 break;
1036 case ENDFONTMETRICS:
1037 cont = FALSE;
1038 error = normalEOF;
1039 break;
1040 case NOPE:
1041 default:
1042 error = AFM_parseError;
1043 break;
1044 } /* switch */
1045 } /* while */
1047 if (error == AFM_ok && ccount != fi->numOfComps)
1048 error = AFM_parseError;
1050 return (error);
1052 } /* parseAFM_CompCharData */
1057 /*************************** 'PUBLIC' FUNCTION ********************/
1060 /*************************** parseFile *****************************/
1062 /* parseFile is the only 'public' procedure available. It is called
1063 * from an application wishing to get information from an AFM file.
1064 * The caller of this function is responsible for locating and opening
1065 * an AFM file and handling all errors associated with that task.
1067 * parseFile expects 3 parameters: a vaild file pointer, a pointer
1068 * to a (FontInfo *) variable (for which storage will be allocated and
1069 * the data requested filled in), and a mask specifying which
1070 * data from the AFM File should be saved in the FontInfo structure.
1072 * The file will be parsed and the requested data will be stored in
1073 * a record of type FontInfo (refer to ParseAFM.h).
1075 * parseFile returns an error code as defined in parseAFM.h.
1077 * The position of the read/write pointer associated with the file
1078 * pointer upon return of this function is undefined.
1081 extern int AFM_parseFile (FILE *fp, AFM_Font_info **fi, int flags)
1084 int code = AFM_ok; /* return code from each of the parsing routines */
1085 int error = AFM_ok; /* used as the return code from this function */
1087 register char *keyword; /* used to store a token */
1090 /* storage data for the global variable ident */
1091 if (!ident)
1092 ident = (char *) calloc (MAX_NAME, sizeof (char));
1093 if (ident == NULL) {error = AFM_storageProblem; return (error);}
1095 (*fi) = (AFM_Font_info *) calloc (1, sizeof (AFM_Font_info));
1096 if ((*fi) == NULL) {error = AFM_storageProblem; return (error);}
1098 if (flags & P_G)
1100 (*fi)->gfi = (AFM_GlobalFontInfo *) calloc (1, sizeof (AFM_GlobalFontInfo));
1101 if ((*fi)->gfi == NULL) {error = AFM_storageProblem; return (error);}
1104 /* The AFM File begins with Global Font Information. This section */
1105 /* will be parsed whether or not information should be saved. */
1106 code = parseGlobals (fp, (*fi)->gfi);
1108 if (code < 0) error = code;
1110 /* The Global Font Information is followed by the Character Metrics */
1111 /* section. Which procedure is used to parse this section depends on */
1112 /* how much information should be saved. If all of the metrics info */
1113 /* is wanted, parseCharMetrics is called. If only the character widths */
1114 /* is wanted, parseCharWidths is called. parseCharWidths will also */
1115 /* be called in the case that no character data is to be saved, just */
1116 /* to parse through the section. */
1118 if ((code != normalEOF) && (code != AFM_earlyEOF))
1120 (*fi)->numOfChars = atoi (token (fp));
1121 if (flags & (P_M ^ P_W))
1123 (*fi)->cmi = (AFM_CharMetricInfo *)
1124 calloc ((*fi)->numOfChars, sizeof (AFM_CharMetricInfo));
1125 if ((*fi)->cmi == NULL) {error = AFM_storageProblem; return (error);}
1126 code = parseCharMetrics (fp, *fi);
1128 else
1130 if (flags & P_W)
1132 (*fi)->cwi = (int *) calloc (256, sizeof (int));
1133 if ((*fi)->cwi == NULL)
1135 error = AFM_storageProblem;
1136 return (error);
1139 /* parse section regardless */
1140 code = parseCharWidths (fp, (*fi)->cwi);
1141 } /* else */
1142 } /* if */
1144 if ((error != AFM_earlyEOF) && (code < 0)) error = code;
1146 /* The remaining sections of the AFM are optional. This code will */
1147 /* look at the next keyword in the file to determine what section */
1148 /* is next, and then allocate the appropriate amount of storage */
1149 /* for the data (if the data is to be saved) and call the */
1150 /* appropriate parsing routine to parse the section. */
1152 while ((code != normalEOF) && (code != AFM_earlyEOF))
1154 keyword = token (fp);
1155 if (keyword == NULL)
1156 /* Have reached an early and unexpected EOF. */
1157 /* Set flag and stop parsing */
1159 code = AFM_earlyEOF;
1160 break; /* get out of loop */
1162 switch (recognize (keyword))
1164 case STARTKERNDATA:
1165 break;
1166 case ENDKERNDATA:
1167 break;
1168 case STARTTRACKKERN:
1169 keyword = token (fp);
1170 if (flags & P_T)
1172 (*fi)->numOfTracks = atoi (keyword);
1173 (*fi)->tkd = (AFM_TrackKernData *)
1174 calloc ((*fi)->numOfTracks, sizeof (AFM_TrackKernData));
1175 if ((*fi)->tkd == NULL)
1177 error = AFM_storageProblem;
1178 return (error);
1180 } /* if */
1181 code = parseAFM_TrackKernData (fp, *fi);
1182 break;
1183 case STARTKERNPAIRS:
1184 keyword = token (fp);
1185 if (flags & P_P)
1187 (*fi)->numOfPairs = atoi (keyword);
1188 (*fi)->pkd = (AFM_PairKernData *)
1189 calloc ((*fi)->numOfPairs, sizeof (AFM_PairKernData));
1190 if ((*fi)->pkd == NULL)
1192 error = AFM_storageProblem;
1193 return (error);
1195 } /* if */
1196 code = parseAFM_PairKernData (fp, *fi);
1197 break;
1198 case STARTCOMPOSITES:
1199 keyword = token (fp);
1200 if (flags & P_C)
1202 (*fi)->numOfComps = atoi (keyword);
1203 (*fi)->ccd = (AFM_CompCharData *)
1204 calloc ((*fi)->numOfComps, sizeof (AFM_CompCharData));
1205 if ((*fi)->ccd == NULL)
1207 error = AFM_storageProblem;
1208 return (error);
1210 } /* if */
1211 code = parseAFM_CompCharData (fp, *fi);
1212 break;
1213 case ENDFONTMETRICS:
1214 code = normalEOF;
1215 break;
1216 case NOPE:
1217 default:
1218 code = AFM_parseError;
1219 break;
1220 } /* switch */
1222 if ((error != AFM_earlyEOF) && (code < 0)) error = code;
1224 } /* while */
1226 if ((error != AFM_earlyEOF) && (code < 0)) error = code;
1228 if (ident != NULL) { free (ident); ident = NULL; }
1230 return (error);
1232 } /* parseFile */
1235 void
1236 AFM_free (AFM_Font_info *fi)
1238 if (fi->gfi) {
1239 free (fi->gfi->afmVersion);
1240 free (fi->gfi->fontName);
1241 free (fi->gfi->fullName);
1242 free (fi->gfi->familyName);
1243 free (fi->gfi->weight);
1244 free (fi->gfi->version);
1245 free (fi->gfi->notice);
1246 free (fi->gfi->encodingScheme);
1247 free (fi->gfi);
1250 /* This contains just scalars. */
1251 free (fi->cwi);
1253 if (fi->cmi) {
1254 int i;
1255 for (i = 0; i < fi->numOfChars; i++) {
1256 free (fi->cmi[i].name);
1257 while (fi->cmi[i].ligs) {
1258 AFM_Ligature *tmp;
1259 tmp = fi->cmi[i].ligs;
1260 free (tmp->succ);
1261 free (tmp->lig);
1262 free (tmp);
1263 fi->cmi[i].ligs = fi->cmi[i].ligs->next;
1266 free (fi->cmi);
1269 /* This contains just scalars. */
1270 free (fi->tkd);
1272 if (fi->pkd) {
1273 int i;
1274 for (i = 0; i < fi->numOfPairs; i++) {
1275 free (fi->pkd[i].name1);
1276 free (fi->pkd[i].name2);
1278 free (fi->pkd);
1281 if (fi->ccd) {
1282 int i, j;
1283 for (i = 0; i < fi->numOfComps; i++) {
1284 free (fi->ccd[i].ccName);
1285 for (j = 0; j < fi->ccd[i].numOfPieces; j++) {
1286 free (fi->ccd[i].pieces[j].AFM_PccName);
1288 free (fi->ccd[i].pieces);
1290 free (fi->ccd);
1293 free (fi);