3 * Copyright (c) 1995-1998 Markku Rossi.
5 * Author: Markku Rossi <mtr@iki.fi>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; see the file COPYING. If not, write to
21 * the Free Software Foundation, 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
33 ((ch) == ' ' || (ch) == '\n' || (ch) == '\r' || (ch) == '\t' || (ch) == ';')
35 #define GET_VALUE(typenum) get_type (handle, ctx, (typenum), &node)
40 char token[1024]; /* maximum line length is 255, this should be
42 unsigned int tokenlen; /* length of the token */
45 typedef struct parse_ctx_st ParseCtx;
52 * The AFM keys. This array must be kept sorted because keys are
53 * searched by using binary search.
55 static struct keyname_st
61 {"Ascender", kAscender},
63 {"AxisLabel", kAxisLabel},
64 {"AxisType", kAxisType},
66 {"BlendAxisTypes", kBlendAxisTypes},
67 {"BlendDesignMap", kBlendDesignMap},
68 {"BlendDesignPositions", kBlendDesignPositions},
72 {"CapHeight", kCapHeight},
73 {"CharWidth", kCharWidth},
74 {"CharacterSet", kCharacterSet},
75 {"Characters", kCharacters},
76 {"Comment", kComment},
77 {"Descendents", kDescendents},
78 {"Descender", kDescender},
79 {"EncodingScheme", kEncodingScheme},
80 {"EndAxis", kEndAxis},
81 {"EndCharMetrics", kEndCharMetrics},
82 {"EndCompFontMetrics", kEndCompFontMetrics},
83 {"EndComposites", kEndComposites},
84 {"EndDescendent", kEndDescendent},
85 {"EndDirection", kEndDirection},
86 {"EndFontMetrics", kEndFontMetrics},
87 {"EndKernData", kEndKernData},
88 {"EndKernPairs", kEndKernPairs},
89 {"EndMaster", kEndMaster},
90 {"EndMasterFontMetrics", kEndMasterFontMetrics},
91 {"EndTrackKern", kEndTrackKern},
92 {"EscChar", kEscChar},
93 {"FamilyName", kFamilyName},
94 {"FontBBox", kFontBBox},
95 {"FontName", kFontName},
96 {"FullName", kFullName},
97 {"IsBaseFont", kIsBaseFont},
98 {"IsFixedPitch", kIsFixedPitch},
99 {"IsFixedV", kIsFixedV},
100 {"ItalicAngle", kItalicAngle},
106 {"MappingScheme", kMappingScheme},
107 {"Masters", kMasters},
108 {"MetricsSets", kMetricsSets},
112 {"StartAxis", kStartAxis},
113 {"StartCharMetrics", kStartCharMetrics},
114 {"StartCompFontMetrics", kStartCompFontMetrics},
115 {"StartComposites", kStartComposites},
116 {"StartDescendent", kStartDescendent},
117 {"StartDirection", kStartDirection},
118 {"StartFontMetrics", kStartFontMetrics},
119 {"StartKernData", kStartKernData},
120 {"StartKernPairs", kStartKernPairs},
121 {"StartMaster", kStartMaster},
122 {"StartMasterFontMetrics", kStartMasterFontMetrics},
123 {"StartTrackKern", kStartTrackKern},
124 {"TrackKern", kTrackKern},
125 {"UnderlinePosition", kUnderlinePosition},
126 {"UnderlineThickness", kUnderlineThickness},
128 {"VVector", kVVector},
129 {"Version", kVersion},
140 {"WeightVector", kWeightVector},
141 {"XHeight", kXHeight},
146 #define NUM_KEYS (sizeof (keynames) / sizeof (struct keyname_st) - 1)
149 * Prototypes for static functions.
152 /* Throw parse error <error>. Never returns. */
153 static void parse_error ___P ((AFMHandle handle, AFMError error));
155 static int get_token ___P ((AFMHandle handle, ParseCtx *ctx));
156 static int get_line_token ___P ((AFMHandle handle, ParseCtx *ctx));
157 static void get_key ___P ((AFMHandle handle, ParseCtx *ctx,
158 AFMKey *key_return));
159 static void get_type ___P ((AFMHandle handle, ParseCtx *ctx, int type,
160 AFMNode *type_return));
161 static void read_character_metrics ___P ((AFMHandle handle, ParseCtx *ctx,
163 static void read_kern_pairs ___P ((AFMHandle handle, ParseCtx *ctx,
165 static void read_track_kerns ___P ((AFMHandle handle, ParseCtx *ctx,
167 static void read_composites ___P ((AFMHandle handle, ParseCtx *ctx,
175 afm_parse_file (AFMHandle handle, const char *filename, AFMFont font)
180 ParseCtx *ctx = &context;
181 int wd = 0; /* Writing direction. */
184 ctx->fp = fopen (filename, "r");
186 parse_error (handle, SYSERROR (AFM_ERROR_FILE_IO));
188 /* Check that file is really an AFM file. */
190 get_key (handle, ctx, &key);
191 if (key != kStartFontMetrics)
192 parse_error (handle, AFM_ERROR_NOT_AFM_FILE);
193 GET_VALUE (AFM_TYPE_NUMBER);
194 font->version = node.u.number;
199 get_key (handle, ctx, &key);
203 (void) get_line_token (handle, ctx);
207 /* File structure. */
209 case kStartFontMetrics:
210 GET_VALUE (AFM_TYPE_NUMBER);
211 font->version = node.u.number;
214 case kEndFontMetrics:
218 case kStartCompFontMetrics:
219 case kEndCompFontMetrics:
220 case kStartMasterFontMetrics:
221 case kEndMasterFontMetrics:
222 parse_error (handle, AFM_ERROR_UNSUPPORTED_FORMAT);
225 /* Global font information. */
227 GET_VALUE (AFM_TYPE_STRING);
228 font->global_info.FontName = node.u.string;
232 GET_VALUE (AFM_TYPE_STRING);
233 font->global_info.FullName = node.u.string;
237 GET_VALUE (AFM_TYPE_STRING);
238 font->global_info.FamilyName = node.u.string;
242 GET_VALUE (AFM_TYPE_STRING);
243 font->global_info.Weight = node.u.string;
247 GET_VALUE (AFM_TYPE_NUMBER);
248 font->global_info.FontBBox_llx = node.u.number;
249 GET_VALUE (AFM_TYPE_NUMBER);
250 font->global_info.FontBBox_lly = node.u.number;
251 GET_VALUE (AFM_TYPE_NUMBER);
252 font->global_info.FontBBox_urx = node.u.number;
253 GET_VALUE (AFM_TYPE_NUMBER);
254 font->global_info.FontBBox_ury = node.u.number;
258 GET_VALUE (AFM_TYPE_STRING);
259 font->global_info.Version = node.u.string;
263 GET_VALUE (AFM_TYPE_STRING);
264 font->global_info.Notice = node.u.string;
267 case kEncodingScheme:
268 GET_VALUE (AFM_TYPE_STRING);
269 font->global_info.EncodingScheme = node.u.string;
273 GET_VALUE (AFM_TYPE_INTEGER);
274 font->global_info.MappingScheme = node.u.integer;
278 GET_VALUE (AFM_TYPE_INTEGER);
279 font->global_info.EscChar = node.u.integer;
283 GET_VALUE (AFM_TYPE_STRING);
284 font->global_info.CharacterSet = node.u.string;
288 GET_VALUE (AFM_TYPE_INTEGER);
289 font->global_info.Characters = node.u.integer;
293 GET_VALUE (AFM_TYPE_BOOLEAN);
294 font->global_info.IsBaseFont = node.u.boolean;
298 GET_VALUE (AFM_TYPE_NUMBER);
299 font->global_info.VVector_0 = node.u.number;
300 GET_VALUE (AFM_TYPE_NUMBER);
301 font->global_info.VVector_1 = node.u.number;
305 GET_VALUE (AFM_TYPE_BOOLEAN);
306 font->global_info.IsFixedV = node.u.boolean;
310 GET_VALUE (AFM_TYPE_NUMBER);
311 font->global_info.CapHeight = node.u.number;
315 GET_VALUE (AFM_TYPE_NUMBER);
316 font->global_info.XHeight = node.u.number;
320 GET_VALUE (AFM_TYPE_NUMBER);
321 font->global_info.Ascender = node.u.number;
325 GET_VALUE (AFM_TYPE_NUMBER);
326 font->global_info.Descender = node.u.number;
329 /* Writing directions. */
330 case kStartDirection:
331 GET_VALUE (AFM_TYPE_INTEGER);
333 font->writing_direction_metrics[wd].is_valid = AFMTrue;
336 case kUnderlinePosition:
337 GET_VALUE (AFM_TYPE_NUMBER);
338 font->writing_direction_metrics[wd].UnderlinePosition
342 case kUnderlineThickness:
343 GET_VALUE (AFM_TYPE_NUMBER);
344 font->writing_direction_metrics[wd].UnderlineThickness
349 GET_VALUE (AFM_TYPE_NUMBER);
350 font->writing_direction_metrics[wd].ItalicAngle = node.u.number;
354 GET_VALUE (AFM_TYPE_NUMBER);
355 font->writing_direction_metrics[wd].CharWidth_x = node.u.number;
356 GET_VALUE (AFM_TYPE_NUMBER);
357 font->writing_direction_metrics[wd].CharWidth_y = node.u.number;
361 GET_VALUE (AFM_TYPE_BOOLEAN);
362 font->writing_direction_metrics[wd].IsFixedPitch = node.u.boolean;
368 /* Individual Character Metrics. */
369 case kStartCharMetrics:
370 GET_VALUE (AFM_TYPE_INTEGER);
371 font->num_character_metrics = node.u.integer;
372 font->character_metrics
373 = ((AFMIndividualCharacterMetrics *)
374 calloc (font->num_character_metrics + 1,
375 sizeof (AFMIndividualCharacterMetrics)));
376 if (font->character_metrics == NULL)
377 parse_error (handle, AFM_ERROR_MEMORY);
379 read_character_metrics (handle, ctx, font);
386 case kStartKernPairs:
387 if (font->info_level & AFM_I_KERN_PAIRS)
389 GET_VALUE (AFM_TYPE_INTEGER);
390 font->num_kern_pairs = node.u.integer;
392 (AFMPairWiseKerning *) calloc (font->num_kern_pairs + 1,
393 sizeof (AFMPairWiseKerning));
394 if (font->kern_pairs == NULL)
395 parse_error (handle, AFM_ERROR_MEMORY);
397 read_kern_pairs (handle, ctx, font);
403 (void) get_line_token (handle, ctx);
404 get_key (handle, ctx, &key);
406 while (key != kEndKernPairs);
410 case kStartTrackKern:
411 if (font->info_level & AFM_I_TRACK_KERNS)
413 GET_VALUE (AFM_TYPE_INTEGER);
414 font->num_track_kerns = node.u.integer;
416 = (AFMTrackKern *) calloc (font->num_track_kerns + 1,
417 sizeof (AFMTrackKern));
418 if (font->track_kerns == NULL)
419 parse_error (handle, AFM_ERROR_MEMORY);
421 read_track_kerns (handle, ctx, font);
427 (void) get_line_token (handle, ctx);
428 get_key (handle, ctx, &key);
430 while (key != kEndTrackKern);
437 /* Composite Character Data. */
438 case kStartComposites:
439 if (font->info_level & AFM_I_COMPOSITES)
441 GET_VALUE (AFM_TYPE_INTEGER);
442 font->num_composites = node.u.integer;
444 = (AFMComposite *) calloc (font->num_composites + 1,
445 sizeof (AFMComposite));
446 if (font->composites == NULL)
447 parse_error (handle, AFM_ERROR_MEMORY);
449 read_composites (handle, ctx, font);
455 (void) get_line_token (handle, ctx);
456 get_key (handle, ctx, &key);
458 while (key != kEndComposites);
469 /* Check post conditions. */
471 if (!font->writing_direction_metrics[0].is_valid
472 && !font->writing_direction_metrics[1].is_valid)
473 /* No direction specified, 0 implied. */
474 font->writing_direction_metrics[0].is_valid = AFMTrue;
476 /* Undef character. */
477 if (!strhash_get (font->private->fontnames, "space", 5,
478 (void *) font->private->undef))
480 /* Character "space" is not defined. Select the first one. */
481 assert (font->num_character_metrics > 0);
482 font->private->undef = &font->character_metrics[0];
486 if (font->writing_direction_metrics[0].is_valid
487 && font->writing_direction_metrics[0].IsFixedPitch)
489 /* Take one, it doesn't matter which one. */
490 font->writing_direction_metrics[0].CharWidth_x
491 = font->character_metrics[0].w0x;
492 font->writing_direction_metrics[0].CharWidth_y
493 = font->character_metrics[0].w0y;
495 if (font->writing_direction_metrics[1].is_valid
496 && font->writing_direction_metrics[1].IsFixedPitch)
498 font->writing_direction_metrics[1].CharWidth_x
499 = font->character_metrics[1].w1x;
500 font->writing_direction_metrics[1].CharWidth_y
501 = font->character_metrics[1].w1y;
511 parse_error (AFMHandle handle, AFMError error)
513 handle->parse_error = error;
514 longjmp (handle->jmpbuf, 1);
516 /* If this is reached, then all is broken. */
517 fprintf (stderr, "AFM: fatal internal longjmp() error.\n");
523 get_token (AFMHandle handle, ParseCtx *ctx)
528 /* Skip the leading whitespace. */
529 while ((ch = getc (ctx->fp)) != EOF)
536 ungetc (ch, ctx->fp);
539 for (i = 0, ch = getc (ctx->fp);
540 i < sizeof (ctx->token) && ch != EOF && !ISSPACE (ch);
541 i++, ch = getc (ctx->fp))
544 if (i >= sizeof (ctx->token))
545 /* Line is too long, this is against AFM specification. */
546 parse_error (handle, AFM_ERROR_SYNTAX);
548 ctx->token[i] = '\0';
556 get_line_token (AFMHandle handle, ParseCtx *ctx)
560 /* Skip the leading whitespace. */
561 while ((ch = getc (ctx->fp)) != EOF)
568 ungetc (ch, ctx->fp);
570 /* Read to the end of the line. */
571 for (i = 0, ch = getc (ctx->fp);
572 i < sizeof (ctx->token) && ch != EOF && ch != '\n';
573 i++, ch = getc (ctx->fp))
576 if (i >= sizeof (ctx->token))
577 parse_error (handle, AFM_ERROR_SYNTAX);
579 /* Skip all trailing whitespace. */
580 for (i--; i >= 0 && ISSPACE (ctx->token[i]); i--)
584 ctx->token[i] = '\0';
592 match_key (char *key)
595 int upper = NUM_KEYS;
596 int midpoint, cmpvalue;
597 AFMBoolean found = AFMFalse;
599 while ((upper >= lower) && !found)
601 midpoint = (lower + upper) / 2;
602 if (keynames[midpoint].name == NULL)
605 cmpvalue = strcmp (key, keynames[midpoint].name);
608 else if (cmpvalue < 0)
609 upper = midpoint - 1;
611 lower = midpoint + 1;
615 return keynames[midpoint].key;
622 get_key (AFMHandle handle, ParseCtx *ctx, AFMKey *key_return)
629 if (!get_token (handle, ctx))
630 /* Unexpected EOF. */
631 parse_error (handle, AFM_ERROR_SYNTAX);
633 key = match_key (ctx->token);
640 /* No match found. According to standard, we must skip this key. */
641 sprintf (msg, "skipping key \"%s\"", ctx->token);
642 afm_error (handle, msg);
643 get_line_token (handle, ctx);
650 /* Reader for AFM types. */
652 get_type (AFMHandle handle, ParseCtx *ctx, int type, AFMNode *type_return)
658 case AFM_TYPE_STRING:
659 if (!get_line_token (handle, ctx))
660 parse_error (handle, AFM_ERROR_SYNTAX);
662 type_return->u.string = (AFMString) calloc (1, ctx->tokenlen + 1);
663 if (type_return->u.string == NULL)
664 parse_error (handle, AFM_ERROR_MEMORY);
666 memcpy (type_return->u.string, ctx->token, ctx->tokenlen);
670 if (!get_token (handle, ctx))
671 parse_error (handle, AFM_ERROR_SYNTAX);
673 type_return->u.name = (AFMName) calloc (1, ctx->tokenlen + 1);
674 if (type_return->u.string == NULL)
675 parse_error (handle, AFM_ERROR_MEMORY);
677 memcpy (type_return->u.name, ctx->token, ctx->tokenlen);
680 case AFM_TYPE_NUMBER:
681 if (!get_token (handle, ctx))
682 parse_error (handle, AFM_ERROR_SYNTAX);
684 memcpy (buf, ctx->token, ctx->tokenlen);
685 buf[ctx->tokenlen] = '\0';
686 type_return->u.number = atof (buf);
689 case AFM_TYPE_INTEGER:
690 if (!get_token (handle, ctx))
691 parse_error (handle, AFM_ERROR_SYNTAX);
693 memcpy (buf, ctx->token, ctx->tokenlen);
694 buf[ctx->tokenlen] = '\0';
695 type_return->u.integer = atoi (buf);
699 fprintf (stderr, "Array types not implemented yet.\n");
703 case AFM_TYPE_BOOLEAN:
704 if (!get_token (handle, ctx))
705 parse_error (handle, AFM_ERROR_SYNTAX);
707 memcpy (buf, ctx->token, ctx->tokenlen);
708 buf[ctx->tokenlen] = '\0';
710 if (strcmp (buf, "true") == 0)
711 type_return->u.boolean = AFMTrue;
712 else if (strcmp (buf, "false") == 0)
713 type_return->u.boolean = AFMFalse;
715 parse_error (handle, AFM_ERROR_SYNTAX);
719 fprintf (stderr, "get_type(): illegal type %d\n", type_return->type);
727 read_character_metrics (AFMHandle handle, ParseCtx *ctx, AFMFont font)
731 AFMIndividualCharacterMetrics *cm = NULL;
738 get_key (handle, ctx, &key);
746 if (i >= font->num_character_metrics)
747 parse_error (handle, AFM_ERROR_SYNTAX);
749 cm = &font->character_metrics[i];
750 GET_VALUE (AFM_TYPE_INTEGER);
751 cm->character_code = node.u.integer;
752 if (cm->character_code >= 0 && cm->character_code <= 255)
753 font->encoding[cm->character_code] = cm;
762 GET_VALUE (AFM_TYPE_NUMBER);
763 cm->w0x = node.u.number;
768 GET_VALUE (AFM_TYPE_NUMBER);
769 cm->w1x = node.u.number;
775 GET_VALUE (AFM_TYPE_NUMBER);
776 cm->w0y = node.u.number;
781 GET_VALUE (AFM_TYPE_NUMBER);
782 cm->w1y = node.u.number;
788 GET_VALUE (AFM_TYPE_NUMBER);
789 cm->w0x = node.u.number;
790 GET_VALUE (AFM_TYPE_NUMBER);
791 cm->w0y = node.u.number;
795 GET_VALUE (AFM_TYPE_NUMBER);
796 cm->w1x = node.u.number;
797 GET_VALUE (AFM_TYPE_NUMBER);
798 cm->w1y = node.u.number;
802 GET_VALUE (AFM_TYPE_NUMBER);
803 cm->vv_x = node.u.number;
804 GET_VALUE (AFM_TYPE_NUMBER);
805 cm->vv_y = node.u.number;
809 GET_VALUE (AFM_TYPE_NAME);
810 cm->name = node.u.name;
811 if (!strhash_put (font->private->fontnames, cm->name,
812 strlen (cm->name), cm, NULL))
813 parse_error (handle, AFM_ERROR_MEMORY);
817 GET_VALUE (AFM_TYPE_NUMBER);
818 cm->llx = node.u.number;
819 GET_VALUE (AFM_TYPE_NUMBER);
820 cm->lly = node.u.number;
821 GET_VALUE (AFM_TYPE_NUMBER);
822 cm->urx = node.u.number;
823 GET_VALUE (AFM_TYPE_NUMBER);
824 cm->ury = node.u.number;
828 /* XXX Skip ligatures. */
829 get_line_token (handle, ctx);
832 case kEndCharMetrics:
833 if (i != font->num_character_metrics - 1)
836 * My opinion is that this is a syntax error; the
837 * creator of this AFM file should have been smart
838 * enought to count these character metrics. Well,
839 * maybe that is too much asked...
841 font->num_character_metrics = i + 1;
848 parse_error (handle, AFM_ERROR_SYNTAX);
856 read_kern_pairs (AFMHandle handle, ParseCtx *ctx, AFMFont font)
860 AFMPairWiseKerning *kp;
863 for (i = 0; i < font->num_kern_pairs; i++)
865 kp = &font->kern_pairs[i];
866 get_key (handle, ctx, &key);
873 GET_VALUE (AFM_TYPE_NAME);
874 kp->name1 = node.u.name;
876 GET_VALUE (AFM_TYPE_NAME);
877 kp->name2 = node.u.name;
879 GET_VALUE (AFM_TYPE_NUMBER);
884 kp->kx = node.u.number;
885 GET_VALUE (AFM_TYPE_NUMBER);
886 kp->ky = node.u.number;
890 kp->kx = node.u.number;
895 kp->ky = node.u.number;
900 fprintf (stderr, "AFM: fatal corruption\n");
911 parse_error (handle, AFM_ERROR_SYNTAX);
917 get_key (handle, ctx, &key);
918 if (key != kEndKernPairs)
919 parse_error (handle, AFM_ERROR_SYNTAX);
924 read_track_kerns (AFMHandle handle, ParseCtx *ctx, AFMFont font)
931 for (i = 0; i < font->num_kern_pairs; i++)
933 tk = &font->track_kerns[i];
934 get_key (handle, ctx, &key);
936 /* TrackKern degree min-ptsize min-kern max-ptrsize max-kern */
938 if (key != kTrackKern)
939 parse_error (handle, AFM_ERROR_SYNTAX);
941 GET_VALUE (AFM_TYPE_INTEGER);
942 tk->degree = node.u.integer;
944 GET_VALUE (AFM_TYPE_NUMBER);
945 tk->min_ptsize = node.u.number;
947 GET_VALUE (AFM_TYPE_NUMBER);
948 tk->min_kern = node.u.number;
950 GET_VALUE (AFM_TYPE_NUMBER);
951 tk->max_ptsize = node.u.number;
953 GET_VALUE (AFM_TYPE_NUMBER);
954 tk->max_kern = node.u.number;
958 get_key (handle, ctx, &key);
959 if (key != kEndTrackKern)
960 parse_error (handle, AFM_ERROR_SYNTAX);
965 read_composites (AFMHandle handle, ParseCtx *ctx, AFMFont font)
972 for (i = 0; i < font->num_composites; i++)
974 cm = &font->composites[i];
975 get_key (handle, ctx, &key);
978 parse_error (handle, AFM_ERROR_SYNTAX);
980 GET_VALUE (AFM_TYPE_NAME);
981 cm->name = node.u.name;
983 /* Create name -> AFMComposite mapping. */
984 if (!strhash_put (font->private->compositenames, cm->name,
985 strlen (cm->name), cm, NULL))
986 parse_error (handle, AFM_ERROR_MEMORY);
988 GET_VALUE (AFM_TYPE_INTEGER);
989 cm->num_components = node.u.integer;
991 = (AFMCompositeComponent *) calloc (cm->num_components + 1,
992 sizeof (AFMCompositeComponent));
994 /* Read composite components. */
995 for (j = 0; j < cm->num_components; j++)
998 get_key (handle, ctx, &key);
1000 parse_error (handle, AFM_ERROR_SYNTAX);
1004 GET_VALUE (AFM_TYPE_NAME);
1005 cm->components[j].name = node.u.name;
1007 GET_VALUE (AFM_TYPE_NUMBER);
1008 cm->components[j].deltax = node.u.number;
1010 GET_VALUE (AFM_TYPE_NUMBER);
1011 cm->components[j].deltay = node.u.number;
1015 /* Get end token. */
1016 get_key (handle, ctx, &key);
1017 if (key != kEndComposites)
1018 parse_error (handle, AFM_ERROR_SYNTAX);