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