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