release commit
[lilypond.git] / flower / parse-afm.cc
blobffb7440809b29ae17c6cfce661fedb980a0cd29c
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.
20 * (C) 1988, 1989, 1990 by Adobe Systems Incorporated. All rights reserved.
22 * This file may be freely copied and redistributed as long as:
23 * 1) This entire notice continues to be included in the file,
24 * 2) If the file has been modified in any way, a notice of such
25 * modification is conspicuously indicated.
27 * PostScript, Display PostScript, and Adobe are registered trademarks of
28 * Adobe Systems Incorporated.
30 * ************************************************************************
31 * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT
32 * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS
33 * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR
34 * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY
35 * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION,
36 * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
37 * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
38 * ************************************************************************
41 /* parseAFM.c
43 * This file is used in conjuction with the parseAFM.h header file.
44 * This file contains several procedures that are used to parse AFM
45 * files. It is intended to work with an application program that needs
46 * font metric information. The program can be used as is by making a
47 * procedure call to "parseFile" (passing in the expected parameters)
48 * and having it fill in a data structure with the data from the
49 * AFM file, or an application developer may wish to customize this
50 * code.
52 * There is also a file, parseAFMclient.c, that is a sample application
53 * showing how to call the "parseFile" procedure and how to use the data
54 * after "parseFile" has returned.
56 * Please read the comments in parseAFM.h and parseAFMclient.c.
58 * History:
59 * original: DSM Thu Oct 20 17:39:59 PDT 1988
60 * modified: DSM Mon Jul 3 14:17:50 PDT 1989
61 * - added 'storageProblem' return code
62 * - fixed bug of not allocating extra byte for string duplication
63 * - fixed typos
64 * modified: DSM Tue Apr 3 11:18:34 PDT 1990
65 * - added free (ident) at end of parseFile routine
66 * modified: DSM Tue Jun 19 10:16:29 PDT 1990
67 * - changed (width == 250) to (width = 250) in initializeArray
70 #include "parse-afm.hh"
72 #include <cstdlib>
73 #include <cerrno>
74 #include <cmath>
75 #include <cstring>
77 #include <sys/file.h>
79 #include "warn.hh"
81 /* your basic constants */
82 #define TRUE 1
83 #define FALSE 0
84 #define EOL '\n' /* end-of-line indicator */
85 #define MAX_NAME 4096 /* max length for identifiers */
87 /* Flags that can be AND'ed together to specify exactly what
88 * information from the AFM file should be saved.
90 #define P_G 0x01 /* 0000 0001 */ /* Global Font Info */
91 #define P_W 0x02 /* 0000 0010 */ /* Character Widths ONLY */
92 #define P_M 0x06 /* 0000 0110 */ /* All Char Metric Info */
93 #define P_P 0x08 /* 0000 1000 */ /* Pair Kerning Info */
94 #define P_T 0x10 /* 0001 0000 */ /* Track Kerning Info */
95 #define P_C 0x20 /* 0010 0000 */ /* Composite Char Info */
97 /* Commonly used flags
99 #define P_GW \
100 (P_G | P_W)
101 #define P_GM \
102 (P_G | P_M)
103 #define P_GMP \
104 (P_G | P_M | P_P)
105 #define P_GMK \
106 (P_G | P_M | P_P | P_T)
107 #define P_GALL \
108 (P_G | P_M | P_P | P_T | P_C)
110 #define METATYPE1_BUG /* Parse Metatype1's (version unknown)
111 'Generated' global tag as comment. */
113 #define lineterm EOL /* line terminating character */
114 #define lineterm_alt '\r' /* alternative line terminating character */
115 #define normalEOF 1 /* return code from parsing routines used only */
116 /* in this module */
117 #define Space "space" /* used in string comparison to look for the width */
118 /* of the space character to init the widths array */
119 #define False "false" /* used in string comparison to check the value of */
120 /* boolean keys (e.g. IsFixedPitch) */
122 #define MATCH(A, B) (strncmp ((A), (B), MAX_NAME) == 0)
124 /*************************** GLOBALS ***********************/
126 static char *ident = NULL; /* storage buffer for keywords */
128 /* "shorts" for fast case statement
129 * The values of each of these enumerated items correspond to an entry in the
130 * table of strings defined below. Therefore, if you add a new string as
131 * new keyword into the keyStrings table, you must also add a corresponding
132 * parseKey AND it MUST be in the same position!
134 * IMPORTANT: since the sorting algorithm is a binary search, the strings of
135 * keywords must be placed in lexicographical order, below. [Therefore, the
136 * enumerated items are not necessarily in lexicographical order, depending
137 * on the name chosen. BUT, they must be placed in the same position as the
138 * corresponding key string.] The NOPE shall remain in the last position,
139 * since it does not correspond to any key string, and it is used in the
140 * "recognize" procedure to calculate how many possible keys there are.
143 enum parseKey
145 ASCENDER, CHARBBOX, CODE, COMPCHAR, CAPHEIGHT, COMMENT,
146 DESCENDER, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES,
147 ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN,
148 FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME,
149 #ifdef METATYPE1_BUG
150 GENERATED,
151 #endif
152 ISFIXEDPITCH,
153 ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, CHARNAME,
154 NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES,
155 STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS,
156 STARTTRACKKERN, TRACKKERN, UNDERLINEPOSITION,
157 UNDERLINETHICKNESS, VERSION, XYWIDTH, XWIDTH, WEIGHT, XHEIGHT,
158 NOPE };
160 /* keywords for the system:
161 * This a table of all of the current strings that are vaild AFM keys.
162 * Each entry can be referenced by the appropriate parseKey value (an
163 * enumerated data type defined above). If you add a new keyword here,
164 * a corresponding parseKey MUST be added to the enumerated data type
165 * defined above, AND it MUST be added in the same position as the
166 * string is in this table.
168 * IMPORTANT: since the sorting algorithm is a binary search, the keywords
169 * must be placed in lexicographical order. And, NULL should remain at the
170 * end.
173 static char *keyStrings[]
175 "Ascender", "B", "C", "CC", "CapHeight", "Comment",
176 "Descender", "EncodingScheme", "EndCharMetrics", "EndComposites",
177 "EndFontMetrics", "EndKernData", "EndKernPairs", "EndTrackKern",
178 "FamilyName", "FontBBox", "FontName", "FullName",
179 #ifdef METATYPE1_BUG
180 "Generated",
181 #endif
182 "IsFixedPitch",
183 "ItalicAngle", "KP", "KPX", "L", "N",
184 "Notice", "PCC", "StartCharMetrics", "StartComposites",
185 "StartFontMetrics", "StartKernData", "StartKernPairs",
186 "StartTrackKern", "TrackKern", "UnderlinePosition",
187 "UnderlineThickness", "Version", "W", "WX", "Weight", "XHeight",
188 NULL };
190 /*************************** PARSING ROUTINES **************/
192 /*************************** token *************************/
194 /* A "AFM File Conventions" tokenizer. That means that it will
195 * return the next token delimited by white space. See also
196 * the `linetoken' routine, which does a similar thing but
197 * reads all tokens until the next end-of-line.
200 static char *
201 token (FILE *stream)
203 int ch, idx;
205 /* skip over white space */
206 while ((ch = fgetc (stream)) == ' ' || ch == lineterm ||
207 ch == lineterm_alt ||
208 ch == ',' || ch == '\t' || ch == ';');
210 idx = 0;
211 while (idx < MAX_NAME - 1 &&
212 ch != EOF && ch != ' ' && ch != lineterm && ch != lineterm_alt
213 && ch != '\t' && ch != ':' && ch != ';')
215 ident[idx++] = ch;
216 ch = fgetc (stream);
217 } /* while */
219 if (ch == EOF && idx < 1) return ((char *)NULL);
220 if (idx >= 1 && ch != ':' ) ungetc (ch, stream);
221 if (idx < 1 ) ident[idx++] = ch; /* single-character token */
222 ident[idx] = 0;
224 return (ident); /* returns pointer to the token */
225 } /* token */
227 /*************************** linetoken *************************/
229 /* "linetoken" will get read all tokens until the EOL character from
230 * the given stream. This is used to get any arguments that can be
231 * more than one word (like Comment lines and FullName).
234 static char *
235 linetoken (FILE *stream)
237 int ch, idx;
239 while ((ch = fgetc (stream)) == ' ' || ch == '\t' );
241 idx = 0;
242 while (idx < MAX_NAME - 1 &&
243 ch != EOF && ch != lineterm && ch != lineterm_alt)
245 ident[idx++] = ch;
246 ch = fgetc (stream);
247 } /* while */
249 ungetc (ch, stream);
250 ident[idx] = 0;
252 return (ident); /* returns pointer to the token */
253 } /* linetoken */
255 /*************************** recognize *************************/
257 /* This function tries to match a string to a known list of
258 * valid AFM entries (check the keyStrings array above).
259 * "ident" contains everything from white space through the
260 * next space, tab, or ":" character.
262 * The algorithm is a standard Knuth binary search.
265 static enum parseKey
266 recognize (register char *ident)
268 int lower = 0,
269 upper = (int) NOPE,
270 midpoint = 0,
271 cmpvalue = 0;
272 BOOL found = FALSE;
274 while ((upper >= lower) && !found)
276 midpoint = (lower + upper) / 2;
277 if (keyStrings[midpoint] == NULL)
278 break;
279 cmpvalue = strncmp (ident, keyStrings[midpoint], MAX_NAME);
280 if (cmpvalue == 0)
281 found = TRUE;
282 else
283 if (cmpvalue < 0)
284 upper = midpoint - 1;
285 else
286 lower = midpoint + 1;
289 if (found)
290 return (enum parseKey) midpoint;
291 else
292 return NOPE;
295 /************************* parseGlobals *****************************/
297 /* This function is called by "parseFile". It will parse the AFM File
298 * up to the "StartCharMetrics" keyword, which essentially marks the
299 * end of the Global Font Information and the beginning of the character
300 * metrics information.
302 * If the caller of "parseFile" specified that it wanted the Global
303 * Font Information (as defined by the "AFM File Specification"
304 * document), then that information will be stored in the returned
305 * data structure.
307 * Any Global Font Information entries that are not found in a
308 * given file, will have the usual default initialization value
309 * for its type (i.e. entries of type int will be 0, etc).
311 * This function returns an error code specifying whether there was
312 * a premature EOF or a parsing error. This return value is used by
313 * parseFile to determine if there is more file to parse.
316 static BOOL
317 parseGlobals (FILE *fp, register AFM_GlobalFontInfo *gfi)
319 BOOL cont = TRUE, save = (gfi != NULL);
320 int error = AFM_ok;
321 register char *keyword;
323 while (cont)
325 keyword = token (fp);
327 if (keyword == NULL)
328 /* Have reached an early and unexpected EOF. */
329 /* Set flag and stop parsing */
331 error = AFM_earlyEOF;
332 break; /* get out of loop */
334 if (!save)
335 /* get tokens until the end of the Global Font info section */
336 /* without saving any of the data */
337 switch (recognize (keyword))
339 case STARTCHARMETRICS:
340 cont = FALSE;
341 break;
342 case ENDFONTMETRICS:
343 cont = FALSE;
344 error = normalEOF;
345 break;
346 default:
347 break;
348 } /* switch */
349 else
350 /* otherwise parse entire global font info section, */
351 /* saving the data */
352 switch (recognize (keyword))
354 case STARTFONTMETRICS:
355 keyword = token (fp);
356 gfi->afmVersion = (char *) malloc (strlen (keyword) + 1);
357 strcpy (gfi->afmVersion, keyword);
358 break;
359 case COMMENT:
360 #ifdef METATYPE1_BUG
361 case GENERATED:
362 #endif
363 keyword = linetoken (fp);
364 break;
365 case FONTNAME:
366 keyword = token (fp);
367 gfi->fontName = (char *) malloc (strlen (keyword) + 1);
368 strcpy (gfi->fontName, keyword);
369 break;
370 case ENCODINGSCHEME:
371 keyword = token (fp);
372 gfi->encodingScheme = (char *)
373 malloc (strlen (keyword) + 1);
374 strcpy (gfi->encodingScheme, keyword);
375 break;
376 case FULLNAME:
377 keyword = linetoken (fp);
378 gfi->fullName = (char *) malloc (strlen (keyword) + 1);
379 strcpy (gfi->fullName, keyword);
380 break;
381 case FAMILYNAME:
382 keyword = linetoken (fp);
383 gfi->familyName = (char *) malloc (strlen (keyword) + 1);
384 strcpy (gfi->familyName, keyword);
385 break;
386 case WEIGHT:
387 keyword = token (fp);
388 gfi->weight = (char *) malloc (strlen (keyword) + 1);
389 strcpy (gfi->weight, keyword);
390 break;
391 case ITALICANGLE:
392 keyword = token (fp);
393 gfi->italicAngle = atof (keyword);
394 if (errno == ERANGE) error = AFM_parseError;
395 break;
396 case ISFIXEDPITCH:
397 keyword = token (fp);
398 if (MATCH (keyword, False))
399 gfi->isFixedPitch = 0;
400 else
401 gfi->isFixedPitch = 1;
402 break;
403 case UNDERLINEPOSITION:
404 keyword = token (fp);
405 gfi->underlinePosition = atoi (keyword);
406 break;
407 case UNDERLINETHICKNESS:
408 keyword = token (fp);
409 gfi->underlineThickness = atoi (keyword);
410 break;
411 case VERSION:
412 keyword = linetoken (fp);
413 gfi->version = (char *) malloc (strlen (keyword) + 1);
414 strcpy (gfi->version, keyword);
415 break;
416 case NOTICE:
417 keyword = linetoken (fp);
418 gfi->notice = (char *) malloc (strlen (keyword) + 1);
419 strcpy (gfi->notice, keyword);
420 break;
421 case FONTBBOX:
422 keyword = token (fp);
423 gfi->fontBBox.llx = atoi (keyword);
424 keyword = token (fp);
425 gfi->fontBBox.lly = atoi (keyword);
426 keyword = token (fp);
427 gfi->fontBBox.urx = atoi (keyword);
428 keyword = token (fp);
429 gfi->fontBBox.ury = atoi (keyword);
430 break;
431 case CAPHEIGHT:
432 keyword = token (fp);
433 gfi->capHeight = atoi (keyword);
434 break;
435 case XHEIGHT:
436 keyword = token (fp);
437 gfi->xHeight = atoi (keyword);
438 break;
439 case DESCENDER:
440 keyword = token (fp);
441 gfi->descender = atoi (keyword);
442 break;
443 case ASCENDER:
444 keyword = token (fp);
445 gfi->ascender = atoi (keyword);
446 break;
447 case STARTCHARMETRICS:
448 cont = FALSE;
449 break;
450 case ENDFONTMETRICS:
451 cont = FALSE;
452 error = normalEOF;
453 break;
454 case NOPE:
455 default:
456 error = AFM_parseError;
457 break;
458 } /* switch */
459 } /* while */
461 return (error);
462 } /* parseGlobals */
464 #if 0
465 /************************* initializeArray ************************/
467 /* Unmapped character codes are (at Adobe Systems) assigned the
468 * width of the space character (if one exists) else they get the
469 * value of 250 ems. This function initializes all entries in the
470 * char widths array to have this value. Then any mapped character
471 * codes will be replaced with the width of the appropriate character
472 * when parsing the character metric section.
474 * This function parses the Character Metrics Section looking
475 * for a space character (by comparing character names). If found,
476 * the width of the space character will be used to initialize the
477 * values in the array of character widths.
479 * Before returning, the position of the read/write pointer of the
480 * file is reset to be where it was upon entering this function.
483 static int
484 initializeArray (FILE *fp, register int *cwi)
486 BOOL cont = TRUE, found = FALSE;
487 long opos = ftell (fp);
488 int code = 0, width = 0, i = 0, error = 0;
489 register char *keyword;
491 while (cont)
493 keyword = token (fp);
494 if (keyword == NULL)
496 error = AFM_earlyEOF;
497 break; /* get out of loop */
499 switch (recognize (keyword))
501 case COMMENT:
502 keyword = linetoken (fp);
503 break;
504 case CODE:
505 code = atoi (token (fp));
506 break;
507 case XWIDTH:
508 width = atoi (token (fp));
509 break;
510 case CHARNAME:
511 keyword = token (fp);
512 if (MATCH (keyword, Space))
514 cont = FALSE;
515 found = TRUE;
517 break;
518 case ENDCHARMETRICS:
519 cont = FALSE;
520 break;
521 case ENDFONTMETRICS:
522 cont = FALSE;
523 error = normalEOF;
524 break;
525 case NOPE:
526 default:
527 error = AFM_parseError;
528 break;
529 } /* switch */
530 } /* while */
532 if (!found)
533 width = 250;
535 for (i = 0; i < 256; ++i)
536 cwi[i] = width;
538 fseek (fp, opos, 0);
540 return (error);
541 } /* initializeArray */
542 #endif
544 /************************* parseCharWidths **************************/
546 /* This function is called by "parseFile". It will parse the AFM File
547 * up to the "EndCharMetrics" keyword. It will save the character
548 * width info (as opposed to all of the character metric information)
549 * if requested by the caller of parseFile. Otherwise, it will just
550 * parse through the section without saving any information.
552 * If data is to be saved, parseCharWidths is passed in a pointer
553 * to an array of widths that has already been initialized by the
554 * standard value for unmapped character codes. This function parses
555 * the Character Metrics section only storing the width information
556 * for the encoded characters into the array using the character code
557 * as the index into that array.
559 * This function returns an error code specifying whether there was
560 * a premature EOF or a parsing error. This return value is used by
561 * parseFile to determine if there is more file to parse.
564 static int
565 parseCharWidths (FILE *fp, register int *cwi)
567 BOOL cont = TRUE, save = (cwi != NULL);
568 int pos = 0, error = AFM_ok;
569 register char *keyword;
571 while (cont)
573 keyword = token (fp);
574 /* Have reached an early and unexpected EOF. */
575 /* Set flag and stop parsing */
576 if (keyword == NULL)
578 error = AFM_earlyEOF;
579 break; /* get out of loop */
581 if (!save)
582 /* get tokens until the end of the Char Metrics section without */
583 /* saving any of the data*/
584 switch (recognize (keyword))
586 case ENDCHARMETRICS:
587 cont = FALSE;
588 break;
589 case ENDFONTMETRICS:
590 cont = FALSE;
591 error = normalEOF;
592 break;
593 default:
594 break;
595 } /* switch */
596 else
597 /* otherwise parse entire char metrics section, saving */
598 /* only the char x-width info */
599 switch (recognize (keyword))
601 case COMMENT:
602 keyword = linetoken (fp);
603 break;
604 case CODE:
605 keyword = token (fp);
606 pos = atoi (keyword);
607 break;
608 case XYWIDTH:
609 /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
610 keyword = token (fp); keyword = token (fp); /* eat values */
611 error = AFM_parseError;
612 break;
613 case XWIDTH:
614 keyword = token (fp);
615 if (pos >= 0) /* ignore unmapped chars */
616 cwi[pos] = atoi (keyword);
617 break;
618 case ENDCHARMETRICS:
619 cont = FALSE;
620 break;
621 case ENDFONTMETRICS:
622 cont = FALSE;
623 error = normalEOF;
624 break;
625 case CHARNAME: /* eat values (so doesn't cause AFM_parseError) */
626 keyword = token (fp);
627 break;
628 case CHARBBOX:
629 keyword = token (fp); keyword = token (fp);
630 keyword = token (fp); keyword = token (fp);
631 break;
632 case LIGATURE:
633 keyword = token (fp); keyword = token (fp);
634 break;
635 case NOPE:
636 default:
637 error = AFM_parseError;
638 break;
639 } /* switch */
640 } /* while */
642 return (error);
643 } /* parseCharWidths */
645 /************************* parseCharMetrics ************************/
647 /* This function is called by parseFile if the caller of parseFile
648 * requested that all character metric information be saved
649 * (as opposed to only the character width information).
651 * parseCharMetrics is passed in a pointer to an array of records
652 * to hold information on a per character basis. This function
653 * parses the Character Metrics section storing all character
654 * metric information for the ALL characters (mapped and unmapped)
655 * into the array.
657 * This function returns an error code specifying whether there was
658 * a premature EOF or a parsing error. This return value is used by
659 * parseFile to determine if there is more file to parse.
662 static int
663 parseCharMetrics (FILE *fp, register AFM_Font_info *fi)
665 BOOL cont = TRUE, firstTime = TRUE;
666 int error = AFM_ok, count = 0;
667 register AFM_CharMetricInfo *temp = fi->cmi;
668 register char *keyword;
670 while (cont)
672 keyword = token (fp);
673 if (keyword == NULL)
675 error = AFM_earlyEOF;
676 break; /* get out of loop */
678 switch (recognize (keyword))
680 case COMMENT:
681 keyword = linetoken (fp);
682 break;
683 case CODE:
684 if (count < fi->numOfChars)
686 if (firstTime)
687 firstTime = FALSE;
688 else
689 temp++;
690 temp->code = atoi (token (fp));
691 count++;
693 else
695 warning ("Too many metrics.");
696 error = AFM_parseError;
697 cont = FALSE;
699 break;
700 case XYWIDTH:
701 temp->wx = atoi (token (fp));
702 temp->wy = atoi (token (fp));
703 break;
704 case XWIDTH:
705 temp->wx = atoi (token (fp));
706 break;
708 case CHARNAME:
709 keyword = token (fp);
710 temp->name = (char *) malloc (strlen (keyword) + 1);
711 strcpy (temp->name, keyword);
712 break;
714 case CHARBBOX:
715 temp->charBBox.llx = atoi (token (fp));
716 temp->charBBox.lly = atoi (token (fp));
717 temp->charBBox.urx = atoi (token (fp));
718 temp->charBBox.ury = atoi (token (fp));
719 break;
721 case LIGATURE:
723 AFM_Ligature **tail = &(temp->ligs);
724 AFM_Ligature *node = *tail;
726 if (*tail != NULL)
728 while (node->next != NULL)
729 node = node->next;
730 tail = &(node->next);
733 *tail = (AFM_Ligature *) calloc (1, sizeof (AFM_Ligature));
734 keyword = token (fp);
735 (*tail)->succ = (char *) malloc (strlen (keyword) + 1);
736 strcpy ((*tail)->succ, keyword);
737 keyword = token (fp);
738 (*tail)->lig = (char *) malloc (strlen (keyword) + 1);
739 strcpy ((*tail)->lig, keyword);
740 break; }
741 case ENDCHARMETRICS:
742 cont = FALSE;;
743 break;
744 case ENDFONTMETRICS:
745 cont = FALSE;
746 error = normalEOF;
747 break;
748 case NOPE:
749 default:
750 warning ("Unknown token");
752 error = AFM_parseError;
753 break;
754 } /* switch */
755 } /* while */
757 if ((error == AFM_ok) && (count != fi->numOfChars))
759 warning ("Incorrect char count");
760 error = AFM_parseError;
762 return (error);
763 } /* parseCharMetrics */
765 /************************* parseAFM_TrackKernData ***********************/
767 /* This function is called by "parseFile". It will parse the AFM File
768 * up to the "EndTrackKern" or "EndKernData" keywords. It will save the
769 * track kerning data if requested by the caller of parseFile.
771 * parseAFM_TrackKernData is passed in a pointer to the FontInfo record.
772 * If data is to be saved, the FontInfo record will already contain
773 * a valid pointer to storage for the track kerning data.
775 * This function returns an error code specifying whether there was
776 * a premature EOF or a parsing error. This return value is used by
777 * parseFile to determine if there is more file to parse.
780 static int
781 parseAFM_TrackKernData (FILE *fp, register AFM_Font_info *fi)
783 BOOL cont = TRUE, save = (fi->tkd != NULL);
784 int pos = 0, error = AFM_ok, tcount = 0;
785 register char *keyword;
787 while (cont)
789 keyword = token (fp);
791 if (keyword == NULL)
793 error = AFM_earlyEOF;
794 break; /* get out of loop */
796 if (!save)
797 /* get tokens until the end of the Track Kerning Data */
798 /* section without saving any of the data */
799 switch (recognize (keyword))
801 case ENDTRACKKERN:
802 case ENDKERNDATA:
803 cont = FALSE;
804 break;
805 case ENDFONTMETRICS:
806 cont = FALSE;
807 error = normalEOF;
808 break;
809 default:
810 break;
811 } /* switch */
812 else
813 /* otherwise parse entire Track Kerning Data section, */
814 /* saving the data */
815 switch (recognize (keyword))
817 case COMMENT:
818 #ifdef METATYPE1_BUG
819 case GENERATED:
820 #endif
821 keyword = linetoken (fp);
822 break;
823 case TRACKKERN:
824 if (tcount < fi->numOfTracks)
826 keyword = token (fp);
827 fi->tkd[pos].degree = atoi (keyword);
828 keyword = token (fp);
829 fi->tkd[pos].minPtSize = atof (keyword);
830 if (errno == ERANGE) error = AFM_parseError;
831 keyword = token (fp);
832 fi->tkd[pos].minKernAmt = atof (keyword);
833 if (errno == ERANGE) error = AFM_parseError;
834 keyword = token (fp);
835 fi->tkd[pos].maxPtSize = atof (keyword);
836 if (errno == ERANGE) error = AFM_parseError;
837 keyword = token (fp);
838 fi->tkd[pos++].maxKernAmt = atof (keyword);
839 if (errno == ERANGE) error = AFM_parseError;
840 tcount++;
842 else
844 error = AFM_parseError;
845 cont = FALSE;
847 break;
848 case ENDTRACKKERN:
849 case ENDKERNDATA:
850 cont = FALSE;
851 break;
852 case ENDFONTMETRICS:
853 cont = FALSE;
854 error = normalEOF;
855 break;
856 case NOPE:
857 default:
858 error = AFM_parseError;
859 break;
860 } /* switch */
861 } /* while */
863 if (error == AFM_ok && tcount != fi->numOfTracks)
864 error = AFM_parseError;
866 return (error);
867 } /* parseAFM_TrackKernData */
869 /************************* parseAFM_PairKernData ************************/
871 /* This function is called by "parseFile". It will parse the AFM File
872 * up to the "EndKernPairs" or "EndKernData" keywords. It will save
873 * the pair kerning data if requested by the caller of parseFile.
875 * parseAFM_PairKernData is passed in a pointer to the FontInfo record.
876 * If data is to be saved, the FontInfo record will already contain
877 * a valid pointer to storage for the pair kerning data.
879 * This function returns an error code specifying whether there was
880 * a premature EOF or a parsing error. This return value is used by
881 * parseFile to determine if there is more file to parse.
884 static int
885 parseAFM_PairKernData (FILE *fp, register AFM_Font_info *fi)
887 BOOL cont = TRUE, save = (fi->pkd != NULL);
888 int pos = 0, error = AFM_ok, pcount = 0;
889 register char *keyword;
891 while (cont)
893 keyword = token (fp);
895 if (keyword == NULL)
897 error = AFM_earlyEOF;
898 break; /* get out of loop */
900 if (!save)
901 /* get tokens until the end of the Pair Kerning Data */
902 /* section without saving any of the data */
903 switch (recognize (keyword))
905 case ENDKERNPAIRS:
906 case ENDKERNDATA:
907 cont = FALSE;
908 break;
909 case ENDFONTMETRICS:
910 cont = FALSE;
911 error = normalEOF;
912 break;
913 default:
914 break;
915 } /* switch */
916 else
917 /* otherwise parse entire Pair Kerning Data section, */
918 /* saving the data */
919 switch (recognize (keyword))
921 case COMMENT:
922 keyword = linetoken (fp);
923 break;
924 case KERNPAIR:
925 if (pcount < fi->numOfPairs)
927 keyword = token (fp);
928 fi->pkd[pos].name1 = (char *)
929 malloc (strlen (keyword) + 1);
930 strcpy (fi->pkd[pos].name1, keyword);
931 keyword = token (fp);
932 fi->pkd[pos].name2 = (char *)
933 malloc (strlen (keyword) + 1);
934 strcpy (fi->pkd[pos].name2, keyword);
935 keyword = token (fp);
936 fi->pkd[pos].xamt = atoi (keyword);
937 keyword = token (fp);
938 fi->pkd[pos++].yamt = atoi (keyword);
939 pcount++;
941 else
943 error = AFM_parseError;
944 cont = FALSE;
946 break;
947 case KERNPAIRXAMT:
948 if (pcount < fi->numOfPairs)
950 keyword = token (fp);
951 fi->pkd[pos].name1 = (char *)
952 malloc (strlen (keyword) + 1);
953 strcpy (fi->pkd[pos].name1, keyword);
954 keyword = token (fp);
955 fi->pkd[pos].name2 = (char *)
956 malloc (strlen (keyword) + 1);
957 strcpy (fi->pkd[pos].name2, keyword);
958 keyword = token (fp);
959 fi->pkd[pos++].xamt = atoi (keyword);
960 pcount++;
962 else
964 error = AFM_parseError;
965 cont = FALSE;
967 break;
968 case ENDKERNPAIRS:
969 case ENDKERNDATA:
970 cont = FALSE;
971 break;
972 case ENDFONTMETRICS:
973 cont = FALSE;
974 error = normalEOF;
975 break;
976 case NOPE:
977 default:
978 error = AFM_parseError;
979 break;
980 } /* switch */
981 } /* while */
983 if (error == AFM_ok && pcount != fi->numOfPairs)
984 error = AFM_parseError;
986 return (error);
987 } /* parseAFM_PairKernData */
989 /************************* parseAFM_CompCharData **************************/
991 /* This function is called by "parseFile". It will parse the AFM File
992 * up to the "EndComposites" keyword. It will save the composite
993 * character data if requested by the caller of parseFile.
995 * parseAFM_CompCharData is passed in a pointer to the FontInfo record, and
996 * a boolean representing if the data should be saved.
998 * This function will create the appropriate amount of storage for
999 * the composite character data and store a pointer to the storage
1000 * in the FontInfo record.
1002 * This function returns an error code specifying whether there was
1003 * a premature EOF or a parsing error. This return value is used by
1004 * parseFile to determine if there is more file to parse.
1007 static int
1008 parseAFM_CompCharData (FILE *fp, register AFM_Font_info *fi)
1010 BOOL cont = TRUE, firstTime = TRUE, save = (fi->ccd != NULL);
1011 int pos = 0, j = 0, error = AFM_ok, ccount = 0, pcount = 0;
1012 register char *keyword;
1014 while (cont)
1016 keyword = token (fp);
1017 if (keyword == NULL)
1018 /* Have reached an early and unexpected EOF. */
1019 /* Set flag and stop parsing */
1021 error = AFM_earlyEOF;
1022 break; /* get out of loop */
1024 if (ccount > fi->numOfComps)
1026 error = AFM_parseError;
1027 break; /* get out of loop */
1029 if (!save)
1030 /* get tokens until the end of the Composite Character info */
1031 /* section without saving any of the data */
1032 switch (recognize (keyword))
1034 case ENDCOMPOSITES:
1035 cont = FALSE;
1036 break;
1037 case ENDFONTMETRICS:
1038 cont = FALSE;
1039 error = normalEOF;
1040 break;
1041 default:
1042 break;
1043 } /* switch */
1044 else
1045 /* otherwise parse entire Composite Character info section, */
1046 /* saving the data */
1047 switch (recognize (keyword))
1049 case COMMENT:
1050 keyword = linetoken (fp);
1051 break;
1052 case COMPCHAR:
1053 if (ccount < fi->numOfComps)
1055 keyword = token (fp);
1056 if (pcount != fi->ccd[pos].numOfPieces)
1057 error = AFM_parseError;
1058 pcount = 0;
1059 if (firstTime) firstTime = FALSE;
1060 else pos++;
1061 fi->ccd[pos].ccName = (char *)
1062 malloc (strlen (keyword) + 1);
1063 strcpy (fi->ccd[pos].ccName, keyword);
1064 keyword = token (fp);
1065 fi->ccd[pos].numOfPieces = atoi (keyword);
1066 fi->ccd[pos].pieces = (AFM_Pcc *)
1067 calloc (fi->ccd[pos].numOfPieces, sizeof (AFM_Pcc));
1068 j = 0;
1069 ccount++;
1071 else
1073 error = AFM_parseError;
1074 cont = FALSE;
1076 break;
1077 case COMPCHARPIECE:
1078 if (pcount < fi->ccd[pos].numOfPieces)
1080 keyword = token (fp);
1081 fi->ccd[pos].pieces[j].AFM_PccName = (char *)
1082 malloc (strlen (keyword) + 1);
1083 strcpy (fi->ccd[pos].pieces[j].AFM_PccName, keyword);
1084 keyword = token (fp);
1085 fi->ccd[pos].pieces[j].deltax = atoi (keyword);
1086 keyword = token (fp);
1087 fi->ccd[pos].pieces[j++].deltay = atoi (keyword);
1088 pcount++;
1090 else
1091 error = AFM_parseError;
1092 break;
1093 case ENDCOMPOSITES:
1094 cont = FALSE;
1095 break;
1096 case ENDFONTMETRICS:
1097 cont = FALSE;
1098 error = normalEOF;
1099 break;
1100 case NOPE:
1101 default:
1102 error = AFM_parseError;
1103 break;
1104 } /* switch */
1105 } /* while */
1107 if (error == AFM_ok && ccount != fi->numOfComps)
1108 error = AFM_parseError;
1110 return (error);
1111 } /* parseAFM_CompCharData */
1113 /*************************** 'PUBLIC' FUNCTION ********************/
1115 void
1116 AFM_free (AFM_Font_info *fi)
1118 if (fi->gfi)
1120 free (fi->gfi->afmVersion);
1121 free (fi->gfi->fontName);
1122 free (fi->gfi->fullName);
1123 free (fi->gfi->familyName);
1124 free (fi->gfi->weight);
1125 free (fi->gfi->version);
1126 free (fi->gfi->notice);
1127 free (fi->gfi->encodingScheme);
1128 free (fi->gfi);
1131 /* This contains just scalars. */
1132 free (fi->cwi);
1134 if (fi->cmi)
1136 int i;
1137 for (i = 0; i < fi->numOfChars; i++)
1139 free (fi->cmi[i].name);
1140 while (fi->cmi[i].ligs)
1142 AFM_Ligature *tmp;
1143 tmp = fi->cmi[i].ligs;
1144 free (tmp->succ);
1145 free (tmp->lig);
1146 free (tmp);
1147 fi->cmi[i].ligs = fi->cmi[i].ligs->next;
1150 free (fi->cmi);
1153 /* This contains just scalars. */
1154 free (fi->tkd);
1156 if (fi->pkd)
1158 int i;
1159 for (i = 0; i < fi->numOfPairs; i++)
1161 free (fi->pkd[i].name1);
1162 free (fi->pkd[i].name2);
1164 free (fi->pkd);
1167 if (fi->ccd)
1169 int i, j;
1170 for (i = 0; i < fi->numOfComps; i++)
1172 free (fi->ccd[i].ccName);
1173 for (j = 0; j < fi->ccd[i].numOfPieces; j++)
1175 free (fi->ccd[i].pieces[j].AFM_PccName);
1177 free (fi->ccd[i].pieces);
1179 free (fi->ccd);
1182 free (fi);
1185 /*************************** parseFile *****************************/
1187 /* parseFile is the only 'public' procedure available. It is called
1188 * from an application wishing to get information from an AFM file.
1189 * The caller of this function is responsible for locating and opening
1190 * an AFM file and handling all errors associated with that task.
1192 * parseFile expects 3 parameters: a vaild file pointer, a pointer
1193 * to a (FontInfo *) variable (for which storage will be allocated and
1194 * the data requested filled in), and a mask specifying which
1195 * data from the AFM File should be saved in the FontInfo structure.
1197 * The file will be parsed and the requested data will be stored in
1198 * a record of type FontInfo (refer to ParseAFM.h).
1200 * parseFile returns an error code as defined in parseAFM.h.
1202 * The position of the read/write pointer associated with the file
1203 * pointer upon return of this function is undefined.
1207 AFM_parseFile (FILE *fp, AFM_Font_info **fi, int flags)
1210 int code = AFM_ok; /* return code from each of the parsing routines */
1211 int error = AFM_ok; /* used as the return code from this function */
1213 register char *keyword; /* used to store a token */
1215 /* storage data for the global variable ident */
1216 if (!ident)
1217 ident = (char *) calloc (MAX_NAME, sizeof (char));
1218 if (ident == NULL)
1220 error = AFM_storageProblem;
1221 return error;
1224 (*fi) = (AFM_Font_info *) calloc (1, sizeof (AFM_Font_info));
1225 if ((*fi) == NULL)
1227 error = AFM_storageProblem;
1228 return error;
1231 if (flags & P_G)
1233 (*fi)->gfi = (AFM_GlobalFontInfo *) calloc (1,
1234 sizeof (AFM_GlobalFontInfo));
1235 if ((*fi)->gfi == NULL)
1237 error = AFM_storageProblem;
1238 return error;
1242 /* The AFM File begins with Global Font Information. This section */
1243 /* will be parsed whether or not information should be saved. */
1244 code = parseGlobals (fp, (*fi)->gfi);
1246 if (code < 0)
1247 error = code;
1249 /* The Global Font Information is followed by the Character Metrics */
1250 /* section. Which procedure is used to parse this section depends on */
1251 /* how much information should be saved. If all of the metrics info */
1252 /* is wanted, parseCharMetrics is called. If only the character widths */
1253 /* is wanted, parseCharWidths is called. parseCharWidths will also */
1254 /* be called in the case that no character data is to be saved, just */
1255 /* to parse through the section. */
1257 if ((code != normalEOF) && (code != AFM_earlyEOF))
1259 (*fi)->numOfChars = atoi (token (fp));
1260 if (flags & (P_M ^ P_W))
1262 (*fi)->cmi = (AFM_CharMetricInfo *)
1263 calloc ((*fi)->numOfChars, sizeof (AFM_CharMetricInfo));
1264 if ((*fi)->cmi == NULL)
1266 error = AFM_storageProblem;
1267 return error;
1269 code = parseCharMetrics (fp, *fi);
1271 else
1273 if (flags & P_W)
1275 (*fi)->cwi = (int *) calloc (256, sizeof (int));
1276 if ((*fi)->cwi == NULL)
1278 error = AFM_storageProblem;
1279 return (error);
1282 /* parse section regardless */
1283 code = parseCharWidths (fp, (*fi)->cwi);
1284 } /* else */
1285 } /* if */
1287 if ((error != AFM_earlyEOF) && (code < 0))
1288 error = code;
1290 /* The remaining sections of the AFM are optional. This code will */
1291 /* look at the next keyword in the file to determine what section */
1292 /* is next, and then allocate the appropriate amount of storage */
1293 /* for the data (if the data is to be saved) and call the */
1294 /* appropriate parsing routine to parse the section. */
1296 while ((code != normalEOF) && (code != AFM_earlyEOF))
1298 keyword = token (fp);
1299 if (keyword == NULL)
1300 /* Have reached an early and unexpected EOF. */
1301 /* Set flag and stop parsing */
1303 code = AFM_earlyEOF;
1304 break; /* get out of loop */
1306 switch (recognize (keyword))
1308 case STARTKERNDATA:
1309 break;
1310 case ENDKERNDATA:
1311 break;
1312 case STARTTRACKKERN:
1313 keyword = token (fp);
1314 if (flags & P_T)
1316 (*fi)->numOfTracks = atoi (keyword);
1317 (*fi)->tkd = (AFM_TrackKernData *)
1318 calloc ((*fi)->numOfTracks, sizeof (AFM_TrackKernData));
1319 if ((*fi)->tkd == NULL)
1321 error = AFM_storageProblem;
1322 return (error);
1324 } /* if */
1325 code = parseAFM_TrackKernData (fp, *fi);
1326 break;
1327 case STARTKERNPAIRS:
1328 keyword = token (fp);
1329 if (flags & P_P)
1331 (*fi)->numOfPairs = atoi (keyword);
1332 (*fi)->pkd = (AFM_PairKernData *)
1333 calloc ((*fi)->numOfPairs, sizeof (AFM_PairKernData));
1334 if ((*fi)->pkd == NULL)
1336 error = AFM_storageProblem;
1337 return (error);
1339 } /* if */
1340 code = parseAFM_PairKernData (fp, *fi);
1341 break;
1342 case STARTCOMPOSITES:
1343 keyword = token (fp);
1344 if (flags & P_C)
1346 (*fi)->numOfComps = atoi (keyword);
1347 (*fi)->ccd = (AFM_CompCharData *)
1348 calloc ((*fi)->numOfComps, sizeof (AFM_CompCharData));
1349 if ((*fi)->ccd == NULL)
1351 error = AFM_storageProblem;
1352 return (error);
1354 } /* if */
1355 code = parseAFM_CompCharData (fp, *fi);
1356 break;
1357 case ENDFONTMETRICS:
1358 code = normalEOF;
1359 break;
1360 case NOPE:
1361 default:
1362 code = AFM_parseError;
1363 break;
1364 } /* switch */
1366 if ((error != AFM_earlyEOF) && (code < 0))
1367 error = code;
1368 } /* while */
1370 if ((error != AFM_earlyEOF) && (code < 0))
1371 error = code;
1373 if (ident != NULL) { free (ident); ident = NULL; }
1375 return (error);
1376 } /* parseFile */