(dos_to_posix)[__CYGWIN__]: Return
[lilypond/patrick.git] / flower / parse-afm.cc
blobb65665738f310d8e99af89800f6fbbb83642152b
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>
76 using namespace std;
78 #include <sys/file.h>
80 #include "warn.hh"
82 /* your basic constants */
83 #define TRUE 1
84 #define FALSE 0
85 #define EOL '\n' /* end-of-line indicator */
86 #define MAX_NAME 4096 /* max length for identifiers */
88 /* Flags that can be AND'ed together to specify exactly what
89 * information from the AFM file should be saved.
91 #define P_G 0x01 /* 0000 0001 */ /* Global Font Info */
92 #define P_W 0x02 /* 0000 0010 */ /* Character Widths ONLY */
93 #define P_M 0x06 /* 0000 0110 */ /* All Char Metric Info */
94 #define P_P 0x08 /* 0000 1000 */ /* Pair Kerning Info */
95 #define P_T 0x10 /* 0001 0000 */ /* Track Kerning Info */
96 #define P_C 0x20 /* 0010 0000 */ /* Composite Char Info */
98 /* Commonly used flags
100 #define P_GW \
101 (P_G | P_W)
102 #define P_GM \
103 (P_G | P_M)
104 #define P_GMP \
105 (P_G | P_M | P_P)
106 #define P_GMK \
107 (P_G | P_M | P_P | P_T)
108 #define P_GALL \
109 (P_G | P_M | P_P | P_T | P_C)
111 #define METATYPE1_BUG /* Parse Metatype1's (version unknown)
112 'Generated' global tag as comment. */
114 #define lineterm EOL /* line terminating character */
115 #define lineterm_alt '\r' /* alternative line terminating character */
116 #define normalEOF 1 /* return code from parsing routines used only */
117 /* in this module */
118 #define Space "space" /* used in string comparison to look for the width */
119 /* of the space character to init the widths array */
120 #define False "false" /* used in string comparison to check the value of */
121 /* boolean keys (e.g. IsFixedPitch) */
123 #define MATCH(A, B) (strncmp ((A), (B), MAX_NAME) == 0)
125 /*************************** GLOBALS ***********************/
127 static char *ident = NULL; /* storage buffer for keywords */
129 /* "shorts" for fast case statement
130 * The values of each of these enumerated items correspond to an entry in the
131 * table of strings defined below. Therefore, if you add a new string as
132 * new keyword into the keyStrings table, you must also add a corresponding
133 * parseKey AND it MUST be in the same position!
135 * IMPORTANT: since the sorting algorithm is a binary search, the strings of
136 * keywords must be placed in lexicographical order, below. [Therefore, the
137 * enumerated items are not necessarily in lexicographical order, depending
138 * on the name chosen. BUT, they must be placed in the same position as the
139 * corresponding key string.] The NOPE shall remain in the last position,
140 * since it does not correspond to any key string, and it is used in the
141 * "recognize" procedure to calculate how many possible keys there are.
144 enum parseKey
146 ASCENDER, CHARBBOX, CODE, COMPCHAR, 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, KERNPAIR, KERNPAIRXAMT, LIGATURE, CHARNAME,
155 NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES,
156 STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS,
157 STARTTRACKKERN, TRACKKERN, UNDERLINEPOSITION,
158 UNDERLINETHICKNESS, VERSION, XYWIDTH, XWIDTH, WEIGHT, XHEIGHT,
159 NOPE };
161 /* keywords for the system:
162 * This a table of all of the current strings that are vaild AFM keys.
163 * Each entry can be referenced by the appropriate parseKey value (an
164 * enumerated data type defined above). If you add a new keyword here,
165 * a corresponding parseKey MUST be added to the enumerated data type
166 * defined above, AND it MUST be added in the same position as the
167 * string is in this table.
169 * IMPORTANT: since the sorting algorithm is a binary search, the keywords
170 * must be placed in lexicographical order. And, NULL should remain at the
171 * end.
174 static char *keyStrings[]
176 "Ascender", "B", "C", "CC", "CapHeight", "Comment",
177 "Descender", "EncodingScheme", "EndCharMetrics", "EndComposites",
178 "EndFontMetrics", "EndKernData", "EndKernPairs", "EndTrackKern",
179 "FamilyName", "FontBBox", "FontName", "FullName",
180 #ifdef METATYPE1_BUG
181 "Generated",
182 #endif
183 "IsFixedPitch",
184 "ItalicAngle", "KP", "KPX", "L", "N",
185 "Notice", "PCC", "StartCharMetrics", "StartComposites",
186 "StartFontMetrics", "StartKernData", "StartKernPairs",
187 "StartTrackKern", "TrackKern", "UnderlinePosition",
188 "UnderlineThickness", "Version", "W", "WX", "Weight", "XHeight",
189 NULL };
191 /*************************** PARSING ROUTINES **************/
193 /*************************** token *************************/
195 /* A "AFM File Conventions" tokenizer. That means that it will
196 * return the next token delimited by white space. See also
197 * the `linetoken' routine, which does a similar thing but
198 * reads all tokens until the next end-of-line.
201 static char *
202 token (FILE *stream)
204 int ch, idx;
206 /* skip over white space */
207 while ((ch = fgetc (stream)) == ' ' || ch == lineterm
208 || ch == lineterm_alt
209 || ch == ',' || ch == '\t' || ch == ';');
211 idx = 0;
212 while (idx < MAX_NAME - 1
213 && ch != EOF && ch != ' ' && ch != lineterm && ch != lineterm_alt
214 && ch != '\t' && ch != ':' && ch != ';')
216 ident[idx++] = ch;
217 ch = fgetc (stream);
218 } /* while */
220 if (ch == EOF && idx < 1) return ((char *)NULL);
221 if (idx >= 1 && ch != ':') ungetc (ch, stream);
222 if (idx < 1) ident[idx++] = ch; /* single-character token */
223 ident[idx] = 0;
225 return (ident); /* returns pointer to the token */
226 } /* token */
228 /*************************** linetoken *************************/
230 /* "linetoken" will get read all tokens until the EOL character from
231 * the given stream. This is used to get any arguments that can be
232 * more than one word (like Comment lines and FullName).
235 static char *
236 linetoken (FILE *stream)
238 int ch, idx;
240 while ((ch = fgetc (stream)) == ' ' || ch == '\t');
242 idx = 0;
243 while (idx < MAX_NAME - 1
244 && ch != EOF && ch != lineterm && ch != lineterm_alt)
246 ident[idx++] = ch;
247 ch = fgetc (stream);
248 } /* while */
250 ungetc (ch, stream);
251 ident[idx] = 0;
253 return (ident); /* returns pointer to the token */
254 } /* linetoken */
256 /*************************** recognize *************************/
258 /* This function tries to match a string to a known list of
259 * valid AFM entries (check the keyStrings array above).
260 * "ident" contains everything from white space through the
261 * next space, tab, or ":" character.
263 * The algorithm is a standard Knuth binary search.
266 static enum parseKey
267 recognize (register char *ident)
269 int lower = 0,
270 upper = (int) NOPE,
271 midpoint = 0,
272 cmpvalue = 0;
273 BOOL found = FALSE;
275 while ((upper >= lower) && !found)
277 midpoint = (lower + upper) / 2;
278 if (keyStrings[midpoint] == NULL)
279 break;
280 cmpvalue = strncmp (ident, keyStrings[midpoint], MAX_NAME);
281 if (cmpvalue == 0)
282 found = TRUE;
283 else
284 if (cmpvalue < 0)
285 upper = midpoint - 1;
286 else
287 lower = midpoint + 1;
290 if (found)
291 return (enum parseKey) midpoint;
292 else
293 return NOPE;
296 /************************* parseGlobals *****************************/
298 /* This function is called by "parseFile". It will parse the AFM File
299 * up to the "StartCharMetrics" keyword, which essentially marks the
300 * end of the Global Font Information and the beginning of the character
301 * metrics information.
303 * If the caller of "parseFile" specified that it wanted the Global
304 * Font Information (as defined by the "AFM File Specification"
305 * document), then that information will be stored in the returned
306 * data structure.
308 * Any Global Font Information entries that are not found in a
309 * given file, will have the usual default initialization value
310 * for its type (i.e. entries of type int will be 0, etc).
312 * This function returns an error code specifying whether there was
313 * a premature EOF or a parsing error. This return value is used by
314 * parseFile to determine if there is more file to parse.
317 static BOOL
318 parseGlobals (FILE *fp, register AFM_GlobalFontInfo *gfi)
320 BOOL cont = TRUE, save = (gfi != NULL);
321 int error = AFM_ok;
322 register char *keyword;
324 while (cont)
326 keyword = token (fp);
328 if (keyword == NULL)
329 /* Have reached an early and unexpected EOF. */
330 /* Set flag and stop parsing */
332 error = AFM_earlyEOF;
333 break; /* get out of loop */
335 if (!save)
336 /* get tokens until the end of the Global Font info section */
337 /* without saving any of the data */
338 switch (recognize (keyword))
340 case STARTCHARMETRICS:
341 cont = FALSE;
342 break;
343 case ENDFONTMETRICS:
344 cont = FALSE;
345 error = normalEOF;
346 break;
347 default:
348 break;
349 } /* switch */
350 else
351 /* otherwise parse entire global font info section, */
352 /* saving the data */
353 switch (recognize (keyword))
355 case STARTFONTMETRICS:
356 keyword = token (fp);
357 gfi->afmVersion = (char *) malloc (strlen (keyword) + 1);
358 strcpy (gfi->afmVersion, keyword);
359 break;
360 case COMMENT:
361 #ifdef METATYPE1_BUG
362 case GENERATED:
363 #endif
364 keyword = linetoken (fp);
365 break;
366 case FONTNAME:
367 keyword = token (fp);
368 gfi->fontName = (char *) malloc (strlen (keyword) + 1);
369 strcpy (gfi->fontName, keyword);
370 break;
371 case ENCODINGSCHEME:
372 keyword = token (fp);
373 gfi->encodingScheme = (char *)
374 malloc (strlen (keyword) + 1);
375 strcpy (gfi->encodingScheme, keyword);
376 break;
377 case FULLNAME:
378 keyword = linetoken (fp);
379 gfi->fullName = (char *) malloc (strlen (keyword) + 1);
380 strcpy (gfi->fullName, keyword);
381 break;
382 case FAMILYNAME:
383 keyword = linetoken (fp);
384 gfi->familyName = (char *) malloc (strlen (keyword) + 1);
385 strcpy (gfi->familyName, keyword);
386 break;
387 case WEIGHT:
388 keyword = token (fp);
389 gfi->weight = (char *) malloc (strlen (keyword) + 1);
390 strcpy (gfi->weight, keyword);
391 break;
392 case ITALICANGLE:
393 keyword = token (fp);
394 gfi->italicAngle = atof (keyword);
395 if (errno == ERANGE) error = AFM_parseError;
396 break;
397 case ISFIXEDPITCH:
398 keyword = token (fp);
399 if (MATCH (keyword, False))
400 gfi->isFixedPitch = 0;
401 else
402 gfi->isFixedPitch = 1;
403 break;
404 case UNDERLINEPOSITION:
405 keyword = token (fp);
406 gfi->underlinePosition = atoi (keyword);
407 break;
408 case UNDERLINETHICKNESS:
409 keyword = token (fp);
410 gfi->underlineThickness = atoi (keyword);
411 break;
412 case VERSION:
413 keyword = linetoken (fp);
414 gfi->version = (char *) malloc (strlen (keyword) + 1);
415 strcpy (gfi->version, keyword);
416 break;
417 case NOTICE:
418 keyword = linetoken (fp);
419 gfi->notice = (char *) malloc (strlen (keyword) + 1);
420 strcpy (gfi->notice, keyword);
421 break;
422 case FONTBBOX:
423 keyword = token (fp);
424 gfi->fontBBox.llx = atoi (keyword);
425 keyword = token (fp);
426 gfi->fontBBox.lly = atoi (keyword);
427 keyword = token (fp);
428 gfi->fontBBox.urx = atoi (keyword);
429 keyword = token (fp);
430 gfi->fontBBox.ury = atoi (keyword);
431 break;
432 case CAPHEIGHT:
433 keyword = token (fp);
434 gfi->capHeight = atoi (keyword);
435 break;
436 case XHEIGHT:
437 keyword = token (fp);
438 gfi->xHeight = atoi (keyword);
439 break;
440 case DESCENDER:
441 keyword = token (fp);
442 gfi->descender = atoi (keyword);
443 break;
444 case ASCENDER:
445 keyword = token (fp);
446 gfi->ascender = atoi (keyword);
447 break;
448 case STARTCHARMETRICS:
449 cont = FALSE;
450 break;
451 case ENDFONTMETRICS:
452 cont = FALSE;
453 error = normalEOF;
454 break;
455 case NOPE:
456 default:
457 error = AFM_parseError;
458 break;
459 } /* switch */
460 } /* while */
462 return (error);
463 } /* parseGlobals */
465 #if 0
466 /************************* initializeArray ************************/
468 /* Unmapped character codes are (at Adobe Systems) assigned the
469 * width of the space character (if one exists) else they get the
470 * value of 250 ems. This function initializes all entries in the
471 * char widths array to have this value. Then any mapped character
472 * codes will be replaced with the width of the appropriate character
473 * when parsing the character metric section.
475 * This function parses the Character Metrics Section looking
476 * for a space character (by comparing character names). If found,
477 * the width of the space character will be used to initialize the
478 * values in the array of character widths.
480 * Before returning, the position of the read/write pointer of the
481 * file is reset to be where it was upon entering this function.
484 static int
485 initializeArray (FILE *fp, register int *cwi)
487 BOOL cont = TRUE, found = FALSE;
488 long opos = ftell (fp);
489 int code = 0, width = 0, i = 0, error = 0;
490 register char *keyword;
492 while (cont)
494 keyword = token (fp);
495 if (keyword == NULL)
497 error = AFM_earlyEOF;
498 break; /* get out of loop */
500 switch (recognize (keyword))
502 case COMMENT:
503 keyword = linetoken (fp);
504 break;
505 case CODE:
506 code = atoi (token (fp));
507 break;
508 case XWIDTH:
509 width = atoi (token (fp));
510 break;
511 case CHARNAME:
512 keyword = token (fp);
513 if (MATCH (keyword, Space))
515 cont = FALSE;
516 found = TRUE;
518 break;
519 case ENDCHARMETRICS:
520 cont = FALSE;
521 break;
522 case ENDFONTMETRICS:
523 cont = FALSE;
524 error = normalEOF;
525 break;
526 case NOPE:
527 default:
528 error = AFM_parseError;
529 break;
530 } /* switch */
531 } /* while */
533 if (!found)
534 width = 250;
536 for (i = 0; i < 256; ++i)
537 cwi[i] = width;
539 fseek (fp, opos, 0);
541 return (error);
542 } /* initializeArray */
543 #endif
545 /************************* parseCharWidths **************************/
547 /* This function is called by "parseFile". It will parse the AFM File
548 * up to the "EndCharMetrics" keyword. It will save the character
549 * width info (as opposed to all of the character metric information)
550 * if requested by the caller of parseFile. Otherwise, it will just
551 * parse through the section without saving any information.
553 * If data is to be saved, parseCharWidths is passed in a pointer
554 * to an array of widths that has already been initialized by the
555 * standard value for unmapped character codes. This function parses
556 * the Character Metrics section only storing the width information
557 * for the encoded characters into the array using the character code
558 * as the index into that array.
560 * This function returns an error code specifying whether there was
561 * a premature EOF or a parsing error. This return value is used by
562 * parseFile to determine if there is more file to parse.
565 static int
566 parseCharWidths (FILE *fp, register int *cwi)
568 BOOL cont = TRUE, save = (cwi != NULL);
569 int pos = 0, error = AFM_ok;
570 register char *keyword;
572 while (cont)
574 keyword = token (fp);
575 /* Have reached an early and unexpected EOF. */
576 /* Set flag and stop parsing */
577 if (keyword == NULL)
579 error = AFM_earlyEOF;
580 break; /* get out of loop */
582 if (!save)
583 /* get tokens until the end of the Char Metrics section without */
584 /* saving any of the data*/
585 switch (recognize (keyword))
587 case ENDCHARMETRICS:
588 cont = FALSE;
589 break;
590 case ENDFONTMETRICS:
591 cont = FALSE;
592 error = normalEOF;
593 break;
594 default:
595 break;
596 } /* switch */
597 else
598 /* otherwise parse entire char metrics section, saving */
599 /* only the char x-width info */
600 switch (recognize (keyword))
602 case COMMENT:
603 keyword = linetoken (fp);
604 break;
605 case CODE:
606 keyword = token (fp);
607 pos = atoi (keyword);
608 break;
609 case XYWIDTH:
610 /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
611 keyword = token (fp); keyword = token (fp); /* eat values */
612 error = AFM_parseError;
613 break;
614 case XWIDTH:
615 keyword = token (fp);
616 if (pos >= 0) /* ignore unmapped chars */
617 cwi[pos] = atoi (keyword);
618 break;
619 case ENDCHARMETRICS:
620 cont = FALSE;
621 break;
622 case ENDFONTMETRICS:
623 cont = FALSE;
624 error = normalEOF;
625 break;
626 case CHARNAME: /* eat values (so doesn't cause AFM_parseError) */
627 keyword = token (fp);
628 break;
629 case CHARBBOX:
630 keyword = token (fp); keyword = token (fp);
631 keyword = token (fp); keyword = token (fp);
632 break;
633 case LIGATURE:
634 keyword = token (fp); keyword = token (fp);
635 break;
636 case NOPE:
637 default:
638 error = AFM_parseError;
639 break;
640 } /* switch */
641 } /* while */
643 return (error);
644 } /* parseCharWidths */
646 /************************* parseCharMetrics ************************/
648 /* This function is called by parseFile if the caller of parseFile
649 * requested that all character metric information be saved
650 * (as opposed to only the character width information).
652 * parseCharMetrics is passed in a pointer to an array of records
653 * to hold information on a per character basis. This function
654 * parses the Character Metrics section storing all character
655 * metric information for the ALL characters (mapped and unmapped)
656 * into the array.
658 * This function returns an error code specifying whether there was
659 * a premature EOF or a parsing error. This return value is used by
660 * parseFile to determine if there is more file to parse.
663 static int
664 parseCharMetrics (FILE *fp, register AFM_Font_info *fi)
666 BOOL cont = TRUE, firstTime = TRUE;
667 int error = AFM_ok, count = 0;
668 register AFM_CharMetricInfo *temp = fi->cmi;
669 register char *keyword;
671 while (cont)
673 keyword = token (fp);
674 if (keyword == NULL)
676 error = AFM_earlyEOF;
677 break; /* get out of loop */
679 switch (recognize (keyword))
681 case COMMENT:
682 keyword = linetoken (fp);
683 break;
684 case CODE:
685 if (count < fi->numOfChars)
687 if (firstTime)
688 firstTime = FALSE;
689 else
690 temp++;
691 temp->code = atoi (token (fp));
692 count++;
694 else
696 warning ("Too many metrics.");
697 error = AFM_parseError;
698 cont = FALSE;
700 break;
701 case XYWIDTH:
702 temp->wx = atoi (token (fp));
703 temp->wy = atoi (token (fp));
704 break;
705 case XWIDTH:
706 temp->wx = atoi (token (fp));
707 break;
709 case CHARNAME:
710 keyword = token (fp);
711 temp->name = (char *) malloc (strlen (keyword) + 1);
712 strcpy (temp->name, keyword);
713 break;
715 case CHARBBOX:
716 temp->charBBox.llx = atoi (token (fp));
717 temp->charBBox.lly = atoi (token (fp));
718 temp->charBBox.urx = atoi (token (fp));
719 temp->charBBox.ury = atoi (token (fp));
720 break;
722 case LIGATURE:
724 AFM_Ligature **tail = &(temp->ligs);
725 AFM_Ligature *node = *tail;
727 if (*tail != NULL)
729 while (node->next != NULL)
730 node = node->next;
731 tail = &(node->next);
734 *tail = (AFM_Ligature *) calloc (1, sizeof (AFM_Ligature));
735 keyword = token (fp);
736 (*tail)->succ = (char *) malloc (strlen (keyword) + 1);
737 strcpy ((*tail)->succ, keyword);
738 keyword = token (fp);
739 (*tail)->lig = (char *) malloc (strlen (keyword) + 1);
740 strcpy ((*tail)->lig, keyword);
741 break; }
742 case ENDCHARMETRICS:
743 cont = FALSE;;
744 break;
745 case ENDFONTMETRICS:
746 cont = FALSE;
747 error = normalEOF;
748 break;
749 case NOPE:
750 default:
751 warning ("Unknown token");
753 error = AFM_parseError;
754 break;
755 } /* switch */
756 } /* while */
758 if ((error == AFM_ok) && (count != fi->numOfChars))
760 warning ("Incorrect char count");
761 error = AFM_parseError;
763 return (error);
764 } /* parseCharMetrics */
766 /************************* parseAFM_TrackKernData ***********************/
768 /* This function is called by "parseFile". It will parse the AFM File
769 * up to the "EndTrackKern" or "EndKernData" keywords. It will save the
770 * track kerning data if requested by the caller of parseFile.
772 * parseAFM_TrackKernData is passed in a pointer to the FontInfo record.
773 * If data is to be saved, the FontInfo record will already contain
774 * a valid pointer to storage for the track kerning data.
776 * This function returns an error code specifying whether there was
777 * a premature EOF or a parsing error. This return value is used by
778 * parseFile to determine if there is more file to parse.
781 static int
782 parseAFM_TrackKernData (FILE *fp, register AFM_Font_info *fi)
784 BOOL cont = TRUE, save = (fi->tkd != NULL);
785 int pos = 0, error = AFM_ok, tcount = 0;
786 register char *keyword;
788 while (cont)
790 keyword = token (fp);
792 if (keyword == NULL)
794 error = AFM_earlyEOF;
795 break; /* get out of loop */
797 if (!save)
798 /* get tokens until the end of the Track Kerning Data */
799 /* section without saving any of the data */
800 switch (recognize (keyword))
802 case ENDTRACKKERN:
803 case ENDKERNDATA:
804 cont = FALSE;
805 break;
806 case ENDFONTMETRICS:
807 cont = FALSE;
808 error = normalEOF;
809 break;
810 default:
811 break;
812 } /* switch */
813 else
814 /* otherwise parse entire Track Kerning Data section, */
815 /* saving the data */
816 switch (recognize (keyword))
818 case COMMENT:
819 #ifdef METATYPE1_BUG
820 case GENERATED:
821 #endif
822 keyword = linetoken (fp);
823 break;
824 case TRACKKERN:
825 if (tcount < fi->numOfTracks)
827 keyword = token (fp);
828 fi->tkd[pos].degree = atoi (keyword);
829 keyword = token (fp);
830 fi->tkd[pos].minPtSize = atof (keyword);
831 if (errno == ERANGE) error = AFM_parseError;
832 keyword = token (fp);
833 fi->tkd[pos].minKernAmt = atof (keyword);
834 if (errno == ERANGE) error = AFM_parseError;
835 keyword = token (fp);
836 fi->tkd[pos].maxPtSize = atof (keyword);
837 if (errno == ERANGE) error = AFM_parseError;
838 keyword = token (fp);
839 fi->tkd[pos++].maxKernAmt = atof (keyword);
840 if (errno == ERANGE) error = AFM_parseError;
841 tcount++;
843 else
845 error = AFM_parseError;
846 cont = FALSE;
848 break;
849 case ENDTRACKKERN:
850 case ENDKERNDATA:
851 cont = FALSE;
852 break;
853 case ENDFONTMETRICS:
854 cont = FALSE;
855 error = normalEOF;
856 break;
857 case NOPE:
858 default:
859 error = AFM_parseError;
860 break;
861 } /* switch */
862 } /* while */
864 if (error == AFM_ok && tcount != fi->numOfTracks)
865 error = AFM_parseError;
867 return (error);
868 } /* parseAFM_TrackKernData */
870 /************************* parseAFM_PairKernData ************************/
872 /* This function is called by "parseFile". It will parse the AFM File
873 * up to the "EndKernPairs" or "EndKernData" keywords. It will save
874 * the pair kerning data if requested by the caller of parseFile.
876 * parseAFM_PairKernData is passed in a pointer to the FontInfo record.
877 * If data is to be saved, the FontInfo record will already contain
878 * a valid pointer to storage for the pair kerning data.
880 * This function returns an error code specifying whether there was
881 * a premature EOF or a parsing error. This return value is used by
882 * parseFile to determine if there is more file to parse.
885 static int
886 parseAFM_PairKernData (FILE *fp, register AFM_Font_info *fi)
888 BOOL cont = TRUE, save = (fi->pkd != NULL);
889 int pos = 0, error = AFM_ok, pcount = 0;
890 register char *keyword;
892 while (cont)
894 keyword = token (fp);
896 if (keyword == NULL)
898 error = AFM_earlyEOF;
899 break; /* get out of loop */
901 if (!save)
902 /* get tokens until the end of the Pair Kerning Data */
903 /* section without saving any of the data */
904 switch (recognize (keyword))
906 case ENDKERNPAIRS:
907 case ENDKERNDATA:
908 cont = FALSE;
909 break;
910 case ENDFONTMETRICS:
911 cont = FALSE;
912 error = normalEOF;
913 break;
914 default:
915 break;
916 } /* switch */
917 else
918 /* otherwise parse entire Pair Kerning Data section, */
919 /* saving the data */
920 switch (recognize (keyword))
922 case COMMENT:
923 keyword = linetoken (fp);
924 break;
925 case KERNPAIR:
926 if (pcount < fi->numOfPairs)
928 keyword = token (fp);
929 fi->pkd[pos].name1 = (char *)
930 malloc (strlen (keyword) + 1);
931 strcpy (fi->pkd[pos].name1, keyword);
932 keyword = token (fp);
933 fi->pkd[pos].name2 = (char *)
934 malloc (strlen (keyword) + 1);
935 strcpy (fi->pkd[pos].name2, keyword);
936 keyword = token (fp);
937 fi->pkd[pos].xamt = atoi (keyword);
938 keyword = token (fp);
939 fi->pkd[pos++].yamt = atoi (keyword);
940 pcount++;
942 else
944 error = AFM_parseError;
945 cont = FALSE;
947 break;
948 case KERNPAIRXAMT:
949 if (pcount < fi->numOfPairs)
951 keyword = token (fp);
952 fi->pkd[pos].name1 = (char *)
953 malloc (strlen (keyword) + 1);
954 strcpy (fi->pkd[pos].name1, keyword);
955 keyword = token (fp);
956 fi->pkd[pos].name2 = (char *)
957 malloc (strlen (keyword) + 1);
958 strcpy (fi->pkd[pos].name2, keyword);
959 keyword = token (fp);
960 fi->pkd[pos++].xamt = atoi (keyword);
961 pcount++;
963 else
965 error = AFM_parseError;
966 cont = FALSE;
968 break;
969 case ENDKERNPAIRS:
970 case ENDKERNDATA:
971 cont = FALSE;
972 break;
973 case ENDFONTMETRICS:
974 cont = FALSE;
975 error = normalEOF;
976 break;
977 case NOPE:
978 default:
979 error = AFM_parseError;
980 break;
981 } /* switch */
982 } /* while */
984 if (error == AFM_ok && pcount != fi->numOfPairs)
985 error = AFM_parseError;
987 return (error);
988 } /* parseAFM_PairKernData */
990 /************************* parseAFM_CompCharData **************************/
992 /* This function is called by "parseFile". It will parse the AFM File
993 * up to the "EndComposites" keyword. It will save the composite
994 * character data if requested by the caller of parseFile.
996 * parseAFM_CompCharData is passed in a pointer to the FontInfo record, and
997 * a boolean representing if the data should be saved.
999 * This function will create the appropriate amount of storage for
1000 * the composite character data and store a pointer to the storage
1001 * in the FontInfo record.
1003 * This function returns an error code specifying whether there was
1004 * a premature EOF or a parsing error. This return value is used by
1005 * parseFile to determine if there is more file to parse.
1008 static int
1009 parseAFM_CompCharData (FILE *fp, register AFM_Font_info *fi)
1011 BOOL cont = TRUE, firstTime = TRUE, save = (fi->ccd != NULL);
1012 int pos = 0, j = 0, error = AFM_ok, ccount = 0, pcount = 0;
1013 register char *keyword;
1015 while (cont)
1017 keyword = token (fp);
1018 if (keyword == NULL)
1019 /* Have reached an early and unexpected EOF. */
1020 /* Set flag and stop parsing */
1022 error = AFM_earlyEOF;
1023 break; /* get out of loop */
1025 if (ccount > fi->numOfComps)
1027 error = AFM_parseError;
1028 break; /* get out of loop */
1030 if (!save)
1031 /* get tokens until the end of the Composite Character info */
1032 /* section without saving any of the data */
1033 switch (recognize (keyword))
1035 case ENDCOMPOSITES:
1036 cont = FALSE;
1037 break;
1038 case ENDFONTMETRICS:
1039 cont = FALSE;
1040 error = normalEOF;
1041 break;
1042 default:
1043 break;
1044 } /* switch */
1045 else
1046 /* otherwise parse entire Composite Character info section, */
1047 /* saving the data */
1048 switch (recognize (keyword))
1050 case COMMENT:
1051 keyword = linetoken (fp);
1052 break;
1053 case COMPCHAR:
1054 if (ccount < fi->numOfComps)
1056 keyword = token (fp);
1057 if (pcount != fi->ccd[pos].numOfPieces)
1058 error = AFM_parseError;
1059 pcount = 0;
1060 if (firstTime) firstTime = FALSE;
1061 else pos++;
1062 fi->ccd[pos].ccName = (char *)
1063 malloc (strlen (keyword) + 1);
1064 strcpy (fi->ccd[pos].ccName, keyword);
1065 keyword = token (fp);
1066 fi->ccd[pos].numOfPieces = atoi (keyword);
1067 fi->ccd[pos].pieces = (AFM_Pcc *)
1068 calloc (fi->ccd[pos].numOfPieces, sizeof (AFM_Pcc));
1069 j = 0;
1070 ccount++;
1072 else
1074 error = AFM_parseError;
1075 cont = FALSE;
1077 break;
1078 case COMPCHARPIECE:
1079 if (pcount < fi->ccd[pos].numOfPieces)
1081 keyword = token (fp);
1082 fi->ccd[pos].pieces[j].AFM_PccName = (char *)
1083 malloc (strlen (keyword) + 1);
1084 strcpy (fi->ccd[pos].pieces[j].AFM_PccName, keyword);
1085 keyword = token (fp);
1086 fi->ccd[pos].pieces[j].deltax = atoi (keyword);
1087 keyword = token (fp);
1088 fi->ccd[pos].pieces[j++].deltay = atoi (keyword);
1089 pcount++;
1091 else
1092 error = AFM_parseError;
1093 break;
1094 case ENDCOMPOSITES:
1095 cont = FALSE;
1096 break;
1097 case ENDFONTMETRICS:
1098 cont = FALSE;
1099 error = normalEOF;
1100 break;
1101 case NOPE:
1102 default:
1103 error = AFM_parseError;
1104 break;
1105 } /* switch */
1106 } /* while */
1108 if (error == AFM_ok && ccount != fi->numOfComps)
1109 error = AFM_parseError;
1111 return (error);
1112 } /* parseAFM_CompCharData */
1114 /*************************** 'PUBLIC' FUNCTION ********************/
1116 void
1117 AFM_free (AFM_Font_info *fi)
1119 if (fi->gfi)
1121 free (fi->gfi->afmVersion);
1122 free (fi->gfi->fontName);
1123 free (fi->gfi->fullName);
1124 free (fi->gfi->familyName);
1125 free (fi->gfi->weight);
1126 free (fi->gfi->version);
1127 free (fi->gfi->notice);
1128 free (fi->gfi->encodingScheme);
1129 free (fi->gfi);
1132 /* This contains just scalars. */
1133 free (fi->cwi);
1135 if (fi->cmi)
1137 int i;
1138 for (i = 0; i < fi->numOfChars; i++)
1140 free (fi->cmi[i].name);
1141 while (fi->cmi[i].ligs)
1143 AFM_Ligature *tmp;
1144 tmp = fi->cmi[i].ligs;
1145 free (tmp->succ);
1146 free (tmp->lig);
1147 free (tmp);
1148 fi->cmi[i].ligs = fi->cmi[i].ligs->next;
1151 free (fi->cmi);
1154 /* This contains just scalars. */
1155 free (fi->tkd);
1157 if (fi->pkd)
1159 int i;
1160 for (i = 0; i < fi->numOfPairs; i++)
1162 free (fi->pkd[i].name1);
1163 free (fi->pkd[i].name2);
1165 free (fi->pkd);
1168 if (fi->ccd)
1170 int i, j;
1171 for (i = 0; i < fi->numOfComps; i++)
1173 free (fi->ccd[i].ccName);
1174 for (j = 0; j < fi->ccd[i].numOfPieces; j++)
1175 free (fi->ccd[i].pieces[j].AFM_PccName);
1176 free (fi->ccd[i].pieces);
1178 free (fi->ccd);
1181 free (fi);
1184 /*************************** parseFile *****************************/
1186 /* parseFile is the only 'public' procedure available. It is called
1187 * from an application wishing to get information from an AFM file.
1188 * The caller of this function is responsible for locating and opening
1189 * an AFM file and handling all errors associated with that task.
1191 * parseFile expects 3 parameters: a vaild file pointer, a pointer
1192 * to a (FontInfo *) variable (for which storage will be allocated and
1193 * the data requested filled in), and a mask specifying which
1194 * data from the AFM File should be saved in the FontInfo structure.
1196 * The file will be parsed and the requested data will be stored in
1197 * a record of type FontInfo (refer to ParseAFM.h).
1199 * parseFile returns an error code as defined in parseAFM.h.
1201 * The position of the read/write pointer associated with the file
1202 * pointer upon return of this function is undefined.
1206 AFM_parseFile (FILE *fp, AFM_Font_info **fi, int flags)
1209 int code = AFM_ok; /* return code from each of the parsing routines */
1210 int error = AFM_ok; /* used as the return code from this function */
1212 register char *keyword; /* used to store a token */
1214 /* storage data for the global variable ident */
1215 if (!ident)
1216 ident = (char *) calloc (MAX_NAME, sizeof (char));
1217 if (ident == NULL)
1219 error = AFM_storageProblem;
1220 return error;
1223 (*fi) = (AFM_Font_info *) calloc (1, sizeof (AFM_Font_info));
1224 if ((*fi) == NULL)
1226 error = AFM_storageProblem;
1227 return error;
1230 if (flags & P_G)
1232 (*fi)->gfi = (AFM_GlobalFontInfo *) calloc (1,
1233 sizeof (AFM_GlobalFontInfo));
1234 if ((*fi)->gfi == NULL)
1236 error = AFM_storageProblem;
1237 return error;
1241 /* The AFM File begins with Global Font Information. This section */
1242 /* will be parsed whether or not information should be saved. */
1243 code = parseGlobals (fp, (*fi)->gfi);
1245 if (code < 0)
1246 error = code;
1248 /* The Global Font Information is followed by the Character Metrics */
1249 /* section. Which procedure is used to parse this section depends on */
1250 /* how much information should be saved. If all of the metrics info */
1251 /* is wanted, parseCharMetrics is called. If only the character widths */
1252 /* is wanted, parseCharWidths is called. parseCharWidths will also */
1253 /* be called in the case that no character data is to be saved, just */
1254 /* to parse through the section. */
1256 if ((code != normalEOF) && (code != AFM_earlyEOF))
1258 (*fi)->numOfChars = atoi (token (fp));
1259 if (flags & (P_M ^ P_W))
1261 (*fi)->cmi = (AFM_CharMetricInfo *)
1262 calloc ((*fi)->numOfChars, sizeof (AFM_CharMetricInfo));
1263 if ((*fi)->cmi == NULL)
1265 error = AFM_storageProblem;
1266 return error;
1268 code = parseCharMetrics (fp, *fi);
1270 else
1272 if (flags & P_W)
1274 (*fi)->cwi = (int *) calloc (256, sizeof (int));
1275 if ((*fi)->cwi == NULL)
1277 error = AFM_storageProblem;
1278 return (error);
1281 /* parse section regardless */
1282 code = parseCharWidths (fp, (*fi)->cwi);
1283 } /* else */
1284 } /* if */
1286 if ((error != AFM_earlyEOF) && (code < 0))
1287 error = code;
1289 /* The remaining sections of the AFM are optional. This code will */
1290 /* look at the next keyword in the file to determine what section */
1291 /* is next, and then allocate the appropriate amount of storage */
1292 /* for the data (if the data is to be saved) and call the */
1293 /* appropriate parsing routine to parse the section. */
1295 while ((code != normalEOF) && (code != AFM_earlyEOF))
1297 keyword = token (fp);
1298 if (keyword == NULL)
1299 /* Have reached an early and unexpected EOF. */
1300 /* Set flag and stop parsing */
1302 code = AFM_earlyEOF;
1303 break; /* get out of loop */
1305 switch (recognize (keyword))
1307 case STARTKERNDATA:
1308 break;
1309 case ENDKERNDATA:
1310 break;
1311 case STARTTRACKKERN:
1312 keyword = token (fp);
1313 if (flags & P_T)
1315 (*fi)->numOfTracks = atoi (keyword);
1316 (*fi)->tkd = (AFM_TrackKernData *)
1317 calloc ((*fi)->numOfTracks, sizeof (AFM_TrackKernData));
1318 if ((*fi)->tkd == NULL)
1320 error = AFM_storageProblem;
1321 return (error);
1323 } /* if */
1324 code = parseAFM_TrackKernData (fp, *fi);
1325 break;
1326 case STARTKERNPAIRS:
1327 keyword = token (fp);
1328 if (flags & P_P)
1330 (*fi)->numOfPairs = atoi (keyword);
1331 (*fi)->pkd = (AFM_PairKernData *)
1332 calloc ((*fi)->numOfPairs, sizeof (AFM_PairKernData));
1333 if ((*fi)->pkd == NULL)
1335 error = AFM_storageProblem;
1336 return (error);
1338 } /* if */
1339 code = parseAFM_PairKernData (fp, *fi);
1340 break;
1341 case STARTCOMPOSITES:
1342 keyword = token (fp);
1343 if (flags & P_C)
1345 (*fi)->numOfComps = atoi (keyword);
1346 (*fi)->ccd = (AFM_CompCharData *)
1347 calloc ((*fi)->numOfComps, sizeof (AFM_CompCharData));
1348 if ((*fi)->ccd == NULL)
1350 error = AFM_storageProblem;
1351 return (error);
1353 } /* if */
1354 code = parseAFM_CompCharData (fp, *fi);
1355 break;
1356 case ENDFONTMETRICS:
1357 code = normalEOF;
1358 break;
1359 case NOPE:
1360 default:
1361 code = AFM_parseError;
1362 break;
1363 } /* switch */
1365 if ((error != AFM_earlyEOF) && (code < 0))
1366 error = code;
1367 } /* while */
1369 if ((error != AFM_earlyEOF) && (code < 0))
1370 error = code;
1372 if (ident != NULL) { free (ident); ident = NULL; }
1374 return (error);
1375 } /* parseFile */