* lily/text-item.cc (interpret_string): insert encoding setting
[lilypond.git] / flower / parse-afm.cc
blob874371a42bd8c7f5e50a35cf0972657df25d4765
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"
78 #include "warn.hh"
80 #define METATYPE1_BUG /* Parse Metatype1's (version unknown)
81 'Generated' global tag as comment. */
83 #define lineterm EOL /* line terminating character */
84 #define lineterm_alt '\r' /* alternative line terminating character */
85 #define normalEOF 1 /* return code from parsing routines used only */
86 /* in this module */
87 #define Space "space" /* used in string comparison to look for the width */
88 /* of the space character to init the widths array */
89 #define False "false" /* used in string comparison to check the value of */
90 /* boolean keys (e.g. IsFixedPitch) */
92 #define MATCH(A,B) (strncmp ((A), (B), MAX_NAME) == 0)
96 /*************************** GLOBALS ***********************/
98 static char *ident = NULL; /* storage buffer for keywords */
101 /* "shorts" for fast case statement
102 * The values of each of these enumerated items correspond to an entry in the
103 * table of strings defined below. Therefore, if you add a new string as
104 * new keyword into the keyStrings table, you must also add a corresponding
105 * parseKey AND it MUST be in the same position!
107 * IMPORTANT: since the sorting algorithm is a binary search, the strings of
108 * keywords must be placed in lexicographical order, below. [Therefore, the
109 * enumerated items are not necessarily in lexicographical order, depending
110 * on the name chosen. BUT, they must be placed in the same position as the
111 * corresponding key string.] The NOPE shall remain in the last position,
112 * since it does not correspond to any key string, and it is used in the
113 * "recognize" procedure to calculate how many possible keys there are.
116 enum parseKey {
117 ASCENDER, CHARBBOX, CODE, COMPCHAR, CAPHEIGHT, COMMENT,
118 DESCENDER, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES,
119 ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN,
120 FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME,
121 #ifdef METATYPE1_BUG
122 GENERATED,
123 #endif
124 ISFIXEDPITCH,
125 ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, CHARNAME,
126 NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES,
127 STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS,
128 STARTTRACKKERN, TRACKKERN, UNDERLINEPOSITION,
129 UNDERLINETHICKNESS, VERSION, XYWIDTH, XWIDTH, WEIGHT, XHEIGHT,
130 NOPE };
132 /* keywords for the system:
133 * This a table of all of the current strings that are vaild AFM keys.
134 * Each entry can be referenced by the appropriate parseKey value (an
135 * enumerated data type defined above). If you add a new keyword here,
136 * a corresponding parseKey MUST be added to the enumerated data type
137 * defined above, AND it MUST be added in the same position as the
138 * string is in this table.
140 * IMPORTANT: since the sorting algorithm is a binary search, the keywords
141 * must be placed in lexicographical order. And, NULL should remain at the
142 * end.
145 static char *keyStrings[] = {
146 "Ascender", "B", "C", "CC", "CapHeight", "Comment",
147 "Descender", "EncodingScheme", "EndCharMetrics", "EndComposites",
148 "EndFontMetrics", "EndKernData", "EndKernPairs", "EndTrackKern",
149 "FamilyName", "FontBBox", "FontName", "FullName",
150 #ifdef METATYPE1_BUG
151 "Generated",
152 #endif
153 "IsFixedPitch",
154 "ItalicAngle", "KP", "KPX", "L", "N",
155 "Notice", "PCC", "StartCharMetrics", "StartComposites",
156 "StartFontMetrics", "StartKernData", "StartKernPairs",
157 "StartTrackKern", "TrackKern", "UnderlinePosition",
158 "UnderlineThickness", "Version", "W", "WX", "Weight", "XHeight",
159 NULL };
161 /*************************** PARSING ROUTINES **************/
163 /*************************** token *************************/
165 /* A "AFM File Conventions" tokenizer. That means that it will
166 * return the next token delimited by white space. See also
167 * the `linetoken' routine, which does a similar thing but
168 * reads all tokens until the next end-of-line.
171 static char*
172 token (FILE *stream)
174 int ch, idx;
176 /* skip over white space */
177 while ((ch = fgetc (stream)) == ' ' || ch == lineterm ||
178 ch == lineterm_alt ||
179 ch == ',' || ch == '\t' || ch == ';');
181 idx = 0;
182 while (idx < MAX_NAME - 1 &&
183 ch != EOF && ch != ' ' && ch != lineterm && ch != lineterm_alt
184 && ch != '\t' && ch != ':' && ch != ';')
186 ident[idx++] = ch;
187 ch = fgetc (stream);
188 } /* while */
190 if (ch == EOF && idx < 1) return ((char *)NULL);
191 if (idx >= 1 && ch != ':' ) ungetc (ch, stream);
192 if (idx < 1 ) ident[idx++] = ch; /* single-character token */
193 ident[idx] = 0;
195 return (ident); /* returns pointer to the token */
197 } /* token */
200 /*************************** linetoken *************************/
202 /* "linetoken" will get read all tokens until the EOL character from
203 * the given stream. This is used to get any arguments that can be
204 * more than one word (like Comment lines and FullName).
207 static char*
208 linetoken (FILE *stream)
210 int ch, idx;
212 while ((ch = fgetc (stream)) == ' ' || ch == '\t' );
214 idx = 0;
215 while (idx < MAX_NAME - 1 &&
216 ch != EOF && ch != lineterm && ch != lineterm_alt)
218 ident[idx++] = ch;
219 ch = fgetc (stream);
220 } /* while */
222 ungetc (ch, stream);
223 ident[idx] = 0;
225 return (ident); /* returns pointer to the token */
227 } /* linetoken */
230 /*************************** recognize *************************/
232 /* This function tries to match a string to a known list of
233 * valid AFM entries (check the keyStrings array above).
234 * "ident" contains everything from white space through the
235 * next space, tab, or ":" character.
237 * The algorithm is a standard Knuth binary search.
240 static enum parseKey
241 recognize (register char *ident)
243 int lower = 0,
244 upper = (int) NOPE,
245 midpoint = 0,
246 cmpvalue = 0;
247 BOOL found = FALSE;
249 while ((upper >= lower) && !found)
251 midpoint = (lower + upper)/2;
252 if (keyStrings[midpoint] == NULL)
253 break;
254 cmpvalue = strncmp (ident, keyStrings[midpoint], MAX_NAME);
255 if (cmpvalue == 0)
256 found = TRUE;
257 else
258 if (cmpvalue < 0)
259 upper = midpoint - 1;
260 else
261 lower = midpoint + 1;
264 if (found)
265 return (enum parseKey) midpoint;
266 else
267 return NOPE;
271 /************************* parseGlobals *****************************/
273 /* This function is called by "parseFile". It will parse the AFM File
274 * up to the "StartCharMetrics" keyword, which essentially marks the
275 * end of the Global Font Information and the beginning of the character
276 * metrics information.
278 * If the caller of "parseFile" specified that it wanted the Global
279 * Font Information (as defined by the "AFM File Specification"
280 * document), then that information will be stored in the returned
281 * data structure.
283 * Any Global Font Information entries that are not found in a
284 * given file, will have the usual default initialization value
285 * for its type (i.e. entries of type int will be 0, etc).
287 * This function returns an error code specifying whether there was
288 * a premature EOF or a parsing error. This return value is used by
289 * parseFile to determine if there is more file to parse.
292 static BOOL
293 parseGlobals (FILE *fp, register AFM_GlobalFontInfo *gfi)
295 BOOL cont = TRUE, save = (gfi != NULL);
296 int error = AFM_ok;
297 register char *keyword;
299 while (cont)
301 keyword = token (fp);
303 if (keyword == NULL)
304 /* Have reached an early and unexpected EOF. */
305 /* Set flag and stop parsing */
307 error = AFM_earlyEOF;
308 break; /* get out of loop */
310 if (!save)
311 /* get tokens until the end of the Global Font info section */
312 /* without saving any of the data */
313 switch (recognize (keyword))
315 case STARTCHARMETRICS:
316 cont = FALSE;
317 break;
318 case ENDFONTMETRICS:
319 cont = FALSE;
320 error = normalEOF;
321 break;
322 default:
323 break;
324 } /* switch */
325 else
326 /* otherwise parse entire global font info section, */
327 /* saving the data */
328 switch (recognize (keyword))
330 case STARTFONTMETRICS:
331 keyword = token (fp);
332 gfi->afmVersion = (char *) malloc (strlen (keyword) + 1);
333 strcpy (gfi->afmVersion, keyword);
334 break;
335 case COMMENT:
336 #ifdef METATYPE1_BUG
337 case GENERATED:
338 #endif
339 keyword = linetoken (fp);
340 break;
341 case FONTNAME:
342 keyword = token (fp);
343 gfi->fontName = (char *) malloc (strlen (keyword) + 1);
344 strcpy (gfi->fontName, keyword);
345 break;
346 case ENCODINGSCHEME:
347 keyword = token (fp);
348 gfi->encodingScheme = (char *)
349 malloc (strlen (keyword) + 1);
350 strcpy (gfi->encodingScheme, keyword);
351 break;
352 case FULLNAME:
353 keyword = linetoken (fp);
354 gfi->fullName = (char *) malloc (strlen (keyword) + 1);
355 strcpy (gfi->fullName, keyword);
356 break;
357 case FAMILYNAME:
358 keyword = linetoken (fp);
359 gfi->familyName = (char *) malloc (strlen (keyword) + 1);
360 strcpy (gfi->familyName, keyword);
361 break;
362 case WEIGHT:
363 keyword = token (fp);
364 gfi->weight = (char *) malloc (strlen (keyword) + 1);
365 strcpy (gfi->weight, keyword);
366 break;
367 case ITALICANGLE:
368 keyword = token (fp);
369 gfi->italicAngle = atof (keyword);
370 if (errno == ERANGE) error = AFM_parseError;
371 break;
372 case ISFIXEDPITCH:
373 keyword = token (fp);
374 if (MATCH (keyword, False))
375 gfi->isFixedPitch = 0;
376 else
377 gfi->isFixedPitch = 1;
378 break;
379 case UNDERLINEPOSITION:
380 keyword = token (fp);
381 gfi->underlinePosition = atoi (keyword);
382 break;
383 case UNDERLINETHICKNESS:
384 keyword = token (fp);
385 gfi->underlineThickness = atoi (keyword);
386 break;
387 case VERSION:
388 keyword = linetoken (fp);
389 gfi->version = (char *) malloc (strlen (keyword) + 1);
390 strcpy (gfi->version, keyword);
391 break;
392 case NOTICE:
393 keyword = linetoken (fp);
394 gfi->notice = (char *) malloc (strlen (keyword) + 1);
395 strcpy (gfi->notice, keyword);
396 break;
397 case FONTBBOX:
398 keyword = token (fp);
399 gfi->fontBBox.llx = atoi (keyword);
400 keyword = token (fp);
401 gfi->fontBBox.lly = atoi (keyword);
402 keyword = token (fp);
403 gfi->fontBBox.urx = atoi (keyword);
404 keyword = token (fp);
405 gfi->fontBBox.ury = atoi (keyword);
406 break;
407 case CAPHEIGHT:
408 keyword = token (fp);
409 gfi->capHeight = atoi (keyword);
410 break;
411 case XHEIGHT:
412 keyword = token (fp);
413 gfi->xHeight = atoi (keyword);
414 break;
415 case DESCENDER:
416 keyword = token (fp);
417 gfi->descender = atoi (keyword);
418 break;
419 case ASCENDER:
420 keyword = token (fp);
421 gfi->ascender = atoi (keyword);
422 break;
423 case STARTCHARMETRICS:
424 cont = FALSE;
425 break;
426 case ENDFONTMETRICS:
427 cont = FALSE;
428 error = normalEOF;
429 break;
430 case NOPE:
431 default:
432 error = AFM_parseError;
433 break;
434 } /* switch */
435 } /* while */
437 return (error);
439 } /* parseGlobals */
442 #if 0
443 /************************* initializeArray ************************/
445 /* Unmapped character codes are (at Adobe Systems) assigned the
446 * width of the space character (if one exists) else they get the
447 * value of 250 ems. This function initializes all entries in the
448 * char widths array to have this value. Then any mapped character
449 * codes will be replaced with the width of the appropriate character
450 * when parsing the character metric section.
452 * This function parses the Character Metrics Section looking
453 * for a space character (by comparing character names). If found,
454 * the width of the space character will be used to initialize the
455 * values in the array of character widths.
457 * Before returning, the position of the read/write pointer of the
458 * file is reset to be where it was upon entering this function.
461 static int
462 initializeArray (FILE *fp, register int *cwi)
464 BOOL cont = TRUE, found = FALSE;
465 long opos = ftell (fp);
466 int code = 0, width = 0, i = 0, error = 0;
467 register char *keyword;
469 while (cont)
471 keyword = token (fp);
472 if (keyword == NULL)
474 error = AFM_earlyEOF;
475 break; /* get out of loop */
477 switch (recognize (keyword))
479 case COMMENT:
480 keyword = linetoken (fp);
481 break;
482 case CODE:
483 code = atoi (token (fp));
484 break;
485 case XWIDTH:
486 width = atoi (token (fp));
487 break;
488 case CHARNAME:
489 keyword = token (fp);
490 if (MATCH (keyword, Space))
492 cont = FALSE;
493 found = TRUE;
495 break;
496 case ENDCHARMETRICS:
497 cont = FALSE;
498 break;
499 case ENDFONTMETRICS:
500 cont = FALSE;
501 error = normalEOF;
502 break;
503 case NOPE:
504 default:
505 error = AFM_parseError;
506 break;
507 } /* switch */
508 } /* while */
510 if (!found)
511 width = 250;
513 for (i = 0; i < 256; ++i)
514 cwi[i] = width;
516 fseek (fp, opos, 0);
518 return (error);
520 } /* initializeArray */
521 #endif
523 /************************* parseCharWidths **************************/
525 /* This function is called by "parseFile". It will parse the AFM File
526 * up to the "EndCharMetrics" keyword. It will save the character
527 * width info (as opposed to all of the character metric information)
528 * if requested by the caller of parseFile. Otherwise, it will just
529 * parse through the section without saving any information.
531 * If data is to be saved, parseCharWidths is passed in a pointer
532 * to an array of widths that has already been initialized by the
533 * standard value for unmapped character codes. This function parses
534 * the Character Metrics section only storing the width information
535 * for the encoded characters into the array using the character code
536 * as the index into that array.
538 * This function returns an error code specifying whether there was
539 * a premature EOF or a parsing error. This return value is used by
540 * parseFile to determine if there is more file to parse.
543 static int
544 parseCharWidths (FILE *fp, register int *cwi)
546 BOOL cont = TRUE, save = (cwi != NULL);
547 int pos = 0, error = AFM_ok;
548 register char *keyword;
550 while (cont)
552 keyword = token (fp);
553 /* Have reached an early and unexpected EOF. */
554 /* Set flag and stop parsing */
555 if (keyword == NULL)
557 error = AFM_earlyEOF;
558 break; /* get out of loop */
560 if (!save)
561 /* get tokens until the end of the Char Metrics section without */
562 /* saving any of the data*/
563 switch (recognize (keyword))
565 case ENDCHARMETRICS:
566 cont = FALSE;
567 break;
568 case ENDFONTMETRICS:
569 cont = FALSE;
570 error = normalEOF;
571 break;
572 default:
573 break;
574 } /* switch */
575 else
576 /* otherwise parse entire char metrics section, saving */
577 /* only the char x-width info */
578 switch (recognize (keyword))
580 case COMMENT:
581 keyword = linetoken (fp);
582 break;
583 case CODE:
584 keyword = token (fp);
585 pos = atoi (keyword);
586 break;
587 case XYWIDTH:
588 /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
589 keyword = token (fp); keyword = token (fp); /* eat values */
590 error = AFM_parseError;
591 break;
592 case XWIDTH:
593 keyword = token (fp);
594 if (pos >= 0) /* ignore unmapped chars */
595 cwi[pos] = atoi (keyword);
596 break;
597 case ENDCHARMETRICS:
598 cont = FALSE;
599 break;
600 case ENDFONTMETRICS:
601 cont = FALSE;
602 error = normalEOF;
603 break;
604 case CHARNAME: /* eat values (so doesn't cause AFM_parseError) */
605 keyword = token (fp);
606 break;
607 case CHARBBOX:
608 keyword = token (fp); keyword = token (fp);
609 keyword = token (fp); keyword = token (fp);
610 break;
611 case LIGATURE:
612 keyword = token (fp); keyword = token (fp);
613 break;
614 case NOPE:
615 default:
616 error = AFM_parseError;
617 break;
618 } /* switch */
619 } /* while */
621 return (error);
623 } /* parseCharWidths */
626 /************************* parseCharMetrics ************************/
628 /* This function is called by parseFile if the caller of parseFile
629 * requested that all character metric information be saved
630 * (as opposed to only the character width information).
632 * parseCharMetrics is passed in a pointer to an array of records
633 * to hold information on a per character basis. This function
634 * parses the Character Metrics section storing all character
635 * metric information for the ALL characters (mapped and unmapped)
636 * into the array.
638 * This function returns an error code specifying whether there was
639 * a premature EOF or a parsing error. This return value is used by
640 * parseFile to determine if there is more file to parse.
643 static int
644 parseCharMetrics (FILE *fp, register AFM_Font_info *fi)
646 BOOL cont = TRUE, firstTime = TRUE;
647 int error = AFM_ok, count = 0;
648 register AFM_CharMetricInfo *temp = fi->cmi;
649 register char *keyword;
651 while (cont)
653 keyword = token (fp);
654 if (keyword == NULL)
656 error = AFM_earlyEOF;
657 break; /* get out of loop */
659 switch (recognize (keyword))
661 case COMMENT:
662 keyword = linetoken (fp);
663 break;
664 case CODE:
665 if (count < fi->numOfChars)
667 if (firstTime)
668 firstTime = FALSE;
669 else
670 temp++;
671 temp->code = atoi (token (fp));
672 count++;
674 else
676 warning ("Too many metrics.");
677 error = AFM_parseError;
678 cont = FALSE;
680 break;
681 case XYWIDTH:
682 temp->wx = atoi (token (fp));
683 temp->wy = atoi (token (fp));
684 break;
685 case XWIDTH:
686 temp->wx = atoi (token (fp));
687 break;
689 case CHARNAME:
690 keyword = token (fp);
691 temp->name = (char *) malloc (strlen (keyword) + 1);
692 strcpy (temp->name, keyword);
693 break;
695 case CHARBBOX:
696 temp->charBBox.llx = atoi (token (fp));
697 temp->charBBox.lly = atoi (token (fp));
698 temp->charBBox.urx = atoi (token (fp));
699 temp->charBBox.ury = atoi (token (fp));
700 break;
702 case LIGATURE: {
703 AFM_Ligature **tail = & (temp->ligs);
704 AFM_Ligature *node = *tail;
706 if (*tail != NULL)
708 while (node->next != NULL)
709 node = node->next;
710 tail = & (node->next);
713 *tail = (AFM_Ligature *) calloc (1, sizeof (AFM_Ligature));
714 keyword = token (fp);
715 (*tail)->succ = (char *) malloc (strlen (keyword) + 1);
716 strcpy ((*tail)->succ, keyword);
717 keyword = token (fp);
718 (*tail)->lig = (char *) malloc (strlen (keyword) + 1);
719 strcpy ((*tail)->lig, keyword);
720 break; }
721 case ENDCHARMETRICS:
722 cont = FALSE;;
723 break;
724 case ENDFONTMETRICS:
725 cont = FALSE;
726 error = normalEOF;
727 break;
728 case NOPE:
729 default:
730 warning ("Unknown token");
732 error = AFM_parseError;
733 break;
734 } /* switch */
735 } /* while */
737 if ((error == AFM_ok) && (count != fi->numOfChars))
739 warning ("Incorrect char count");
740 error = AFM_parseError;
742 return (error);
744 } /* parseCharMetrics */
748 /************************* parseAFM_TrackKernData ***********************/
750 /* This function is called by "parseFile". It will parse the AFM File
751 * up to the "EndTrackKern" or "EndKernData" keywords. It will save the
752 * track kerning data if requested by the caller of parseFile.
754 * parseAFM_TrackKernData is passed in a pointer to the FontInfo record.
755 * If data is to be saved, the FontInfo record will already contain
756 * a valid pointer to storage for the track kerning data.
758 * This function returns an error code specifying whether there was
759 * a premature EOF or a parsing error. This return value is used by
760 * parseFile to determine if there is more file to parse.
763 static int
764 parseAFM_TrackKernData (FILE *fp, register AFM_Font_info *fi)
766 BOOL cont = TRUE, save = (fi->tkd != NULL);
767 int pos = 0, error = AFM_ok, tcount = 0;
768 register char *keyword;
770 while (cont)
772 keyword = token (fp);
774 if (keyword == NULL)
776 error = AFM_earlyEOF;
777 break; /* get out of loop */
779 if (!save)
780 /* get tokens until the end of the Track Kerning Data */
781 /* section without saving any of the data */
782 switch (recognize (keyword))
784 case ENDTRACKKERN:
785 case ENDKERNDATA:
786 cont = FALSE;
787 break;
788 case ENDFONTMETRICS:
789 cont = FALSE;
790 error = normalEOF;
791 break;
792 default:
793 break;
794 } /* switch */
795 else
796 /* otherwise parse entire Track Kerning Data section, */
797 /* saving the data */
798 switch (recognize (keyword))
800 case COMMENT:
801 #ifdef METATYPE1_BUG
802 case GENERATED:
803 #endif
804 keyword = linetoken (fp);
805 break;
806 case TRACKKERN:
807 if (tcount < fi->numOfTracks)
809 keyword = token (fp);
810 fi->tkd[pos].degree = atoi (keyword);
811 keyword = token (fp);
812 fi->tkd[pos].minPtSize = atof (keyword);
813 if (errno == ERANGE) error = AFM_parseError;
814 keyword = token (fp);
815 fi->tkd[pos].minKernAmt = atof (keyword);
816 if (errno == ERANGE) error = AFM_parseError;
817 keyword = token (fp);
818 fi->tkd[pos].maxPtSize = atof (keyword);
819 if (errno == ERANGE) error = AFM_parseError;
820 keyword = token (fp);
821 fi->tkd[pos++].maxKernAmt = atof (keyword);
822 if (errno == ERANGE) error = AFM_parseError;
823 tcount++;
825 else
827 error = AFM_parseError;
828 cont = FALSE;
830 break;
831 case ENDTRACKKERN:
832 case ENDKERNDATA:
833 cont = FALSE;
834 break;
835 case ENDFONTMETRICS:
836 cont = FALSE;
837 error = normalEOF;
838 break;
839 case NOPE:
840 default:
841 error = AFM_parseError;
842 break;
843 } /* switch */
844 } /* while */
846 if (error == AFM_ok && tcount != fi->numOfTracks)
847 error = AFM_parseError;
849 return (error);
851 } /* parseAFM_TrackKernData */
854 /************************* parseAFM_PairKernData ************************/
856 /* This function is called by "parseFile". It will parse the AFM File
857 * up to the "EndKernPairs" or "EndKernData" keywords. It will save
858 * the pair kerning data if requested by the caller of parseFile.
860 * parseAFM_PairKernData is passed in a pointer to the FontInfo record.
861 * If data is to be saved, the FontInfo record will already contain
862 * a valid pointer to storage for the pair kerning data.
864 * This function returns an error code specifying whether there was
865 * a premature EOF or a parsing error. This return value is used by
866 * parseFile to determine if there is more file to parse.
869 static int
870 parseAFM_PairKernData (FILE *fp, register AFM_Font_info *fi)
872 BOOL cont = TRUE, save = (fi->pkd != NULL);
873 int pos = 0, error = AFM_ok, pcount = 0;
874 register char *keyword;
876 while (cont)
878 keyword = token (fp);
880 if (keyword == NULL)
882 error = AFM_earlyEOF;
883 break; /* get out of loop */
885 if (!save)
886 /* get tokens until the end of the Pair Kerning Data */
887 /* section without saving any of the data */
888 switch (recognize (keyword))
890 case ENDKERNPAIRS:
891 case ENDKERNDATA:
892 cont = FALSE;
893 break;
894 case ENDFONTMETRICS:
895 cont = FALSE;
896 error = normalEOF;
897 break;
898 default:
899 break;
900 } /* switch */
901 else
902 /* otherwise parse entire Pair Kerning Data section, */
903 /* saving the data */
904 switch (recognize (keyword))
906 case COMMENT:
907 keyword = linetoken (fp);
908 break;
909 case KERNPAIR:
910 if (pcount < fi->numOfPairs)
912 keyword = token (fp);
913 fi->pkd[pos].name1 = (char *)
914 malloc (strlen (keyword) + 1);
915 strcpy (fi->pkd[pos].name1, keyword);
916 keyword = token (fp);
917 fi->pkd[pos].name2 = (char *)
918 malloc (strlen (keyword) + 1);
919 strcpy (fi->pkd[pos].name2, keyword);
920 keyword = token (fp);
921 fi->pkd[pos].xamt = atoi (keyword);
922 keyword = token (fp);
923 fi->pkd[pos++].yamt = atoi (keyword);
924 pcount++;
926 else
928 error = AFM_parseError;
929 cont = FALSE;
931 break;
932 case KERNPAIRXAMT:
933 if (pcount < fi->numOfPairs)
935 keyword = token (fp);
936 fi->pkd[pos].name1 = (char *)
937 malloc (strlen (keyword) + 1);
938 strcpy (fi->pkd[pos].name1, keyword);
939 keyword = token (fp);
940 fi->pkd[pos].name2 = (char *)
941 malloc (strlen (keyword) + 1);
942 strcpy (fi->pkd[pos].name2, keyword);
943 keyword = token (fp);
944 fi->pkd[pos++].xamt = atoi (keyword);
945 pcount++;
947 else
949 error = AFM_parseError;
950 cont = FALSE;
952 break;
953 case ENDKERNPAIRS:
954 case ENDKERNDATA:
955 cont = FALSE;
956 break;
957 case ENDFONTMETRICS:
958 cont = FALSE;
959 error = normalEOF;
960 break;
961 case NOPE:
962 default:
963 error = AFM_parseError;
964 break;
965 } /* switch */
966 } /* while */
968 if (error == AFM_ok && pcount != fi->numOfPairs)
969 error = AFM_parseError;
971 return (error);
973 } /* parseAFM_PairKernData */
976 /************************* parseAFM_CompCharData **************************/
978 /* This function is called by "parseFile". It will parse the AFM File
979 * up to the "EndComposites" keyword. It will save the composite
980 * character data if requested by the caller of parseFile.
982 * parseAFM_CompCharData is passed in a pointer to the FontInfo record, and
983 * a boolean representing if the data should be saved.
985 * This function will create the appropriate amount of storage for
986 * the composite character data and store a pointer to the storage
987 * in the FontInfo record.
989 * This function returns an error code specifying whether there was
990 * a premature EOF or a parsing error. This return value is used by
991 * parseFile to determine if there is more file to parse.
994 static int
995 parseAFM_CompCharData (FILE *fp, register AFM_Font_info *fi)
997 BOOL cont = TRUE, firstTime = TRUE, save = (fi->ccd != NULL);
998 int pos = 0, j = 0, error = AFM_ok, ccount = 0, pcount = 0;
999 register char *keyword;
1001 while (cont)
1003 keyword = token (fp);
1004 if (keyword == NULL)
1005 /* Have reached an early and unexpected EOF. */
1006 /* Set flag and stop parsing */
1008 error = AFM_earlyEOF;
1009 break; /* get out of loop */
1011 if (ccount > fi->numOfComps)
1013 error = AFM_parseError;
1014 break; /* get out of loop */
1016 if (!save)
1017 /* get tokens until the end of the Composite Character info */
1018 /* section without saving any of the data */
1019 switch (recognize (keyword))
1021 case ENDCOMPOSITES:
1022 cont = FALSE;
1023 break;
1024 case ENDFONTMETRICS:
1025 cont = FALSE;
1026 error = normalEOF;
1027 break;
1028 default:
1029 break;
1030 } /* switch */
1031 else
1032 /* otherwise parse entire Composite Character info section, */
1033 /* saving the data */
1034 switch (recognize (keyword))
1036 case COMMENT:
1037 keyword = linetoken (fp);
1038 break;
1039 case COMPCHAR:
1040 if (ccount < fi->numOfComps)
1042 keyword = token (fp);
1043 if (pcount != fi->ccd[pos].numOfPieces)
1044 error = AFM_parseError;
1045 pcount = 0;
1046 if (firstTime) firstTime = FALSE;
1047 else pos++;
1048 fi->ccd[pos].ccName = (char *)
1049 malloc (strlen (keyword) + 1);
1050 strcpy (fi->ccd[pos].ccName, keyword);
1051 keyword = token (fp);
1052 fi->ccd[pos].numOfPieces = atoi (keyword);
1053 fi->ccd[pos].pieces = (AFM_Pcc *)
1054 calloc (fi->ccd[pos].numOfPieces, sizeof (AFM_Pcc));
1055 j = 0;
1056 ccount++;
1058 else
1060 error = AFM_parseError;
1061 cont = FALSE;
1063 break;
1064 case COMPCHARPIECE:
1065 if (pcount < fi->ccd[pos].numOfPieces)
1067 keyword = token (fp);
1068 fi->ccd[pos].pieces[j].AFM_PccName = (char *)
1069 malloc (strlen (keyword) + 1);
1070 strcpy (fi->ccd[pos].pieces[j].AFM_PccName, keyword);
1071 keyword = token (fp);
1072 fi->ccd[pos].pieces[j].deltax = atoi (keyword);
1073 keyword = token (fp);
1074 fi->ccd[pos].pieces[j++].deltay = atoi (keyword);
1075 pcount++;
1077 else
1078 error = AFM_parseError;
1079 break;
1080 case ENDCOMPOSITES:
1081 cont = FALSE;
1082 break;
1083 case ENDFONTMETRICS:
1084 cont = FALSE;
1085 error = normalEOF;
1086 break;
1087 case NOPE:
1088 default:
1089 error = AFM_parseError;
1090 break;
1091 } /* switch */
1092 } /* while */
1094 if (error == AFM_ok && ccount != fi->numOfComps)
1095 error = AFM_parseError;
1097 return (error);
1099 } /* parseAFM_CompCharData */
1104 /*************************** 'PUBLIC' FUNCTION ********************/
1106 void
1107 AFM_free (AFM_Font_info *fi)
1109 if (fi->gfi) {
1110 free (fi->gfi->afmVersion);
1111 free (fi->gfi->fontName);
1112 free (fi->gfi->fullName);
1113 free (fi->gfi->familyName);
1114 free (fi->gfi->weight);
1115 free (fi->gfi->version);
1116 free (fi->gfi->notice);
1117 free (fi->gfi->encodingScheme);
1118 free (fi->gfi);
1121 /* This contains just scalars. */
1122 free (fi->cwi);
1124 if (fi->cmi) {
1125 int i;
1126 for (i = 0; i < fi->numOfChars; i++) {
1127 free (fi->cmi[i].name);
1128 while (fi->cmi[i].ligs) {
1129 AFM_Ligature *tmp;
1130 tmp = fi->cmi[i].ligs;
1131 free (tmp->succ);
1132 free (tmp->lig);
1133 free (tmp);
1134 fi->cmi[i].ligs = fi->cmi[i].ligs->next;
1137 free (fi->cmi);
1140 /* This contains just scalars. */
1141 free (fi->tkd);
1143 if (fi->pkd) {
1144 int i;
1145 for (i = 0; i < fi->numOfPairs; i++) {
1146 free (fi->pkd[i].name1);
1147 free (fi->pkd[i].name2);
1149 free (fi->pkd);
1152 if (fi->ccd) {
1153 int i, j;
1154 for (i = 0; i < fi->numOfComps; i++) {
1155 free (fi->ccd[i].ccName);
1156 for (j = 0; j < fi->ccd[i].numOfPieces; j++) {
1157 free (fi->ccd[i].pieces[j].AFM_PccName);
1159 free (fi->ccd[i].pieces);
1161 free (fi->ccd);
1164 free (fi);
1168 /*************************** parseFile *****************************/
1170 /* parseFile is the only 'public' procedure available. It is called
1171 * from an application wishing to get information from an AFM file.
1172 * The caller of this function is responsible for locating and opening
1173 * an AFM file and handling all errors associated with that task.
1175 * parseFile expects 3 parameters: a vaild file pointer, a pointer
1176 * to a (FontInfo *) variable (for which storage will be allocated and
1177 * the data requested filled in), and a mask specifying which
1178 * data from the AFM File should be saved in the FontInfo structure.
1180 * The file will be parsed and the requested data will be stored in
1181 * a record of type FontInfo (refer to ParseAFM.h).
1183 * parseFile returns an error code as defined in parseAFM.h.
1185 * The position of the read/write pointer associated with the file
1186 * pointer upon return of this function is undefined.
1190 AFM_parseFile (FILE *fp, AFM_Font_info **fi, int flags)
1193 int code = AFM_ok; /* return code from each of the parsing routines */
1194 int error = AFM_ok; /* used as the return code from this function */
1196 register char *keyword; /* used to store a token */
1199 /* storage data for the global variable ident */
1200 if (!ident)
1201 ident = (char *) calloc (MAX_NAME, sizeof (char));
1202 if (ident == NULL)
1204 error = AFM_storageProblem;
1205 return error;
1208 (*fi) = (AFM_Font_info *) calloc (1, sizeof (AFM_Font_info));
1209 if ((*fi) == NULL)
1211 error = AFM_storageProblem;
1212 return error;
1215 if (flags & P_G)
1217 (*fi)->gfi = (AFM_GlobalFontInfo *) calloc (1,
1218 sizeof (AFM_GlobalFontInfo));
1219 if ((*fi)->gfi == NULL)
1221 error = AFM_storageProblem;
1222 return error;
1226 /* The AFM File begins with Global Font Information. This section */
1227 /* will be parsed whether or not information should be saved. */
1228 code = parseGlobals (fp, (*fi)->gfi);
1230 if (code < 0)
1231 error = code;
1233 /* The Global Font Information is followed by the Character Metrics */
1234 /* section. Which procedure is used to parse this section depends on */
1235 /* how much information should be saved. If all of the metrics info */
1236 /* is wanted, parseCharMetrics is called. If only the character widths */
1237 /* is wanted, parseCharWidths is called. parseCharWidths will also */
1238 /* be called in the case that no character data is to be saved, just */
1239 /* to parse through the section. */
1241 if ((code != normalEOF) && (code != AFM_earlyEOF))
1243 (*fi)->numOfChars = atoi (token (fp));
1244 if (flags & (P_M ^ P_W))
1246 (*fi)->cmi = (AFM_CharMetricInfo *)
1247 calloc ((*fi)->numOfChars, sizeof (AFM_CharMetricInfo));
1248 if ((*fi)->cmi == NULL)
1250 error = AFM_storageProblem;
1251 return error;
1253 code = parseCharMetrics (fp, *fi);
1255 else
1257 if (flags & P_W)
1259 (*fi)->cwi = (int *) calloc (256, sizeof (int));
1260 if ((*fi)->cwi == NULL)
1262 error = AFM_storageProblem;
1263 return (error);
1266 /* parse section regardless */
1267 code = parseCharWidths (fp, (*fi)->cwi);
1268 } /* else */
1269 } /* if */
1271 if ((error != AFM_earlyEOF) && (code < 0))
1272 error = code;
1274 /* The remaining sections of the AFM are optional. This code will */
1275 /* look at the next keyword in the file to determine what section */
1276 /* is next, and then allocate the appropriate amount of storage */
1277 /* for the data (if the data is to be saved) and call the */
1278 /* appropriate parsing routine to parse the section. */
1280 while ((code != normalEOF) && (code != AFM_earlyEOF))
1282 keyword = token (fp);
1283 if (keyword == NULL)
1284 /* Have reached an early and unexpected EOF. */
1285 /* Set flag and stop parsing */
1287 code = AFM_earlyEOF;
1288 break; /* get out of loop */
1290 switch (recognize (keyword))
1292 case STARTKERNDATA:
1293 break;
1294 case ENDKERNDATA:
1295 break;
1296 case STARTTRACKKERN:
1297 keyword = token (fp);
1298 if (flags & P_T)
1300 (*fi)->numOfTracks = atoi (keyword);
1301 (*fi)->tkd = (AFM_TrackKernData *)
1302 calloc ((*fi)->numOfTracks, sizeof (AFM_TrackKernData));
1303 if ((*fi)->tkd == NULL)
1305 error = AFM_storageProblem;
1306 return (error);
1308 } /* if */
1309 code = parseAFM_TrackKernData (fp, *fi);
1310 break;
1311 case STARTKERNPAIRS:
1312 keyword = token (fp);
1313 if (flags & P_P)
1315 (*fi)->numOfPairs = atoi (keyword);
1316 (*fi)->pkd = (AFM_PairKernData *)
1317 calloc ((*fi)->numOfPairs, sizeof (AFM_PairKernData));
1318 if ((*fi)->pkd == NULL)
1320 error = AFM_storageProblem;
1321 return (error);
1323 } /* if */
1324 code = parseAFM_PairKernData (fp, *fi);
1325 break;
1326 case STARTCOMPOSITES:
1327 keyword = token (fp);
1328 if (flags & P_C)
1330 (*fi)->numOfComps = atoi (keyword);
1331 (*fi)->ccd = (AFM_CompCharData *)
1332 calloc ((*fi)->numOfComps, sizeof (AFM_CompCharData));
1333 if ((*fi)->ccd == NULL)
1335 error = AFM_storageProblem;
1336 return (error);
1338 } /* if */
1339 code = parseAFM_CompCharData (fp, *fi);
1340 break;
1341 case ENDFONTMETRICS:
1342 code = normalEOF;
1343 break;
1344 case NOPE:
1345 default:
1346 code = AFM_parseError;
1347 break;
1348 } /* switch */
1350 if ((error != AFM_earlyEOF) && (code < 0))
1351 error = code;
1353 } /* while */
1355 if ((error != AFM_earlyEOF) && (code < 0))
1356 error = code;
1358 if (ident != NULL) { free (ident); ident = NULL; }
1360 return (error);
1362 } /* parseFile */