a46559cab104c3d0e148fa26e2c0342e45cab62f
[pspp] / src / groff-font.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "font.h"
22 #include "error.h"
23 #include <stdio.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <limits.h>
27 #include <stdarg.h>
28 #include "alloc.h"
29 #include "error.h"
30 #include "filename.h"
31 #include "getline.h"
32 #include "hash.h"
33 #include "pool.h"
34 #include "str.h"
35 #include "version.h"
36
37 #include "gettext.h"
38 #define _(msgid) gettext (msgid)
39
40 int font_number_to_index (int);
41
42 int space_index;
43
44 static int font_msg (int, const char *,...)
45      PRINTF_FORMAT (2, 3);
46 static void scan_badchars (char *, int);
47 static void dup_char_metric (struct font_desc * font, int dest, int src);
48 static void add_char_metric (struct font_desc * font, struct char_metrics *metrics,
49                              int code);
50 static void add_kern (struct font_desc * font, int ch1, int ch2, int adjust);
51
52 /* Typical whitespace characters for tokenizing. */
53 static const char whitespace[] = " \t\n\r\v";
54
55 /* Some notes on the groff_font manpage:
56
57    DESC file format: A typical PostScript `res' would be 72000, with
58    `hor' and `vert' set to 1 to indicate that all those positions are
59    valid.  `sizescale' of 1000 would indicate that a scaled point is
60    1/1000 of a point (which is 1/72000 of an inch, the same as the
61    number of machine units per inch indicated on `res').  `unitwidth'
62    of 1000 would indicate that font files are set up for fonts with
63    point size of 1000 scaled points, which would equal 1/72 inch or 1
64    point (this would tell Groff's postprocessor that it needs to scale
65    the font 12 times larger to get a 12-point font). */
66
67 /* Reads a Groff font description file and converts it to a usable
68    binary format in memory.  Installs the binary format in the global
69    font table.  See groff_font for a description of the font
70    description format supported.  Returns nonzero on success. */
71 struct font_desc *
72 groff_read_font (const char *fn)
73 {
74   struct char_metrics *metrics;
75
76   /* Pool created for font, font being created, font file. */
77   struct pool *font_pool = NULL;
78   struct font_desc *font = NULL;
79   FILE *f = NULL;
80
81   /* Current line, size of line buffer, length of line. */
82   char *line = NULL;
83   size_t size;
84   int len;
85
86   /* Tokenization saved pointer. */
87   char *sp;
88   
89   /* First token on line. */
90   char *key;
91
92   /* 0=kernpairs section, 1=charset section. */
93   int charset = 0;
94
95   /* Index for previous line. */
96   int prev_index = -1;
97
98   /* Current location in file, used for error reporting. */
99   struct file_locator where;
100
101 #ifdef unix
102   fn = fn_tilde_expand (fn);
103 #endif
104
105   msg (VM (1), _("%s: Opening Groff font file..."), fn);
106
107   where.filename = fn;
108   where.line_number = 1;
109   err_push_file_locator (&where);
110
111   f = fopen (fn, "r");
112   if (!f)
113     goto file_lossage;
114
115   font_pool = pool_create ();
116   font = pool_alloc (font_pool, sizeof *font);
117   font->owner = font_pool;
118   font->name = NULL;
119   font->internal_name = NULL;
120   font->encoding = NULL;
121   font->space_width = 0;
122   font->slant = 0.0;
123   font->ligatures = 0;
124   font->special = 0;
125   font->deref = NULL;
126   font->deref_size = 0;
127   font->metric = NULL;
128   font->metric_size = 0;
129   font->metric_used = 0;
130   font->kern = NULL;
131   font->kern_size = 8;
132   font->kern_used = 0;
133   font->kern_max_used = 0;
134
135   /* Parses first section of font file. */
136   for (;;)
137     {
138       /* Location of '#' in line. */
139       char *p;
140
141       len = getline (&line, &size, f);
142       if (len == -1)
143         break;
144       
145       scan_badchars (line, len);
146       p = strchr (line, '#');
147       if (p)
148         *p = '\0';              /* Reject comments. */
149
150       key = strtok_r (line, whitespace, &sp);
151       if (!key)
152         goto next_iteration;
153
154       if (!strcmp (key, "internalname"))
155         {
156           font->internal_name = strtok_r (NULL, whitespace, &sp);
157           if (font->internal_name == NULL)
158             {
159               font_msg (SE, _("Missing font name."));
160               goto lose;
161             }
162           font->internal_name = pool_strdup (font_pool, font->internal_name);
163         }
164       else if (!strcmp (key, "encoding"))
165         {
166           font->encoding = strtok_r (NULL, whitespace, &sp);
167           if (font->encoding == NULL)
168             {
169               font_msg (SE, _("Missing encoding filename."));
170               goto lose;
171             }
172           font->encoding = pool_strdup (font_pool, font->encoding);
173         }
174       else if (!strcmp (key, "spacewidth"))
175         {
176           char *n = strtok_r (NULL, whitespace, &sp);
177           char *tail;
178           if (n)
179             font->space_width = strtol (n, &tail, 10);
180           if (n == NULL || tail == n)
181             {
182               font_msg (SE, _("Bad spacewidth value."));
183               goto lose;
184             }
185         }
186       else if (!strcmp (key, "slant"))
187         {
188           char *n = strtok_r (NULL, whitespace, &sp);
189           char *tail;
190           if (n)
191             font->slant = strtod (n, &tail);
192           if (n == NULL || tail == n)
193             {
194               font_msg (SE, _("Bad slant value."));
195               goto lose;
196             }
197         }
198       else if (!strcmp (key, "ligatures"))
199         {
200           char *lig;
201
202           for (;;)
203             {
204               lig = strtok_r (NULL, whitespace, &sp);
205               if (!lig || !strcmp (lig, "0"))
206                 break;
207               else if (!strcmp (lig, "ff"))
208                 font->ligatures |= LIG_ff;
209               else if (!strcmp (lig, "ffi"))
210                 font->ligatures |= LIG_ffi;
211               else if (!strcmp (lig, "ffl"))
212                 font->ligatures |= LIG_ffl;
213               else if (!strcmp (lig, "fi"))
214                 font->ligatures |= LIG_fi;
215               else if (!strcmp (lig, "fl"))
216                 font->ligatures |= LIG_fl;
217               else
218                 {
219                   font_msg (SE, _("Unknown ligature `%s'."), lig);
220                   goto lose;
221                 }
222             }
223         }
224       else if (!strcmp (key, "special"))
225         font->special = 1;
226       else if (!strcmp (key, "charset") || !strcmp (key, "kernpairs"))
227         break;
228
229       where.line_number++;
230     }
231   if (ferror (f))
232     goto file_lossage;
233
234   /* Parses second section of font file (metrics & kerning data). */
235   do
236     {
237       key = strtok_r (line, whitespace, &sp);
238       if (!key)
239         goto next_iteration;
240
241       if (!strcmp (key, "charset"))
242         charset = 1;
243       else if (!strcmp (key, "kernpairs"))
244         charset = 0;
245       else if (charset)
246         {
247           struct char_metrics *metrics = pool_alloc (font_pool,
248                                                      sizeof *metrics);
249           char *m, *type, *code, *tail;
250
251           m = strtok_r (NULL, whitespace, &sp);
252           if (!m)
253             {
254               font_msg (SE, _("Unexpected end of line reading character "
255                               "set."));
256               goto lose;
257             }
258           if (!strcmp (m, "\""))
259             {
260               if (!prev_index)
261                 {
262                   font_msg (SE, _("Can't use ditto mark for first character."));
263                   goto lose;
264                 }
265               if (!strcmp (key, "---"))
266                 {
267                   font_msg (SE, _("Can't ditto into an unnamed character."));
268                   goto lose;
269                 }
270               dup_char_metric (font, font_char_name_to_index (key), prev_index);
271               where.line_number++;
272               goto next_iteration;
273             }
274
275           if (m)
276             {
277               metrics->code = metrics->width
278                 = metrics->height = metrics->depth = 0;
279             }
280           
281           if (m == NULL || 1 > sscanf (m, "%d,%d,%d", &metrics->width,
282                                        &metrics->height, &metrics->depth))
283             {
284               font_msg (SE, _("Missing metrics for character `%s'."), key);
285               goto lose;
286             }
287
288           type = strtok_r (NULL, whitespace, &sp);
289           if (type)
290             metrics->type = strtol (type, &tail, 10);
291           if (!type || tail == type)
292             {
293               font_msg (SE, _("Missing type for character `%s'."), key);
294               goto lose;
295             }
296
297           code = strtok_r (NULL, whitespace, &sp);
298           if (code)
299             metrics->code = strtol (code, &tail, 0);
300           if (tail == code)
301             {
302               font_msg (SE, _("Missing code for character `%s'."), key);
303               goto lose;
304             }
305
306           if (strcmp (key, "---"))
307             prev_index = font_char_name_to_index (key);
308           else
309             prev_index = font_number_to_index (metrics->code);
310           add_char_metric (font, metrics, prev_index);
311         }
312       else
313         {
314           char *c1 = key;
315           char *c2 = strtok_r (NULL, whitespace, &sp);
316           char *n, *tail;
317           int adjust;
318
319           if (c2 == NULL)
320             {
321               font_msg (SE, _("Malformed kernpair."));
322               goto lose;
323             }
324
325           n = strtok_r (NULL, whitespace, &sp);
326           if (!n)
327             {
328               font_msg (SE, _("Unexpected end of line reading kernpairs."));
329               goto lose;
330             }
331           adjust = strtol (n, &tail, 10);
332           if (tail == n || *tail)
333             {
334               font_msg (SE, _("Bad kern value."));
335               goto lose;
336             }
337           add_kern (font, font_char_name_to_index (c1),
338                     font_char_name_to_index (c2), adjust);
339         }
340
341     next_iteration:
342       where.line_number++;
343
344       len = getline (&line, &size, f);
345     }
346   while (len != -1);
347   
348   if (ferror (f))
349     goto file_lossage;
350   if (fclose (f) == EOF)
351     {
352       f = NULL;
353       goto file_lossage;
354     }
355   free (line);
356 #ifdef unix
357   free ((char *) fn);
358 #endif
359
360   /* Get font ascent and descent. */
361   metrics = font_get_char_metrics (font, font_char_name_to_index ("d"));
362   font->ascent = metrics ? metrics->height : 0;
363   metrics = font_get_char_metrics (font, font_char_name_to_index ("p"));
364   font->descent = metrics ? metrics->depth : 0;
365
366   msg (VM (2), _("Font read successfully with internal name %s."),
367        font->internal_name == NULL ? "<none>" : font->internal_name);
368   
369   err_pop_file_locator (&where);
370
371   return font;
372
373   /* Come here on a file error. */
374 file_lossage:
375   msg (ME, "%s: %s", fn, strerror (errno));
376
377   /* Come here on any error. */
378 lose:
379   if (f != NULL)
380     fclose (f);
381   pool_destroy (font_pool);
382 #ifdef unix
383   free ((char *) fn);
384 #endif
385   err_pop_file_locator (&where);
386
387   msg (VM (1), _("Error reading font."));
388   return NULL;
389 }
390
391 /* Prints a font error on stderr. */
392 static int
393 font_msg (int class, const char *format,...)
394 {
395   va_list args;
396
397   va_start (args, format);
398   tmsg (class, format, args, _("installation error: Groff font error: "));
399   va_end (args);
400
401   return 0;
402 }
403
404 /* Scans string LINE of length LEN (not incl. null terminator) for bad
405    characters, converts to spaces; reports warnings on file FN. */
406 static void
407 scan_badchars (char *line, int len)
408 {
409   char *cp = line;
410
411   /* Same bad characters as Groff. */
412   static unsigned char badchars[32] =
413   {
414     0x01, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
415     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
416     0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
417     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
418   };
419
420   for (; len--; cp++) 
421     {
422       int c = (unsigned char) *cp;
423       if (badchars[c >> 3] & (1 << (c & 7)))
424         {
425           font_msg (SE, _("Bad character \\%3o."), *cp);
426           *cp = ' ';
427         } 
428     }
429 }
430 \f
431 /* Character name hashing. */
432
433 /* Associates a character index with a character name. */
434 struct index_hash
435   {
436     char *name;
437     int index;
438   };
439
440 /* Character index hash table. */
441 static struct
442   {
443     int size;                   /* Size of table (must be power of 2). */
444     int used;                   /* Number of full entries. */
445     int next_index;             /* Next index to allocate. */
446     struct index_hash *tab;     /* Hash table proper. */
447     struct pool *ar;            /* Pool for names. */
448   }
449 hash;
450
451 void
452 groff_init (void)
453 {
454   space_index = font_char_name_to_index ("space");
455 }
456
457 void
458 groff_done (void)
459 {
460   free (hash.tab) ;
461   pool_destroy(hash.ar);
462 }
463
464
465 /* Searches for NAME in the global character code table, returns the
466    index if found; otherwise inserts NAME and returns the new
467    index. */
468 int
469 font_char_name_to_index (const char *name)
470 {
471   int i;
472
473   if (name[0] == ' ')
474     return space_index;
475   if (name[0] == '\0' || name[1] == '\0')
476     return name[0];
477   if (0 == strncmp (name, "char", 4))
478     {
479       char *tail;
480       int x = strtol (name + 4, &tail, 10);
481       if (tail != name + 4 && *tail == 0 && x >= 0 && x <= 255)
482         return x;
483     }
484
485   if (!hash.tab)
486     {
487       hash.size = 128;
488       hash.used = 0;
489       hash.next_index = 256;
490       hash.tab = xmalloc (sizeof *hash.tab * hash.size);
491       hash.ar = pool_create ();
492       for (i = 0; i < hash.size; i++)
493         hash.tab[i].name = NULL;
494     }
495
496   for (i = hsh_hash_string (name) & (hash.size - 1); hash.tab[i].name; )
497     {
498       if (!strcmp (hash.tab[i].name, name))
499         return hash.tab[i].index;
500       if (++i >= hash.size)
501         i = 0;
502     }
503
504   hash.used++;
505   if (hash.used >= hash.size / 2)
506     {
507       struct index_hash *old_tab = hash.tab;
508       int old_size = hash.size;
509       int i, j;
510
511       hash.size *= 2;
512       hash.tab = xmalloc (sizeof *hash.tab * hash.size);
513       for (i = 0; i < hash.size; i++)
514         hash.tab[i].name = NULL;
515       for (i = 0; i < old_size; i++)
516         if (old_tab[i].name)
517           {
518             for (j = hsh_hash_string (old_tab[i].name) & (hash.size - 1);
519                  hash.tab[j].name;)
520               if (++j >= hash.size)
521                 j = 0;
522             hash.tab[j] = old_tab[i];
523           }
524       free (old_tab);
525     }
526
527   hash.tab[i].name = pool_strdup (hash.ar, name);
528   hash.tab[i].index = hash.next_index;
529   return hash.next_index++;
530 }
531
532 /* Returns an index for a character that has only a code, not a
533    name. */
534 int
535 font_number_to_index (int x)
536 {
537   char name[INT_DIGITS + 2];
538
539   /* Note that space is the only character that can't appear in a
540      character name.  That makes it an excellent choice for a name
541      that won't conflict. */
542   sprintf (name, " %d", x);
543   return font_char_name_to_index (name);
544 }
545 \f
546 /* Font character metric entries. */
547
548 /* Ensures room for at least MIN_SIZE metric indexes in deref of
549    FONT. */
550 static void
551 check_deref_space (struct font_desc *font, int min_size)
552 {
553   if (min_size >= font->deref_size)
554     {
555       int i = font->deref_size;
556
557       font->deref_size = min_size + 16;
558       if (font->deref_size < 256)
559         font->deref_size = 256;
560       font->deref = pool_realloc (font->owner, font->deref,
561                                   sizeof *font->deref * font->deref_size);
562       for (; i < font->deref_size; i++)
563         font->deref[i] = -1;
564     }
565 }
566
567 /* Inserts METRICS for character with code CODE into FONT. */
568 static void
569 add_char_metric (struct font_desc *font, struct char_metrics *metrics, int code)
570 {
571   check_deref_space (font, code);
572   if (font->metric_used >= font->metric_size)
573     {
574       font->metric_size += 64;
575       font->metric = pool_realloc (font->owner, font->metric,
576                                    sizeof *font->metric * font->metric_size);
577     }
578   font->metric[font->metric_used] = metrics;
579   font->deref[code] = font->metric_used++;
580 }
581
582 /* Copies metric in FONT from character with code SRC to character
583    with code DEST. */
584 static void
585 dup_char_metric (struct font_desc *font, int dest, int src)
586 {
587   check_deref_space (font, dest);
588   assert (font->deref[src] != -1);
589   font->deref[dest] = font->deref[src];
590 }
591 \f
592 /* Kerning. */
593
594 /* Returns a hash value for characters with codes CH1 and CH2. */
595 #define hash_kern(CH1, CH2)                     \
596         ((unsigned) (((CH1) << 16) ^ (CH2)))
597
598 /* Adds an ADJUST-size kern to FONT between characters with codes CH1
599    and CH2. */
600 static void
601 add_kern (struct font_desc *font, int ch1, int ch2, int adjust)
602 {
603   int i;
604
605   if (font->kern_used >= font->kern_max_used)
606     {
607       struct kern_pair *old_kern = font->kern;
608       int old_kern_size = font->kern_size;
609       int j;
610
611       font->kern_size *= 2;
612       font->kern_max_used = font->kern_size / 2;
613       font->kern = pool_malloc (font->owner,
614                                 sizeof *font->kern * font->kern_size);
615       for (i = 0; i < font->kern_size; i++)
616         font->kern[i].ch1 = -1;
617
618       if (old_kern)
619         {
620           for (i = 0; i < old_kern_size; i++)
621             {
622               if (old_kern[i].ch1 == -1)
623                 continue;
624
625               j = (hash_kern (old_kern[i].ch1, old_kern[i].ch2)
626                    & (font->kern_size - 1));
627               while (font->kern[j].ch1 != -1)
628                 if (0 == j--)
629                   j = font->kern_size - 1;
630               font->kern[j] = old_kern[i];
631             }
632           pool_free (font->owner, old_kern);
633         }
634     }
635
636   for (i = hash_kern (ch1, ch2) & (font->kern_size - 1);
637        font->kern[i].ch1 != -1; )
638     if (0 == i--)
639       i = font->kern_size - 1;
640   font->kern[i].ch1 = ch1;
641   font->kern[i].ch2 = ch2;
642   font->kern[i].adjust = adjust;
643   font->kern_used++;
644 }
645
646 /* Finds a font file corresponding to font NAME for device DEV. */
647 static char *
648 find_font_file (const char *dev, const char *name)
649 {
650   char *basename = xmalloc (3 + strlen (dev) + 1 + strlen (name) + 1);
651   char *cp;
652   char *filename;
653   char *path;
654
655   cp = stpcpy (basename, "dev");
656   cp = stpcpy (cp, dev);
657   *cp++ = DIR_SEPARATOR;
658   strcpy (cp, name);
659
660   /* Search order:
661      1. $STAT_GROFF_FONT_PATH
662      2. $GROFF_FONT_PATH
663      3. GROFF_FONT_PATH from pref.h
664      4. config_path
665    */
666   if ((path = getenv ("STAT_GROFF_FONT_PATH")) != NULL
667       && (filename = fn_search_path (basename, path, NULL)) != NULL)
668     goto win;
669
670   if ((path = getenv ("GROFF_FONT_PATH")) != NULL
671       && (filename = fn_search_path (basename, path, NULL)) != NULL)
672     goto win;
673
674   if ((filename = fn_search_path (basename, groff_font_path, NULL)) != NULL)
675     goto win;
676
677   if ((filename = fn_search_path (basename, config_path, NULL)) != NULL)
678     goto win;
679
680   msg (IE, _("Groff font error: Cannot find \"%s\"."), basename);
681
682 win:
683   free (basename);
684   return filename;
685 }
686
687 /* Finds a font for device DEV with name NAME, reads it with
688    groff_read_font(), and returns the resultant font. */
689 struct font_desc *
690 groff_find_font (const char *dev, const char *name)
691 {
692   char *filename = find_font_file (dev, name);
693   struct font_desc *fd;
694
695   if (!filename)
696     return NULL;
697   fd = groff_read_font (filename);
698   free (filename);
699   return fd;
700 }
701
702 /* Reads a DESC file for device DEV and sets the appropriate fields in
703    output driver *DRIVER, which must be previously allocated.  Returns
704    nonzero on success. */
705 int
706 groff_read_DESC (const char *dev_name, struct groff_device_info * dev)
707 {
708   char *filename;               /* Full name of DESC file. */
709   FILE *f;                      /* DESC file. */
710
711   char *line = NULL;            /* Current line. */
712   int line_len;                 /* Number of chars in current line. */
713   size_t line_size = 0;         /* Number of chars allocated for line. */
714
715   char *token;                  /* strtok()'d token inside line. */
716
717   unsigned found = 0;           /* Bitmask showing what settings
718                                    have been encountered. */
719
720   int m_sizes = 0;              /* Number of int[2] items that
721                                    can fit in driver->sizes. */
722
723   char *sp;                     /* Tokenization string pointer. */
724   struct file_locator where;
725
726   int i;
727
728   dev->horiz = 1;
729   dev->vert = 1;
730   dev->size_scale = 1;
731   dev->n_sizes = 0;
732   dev->sizes = NULL;
733   dev->family = NULL;
734   for (i = 0; i < 4; i++)
735     dev->font_name[i] = NULL;
736
737   filename = find_font_file (dev_name, "DESC");
738   if (!filename)
739     return 0;
740
741   where.filename = filename;
742   where.line_number = 0;
743   err_push_file_locator (&where);
744
745   msg (VM (1), _("%s: Opening Groff description file..."), filename);
746   f = fopen (filename, "r");
747   if (!f)
748     goto file_lossage;
749
750   while ((line_len = getline (&line, &line_size, f)) != -1)
751     {
752       where.line_number++;
753
754       token = strtok_r (line, whitespace, &sp);
755       if (!token)
756         continue;
757
758       if (!strcmp (token, "sizes"))
759         {
760           if (found & 0x10000)
761             font_msg (SW, _("Multiple `sizes' declarations."));
762           for (;;)
763             {
764               char *tail;
765               int lower, upper;
766
767               for (;;)
768                 {
769                   token = strtok_r (NULL, whitespace, &sp);
770                   if (token)
771                     break;
772
773                   where.line_number++;
774                   if ((line_len = getline (&line, &line_size, f)) != -1)
775                     {
776                       if (ferror (f))
777                         goto file_lossage;
778                       font_msg (SE, _("Unexpected end of file.  "
779                                 "Missing 0 terminator to `sizes' command?"));
780                       goto lossage;
781                     }
782                 }
783
784               if (!strcmp (token, "0"))
785                 break;
786
787               errno = 0;
788               if (0 == (lower = strtol (token, &tail, 0)) || errno == ERANGE)
789                 {
790                   font_msg (SE, _("Bad argument to `sizes'."));
791                   goto lossage;
792                 }
793               if (*tail == '-')
794                 {
795                   if (0 == (upper = strtol (&tail[1], &tail, 0)) || errno == ERANGE)
796                     {
797                       font_msg (SE, _("Bad argument to `sizes'."));
798                       goto lossage;
799                     }
800                   if (lower < upper)
801                     {
802                       font_msg (SE, _("Bad range in argument to `sizes'."));
803                       goto lossage;
804                     }
805                 }
806               else
807                 upper = lower;
808               if (*tail)
809                 {
810                   font_msg (SE, _("Bad argument to `sizes'."));
811                   goto lossage;
812                 }
813
814               if (dev->n_sizes + 2 >= m_sizes)
815                 {
816                   m_sizes += 1;
817                   dev->sizes = xrealloc (dev->sizes,
818                                          m_sizes * sizeof *dev->sizes);
819                 }
820               dev->sizes[dev->n_sizes++][0] = lower;
821               dev->sizes[dev->n_sizes][1] = upper;
822
823               found |= 0x10000;
824             }
825         }
826       else if (!strcmp (token, "family"))
827         {
828           token = strtok_r (NULL, whitespace, &sp);
829           if (!token)
830             {
831               font_msg (SE, _("Family name expected."));
832               goto lossage;
833             }
834           if (found & 0x20000)
835             {
836               font_msg (SE, _("This command already specified."));
837               goto lossage;
838             }
839           dev->family = xstrdup (token);
840         }
841       else if (!strcmp (token, "charset"))
842         break;
843       else
844         {
845           static const char *id[]
846             = {"res", "hor", "vert", "sizescale", "unitwidth", NULL};
847           const char **cp;
848           int value;
849
850           for (cp = id; *cp; cp++)
851             if (!strcmp (token, *cp))
852               break;
853           if (*cp == NULL)
854             continue;           /* completely ignore unrecognized lines */
855           if (found & (1 << (cp - id)))
856             font_msg (SW, _("%s: Device characteristic already defined."), *cp);
857
858           token = strtok_r (NULL, whitespace, &sp);
859           errno = 0;
860           if (!token || (value = strtol (token, NULL, 0)) <= 0 || errno == ERANGE)
861             {
862               font_msg (SE, _("%s: Invalid numeric format."), *cp);
863               goto lossage;
864             }
865           found |= (1 << (cp - id));
866           switch (cp - id)
867             {
868             case 0:
869               dev->res = value;
870               break;
871             case 1:
872               dev->horiz = value;
873               break;
874             case 2:
875               dev->vert = value;
876               break;
877             case 3:
878               dev->size_scale = value;
879               break;
880             case 4:
881               dev->unit_width = value;
882               break;
883             default:
884               assert (0);
885             }
886         }
887     }
888   if (ferror (f))
889     goto file_lossage;
890   if ((found & 0x10011) != 0x10011)
891     {
892       font_msg (SE, _("Missing `res', `unitwidth', and/or `sizes' line(s)."));
893       goto lossage;
894     }
895
896   /* Font name = family name + suffix. */
897   {
898     static const char *suffix[4] =
899       {"R", "I", "B", "BI"};    /* match OUTP_F_* */
900     int len;                    /* length of family name */
901     int i;
902
903     if (!dev->family)
904       dev->family = xstrdup ("");
905     len = strlen (dev->family);
906     for (i = 0; i < 4; i++)
907       {
908         char *cp;
909         dev->font_name[i] = xmalloc (len + strlen (suffix[i]) + 1);
910         cp = stpcpy (dev->font_name[i], dev->family);
911         strcpy (cp, suffix[i]);
912       }
913   }
914
915   dev->sizes[dev->n_sizes][0] = 0;
916   dev->sizes[dev->n_sizes][1] = 0;
917
918   msg (VM (2), _("Description file read successfully."));
919   
920   err_pop_file_locator (&where);
921   free (filename);
922   free (line);
923   return 1;
924
925   /* Come here on a file error. */
926 file_lossage:
927   msg (ME, "%s: %s", filename, strerror (errno));
928
929   /* Come here on any error. */
930 lossage:
931   fclose (f);
932   free (line);
933   free (dev->family);
934   dev->family = NULL;
935   free (filename);
936   free (dev->sizes);
937   dev->sizes = NULL;
938   dev->n_sizes = 0;
939 #if 0                           /* at the moment, no errors can come here when dev->font_name[*] are
940                                    nonzero. */
941   for (i = 0; i < 4; i++)
942     {
943       free (dev->font_name[i]);
944       dev->font_name[i] = NULL;
945     }
946 #endif
947
948   err_pop_file_locator (&where);
949   
950   msg (VM (1), _("Error reading description file."));
951   
952   return 0;
953 }
954
955 /* Finds character with index CH (as returned by name_to_index() or
956    number_to_index()) in font FONT and returns the associated metrics.
957    Nonexistent characters have width 0. */
958 struct char_metrics *
959 font_get_char_metrics (const struct font_desc *font, int ch)
960 {
961   short index;
962
963   if (ch < 0 || ch >= font->deref_size)
964     return 0;
965
966   index = font->deref[ch];
967   if (index == -1)
968     return 0;
969
970   return font->metric[index];
971 }
972
973 /* Finds kernpair consisting of CH1 and CH2, in that order, in font
974    FONT and returns the associated kerning adjustment. */
975 int
976 font_get_kern_adjust (const struct font_desc *font, int ch1, int ch2)
977 {
978   unsigned i;
979
980   if (!font->kern)
981     return 0;
982   for (i = hash_kern (ch1, ch2) & (font->kern_size - 1);
983        font->kern[i].ch1 != -1;)
984     {
985       if (font->kern[i].ch1 == ch1 && font->kern[i].ch2 == ch2)
986         return font->kern[i].adjust;
987       if (0 == i--)
988         i = font->kern_size - 1;
989     }
990   return 0;
991 }
992
993 /* Returns a twelve-point fixed-pitch font that can be used as a
994    last-resort fallback. */
995 struct font_desc *
996 default_font (void)
997 {
998   struct pool *font_pool;
999   static struct font_desc *font;
1000
1001   if (font)
1002     return font;
1003   font_pool = pool_create ();
1004   font = pool_alloc (font_pool, sizeof *font);
1005   font->owner = font_pool;
1006   font->name = NULL;
1007   font->internal_name = pool_strdup (font_pool, _("<<fallback>>"));
1008   font->encoding = pool_strdup (font_pool, "text.enc");
1009   font->space_width = 12000;
1010   font->slant = 0.0;
1011   font->ligatures = 0;
1012   font->special = 0;
1013   font->ascent = 8000;
1014   font->descent = 4000;
1015   font->deref = NULL;
1016   font->deref_size = 0;
1017   font->metric = NULL;
1018   font->metric_size = 0;
1019   font->metric_used = 0;
1020   font->kern = NULL;
1021   font->kern_size = 8;
1022   font->kern_used = 0;
1023   font->kern_max_used = 0;
1024   return font;
1025 }