1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
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.
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.
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
22 #include <libpspp/message.h>
28 #include <libpspp/alloc.h>
29 #include <libpspp/compiler.h>
30 #include <libpspp/message.h>
31 #include <data/filename.h>
33 #include <libpspp/hash.h>
35 #include <libpspp/pool.h>
36 #include <libpspp/str.h>
37 #include <libpspp/version.h>
40 #define _(msgid) gettext (msgid)
42 int font_number_to_index (int);
46 static int font_msg (int, const char *,...)
48 static void scan_badchars (char *, int);
49 static void dup_char_metric (struct font_desc * font, int dest, int src);
50 static void add_char_metric (struct font_desc * font, struct char_metrics *metrics,
52 static void add_kern (struct font_desc * font, int ch1, int ch2, int adjust);
54 /* Typical whitespace characters for tokenizing. */
55 static const char whitespace[] = " \t\n\r\v";
57 /* Some notes on the groff_font manpage:
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). */
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 for a description of the font
72 description format supported. Returns nonzero on success. */
74 groff_read_font (const char *fn)
76 struct char_metrics *metrics;
78 /* Pool created for font, font being created, font file. */
79 struct pool *font_pool = NULL;
80 struct font_desc *font = NULL;
83 /* Current line, size of line buffer, length of line. */
88 /* Tokenization saved pointer. */
91 /* First token on line. */
94 /* 0=kernpairs section, 1=charset section. */
97 /* Index for previous line. */
100 /* Current location in file, used for error reporting. */
101 struct file_locator where;
104 fn = fn_tilde_expand (fn);
107 msg (VM (1), _("%s: Opening Groff font file..."), fn);
110 where.line_number = 1;
111 err_push_file_locator (&where);
117 font_pool = pool_create ();
118 font = pool_alloc (font_pool, sizeof *font);
119 font->owner = font_pool;
121 font->internal_name = NULL;
122 font->encoding = NULL;
123 font->space_width = 0;
128 font->deref_size = 0;
130 font->metric_size = 0;
131 font->metric_used = 0;
135 font->kern_max_used = 0;
137 /* Parses first section of font file. */
140 /* Location of '#' in line. */
143 len = getline (&line, &size, f);
147 scan_badchars (line, len);
148 p = strchr (line, '#');
150 *p = '\0'; /* Reject comments. */
152 key = strtok_r (line, whitespace, &sp);
156 if (!strcmp (key, "internalname"))
158 font->internal_name = strtok_r (NULL, whitespace, &sp);
159 if (font->internal_name == NULL)
161 font_msg (SE, _("Missing font name."));
164 font->internal_name = pool_strdup (font_pool, font->internal_name);
166 else if (!strcmp (key, "encoding"))
168 font->encoding = strtok_r (NULL, whitespace, &sp);
169 if (font->encoding == NULL)
171 font_msg (SE, _("Missing encoding filename."));
174 font->encoding = pool_strdup (font_pool, font->encoding);
176 else if (!strcmp (key, "spacewidth"))
178 char *n = strtok_r (NULL, whitespace, &sp);
181 font->space_width = strtol (n, &tail, 10);
182 if (n == NULL || tail == n)
184 font_msg (SE, _("Bad spacewidth value."));
188 else if (!strcmp (key, "slant"))
190 char *n = strtok_r (NULL, whitespace, &sp);
193 font->slant = strtod (n, &tail);
194 if (n == NULL || tail == n)
196 font_msg (SE, _("Bad slant value."));
200 else if (!strcmp (key, "ligatures"))
206 lig = strtok_r (NULL, whitespace, &sp);
207 if (!lig || !strcmp (lig, "0"))
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;
221 font_msg (SE, _("Unknown ligature `%s'."), lig);
226 else if (!strcmp (key, "special"))
228 else if (!strcmp (key, "charset") || !strcmp (key, "kernpairs"))
236 /* Parses second section of font file (metrics & kerning data). */
239 key = strtok_r (line, whitespace, &sp);
243 if (!strcmp (key, "charset"))
245 else if (!strcmp (key, "kernpairs"))
249 struct char_metrics *metrics = pool_alloc (font_pool,
251 char *m, *type, *code, *tail;
253 m = strtok_r (NULL, whitespace, &sp);
256 font_msg (SE, _("Unexpected end of line reading character "
260 if (!strcmp (m, "\""))
264 font_msg (SE, _("Can't use ditto mark for first character."));
267 if (!strcmp (key, "---"))
269 font_msg (SE, _("Can't ditto into an unnamed character."));
272 dup_char_metric (font, font_char_name_to_index (key), prev_index);
279 metrics->code = metrics->width
280 = metrics->height = metrics->depth = 0;
283 if (m == NULL || 1 > sscanf (m, "%d,%d,%d", &metrics->width,
284 &metrics->height, &metrics->depth))
286 font_msg (SE, _("Missing metrics for character `%s'."), key);
290 type = strtok_r (NULL, whitespace, &sp);
292 metrics->type = strtol (type, &tail, 10);
293 if (!type || tail == type)
295 font_msg (SE, _("Missing type for character `%s'."), key);
299 code = strtok_r (NULL, whitespace, &sp);
301 metrics->code = strtol (code, &tail, 0);
304 font_msg (SE, _("Missing code for character `%s'."), key);
308 if (strcmp (key, "---"))
309 prev_index = font_char_name_to_index (key);
311 prev_index = font_number_to_index (metrics->code);
312 add_char_metric (font, metrics, prev_index);
317 char *c2 = strtok_r (NULL, whitespace, &sp);
323 font_msg (SE, _("Malformed kernpair."));
327 n = strtok_r (NULL, whitespace, &sp);
330 font_msg (SE, _("Unexpected end of line reading kernpairs."));
333 adjust = strtol (n, &tail, 10);
334 if (tail == n || *tail)
336 font_msg (SE, _("Bad kern value."));
339 add_kern (font, font_char_name_to_index (c1),
340 font_char_name_to_index (c2), adjust);
346 len = getline (&line, &size, f);
352 if (fclose (f) == EOF)
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;
368 msg (VM (2), _("Font read successfully with internal name %s."),
369 font->internal_name == NULL ? "<none>" : font->internal_name);
371 err_pop_file_locator (&where);
375 /* Come here on a file error. */
377 msg (ME, "%s: %s", fn, strerror (errno));
379 /* Come here on any error. */
383 pool_destroy (font_pool);
387 err_pop_file_locator (&where);
389 msg (VM (1), _("Error reading font."));
393 /* Prints a font error on stderr. */
395 font_msg (int class, const char *format,...)
401 err_location (&error.where);
402 error.title = _("installation error: Groff font error: ");
404 va_start (args, format);
405 err_vmsg (&error, format, args);
411 /* Scans string LINE of length LEN (not incl. null terminator) for bad
412 characters, converts to spaces; reports warnings on file FN. */
414 scan_badchars (char *line, int len)
418 /* Same bad characters as Groff. */
419 static unsigned char badchars[32] =
421 0x01, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
422 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
423 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
424 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
429 int c = (unsigned char) *cp;
430 if (badchars[c >> 3] & (1 << (c & 7)))
432 font_msg (SE, _("Bad character \\%3o."), *cp);
438 /* Character name hashing. */
440 /* Associates a character index with a character name. */
447 /* Character index hash table. */
450 int size; /* Size of table (must be power of 2). */
451 int used; /* Number of full entries. */
452 int next_index; /* Next index to allocate. */
453 struct index_hash *tab; /* Hash table proper. */
454 struct pool *ar; /* Pool for names. */
461 space_index = font_char_name_to_index ("space");
468 pool_destroy(hash.ar);
472 /* Searches for NAME in the global character code table, returns the
473 index if found; otherwise inserts NAME and returns the new
476 font_char_name_to_index (const char *name)
482 if (name[0] == '\0' || name[1] == '\0')
484 if (0 == strncmp (name, "char", 4))
487 int x = strtol (name + 4, &tail, 10);
488 if (tail != name + 4 && *tail == 0 && x >= 0 && x <= 255)
496 hash.next_index = 256;
497 hash.tab = xnmalloc (hash.size, sizeof *hash.tab);
498 hash.ar = pool_create ();
499 for (i = 0; i < hash.size; i++)
500 hash.tab[i].name = NULL;
503 for (i = hsh_hash_string (name) & (hash.size - 1); hash.tab[i].name; )
505 if (!strcmp (hash.tab[i].name, name))
506 return hash.tab[i].index;
507 if (++i >= hash.size)
512 if (hash.used >= hash.size / 2)
514 struct index_hash *old_tab = hash.tab;
515 int old_size = hash.size;
519 hash.tab = xnmalloc (hash.size, sizeof *hash.tab);
520 for (i = 0; i < hash.size; i++)
521 hash.tab[i].name = NULL;
522 for (i = 0; i < old_size; i++)
525 for (j = hsh_hash_string (old_tab[i].name) & (hash.size - 1);
527 if (++j >= hash.size)
529 hash.tab[j] = old_tab[i];
534 hash.tab[i].name = pool_strdup (hash.ar, name);
535 hash.tab[i].index = hash.next_index;
536 return hash.next_index++;
539 /* Returns an index for a character that has only a code, not a
542 font_number_to_index (int x)
544 char name[INT_STRLEN_BOUND (x) + 2];
546 /* Note that space is the only character that can't appear in a
547 character name. That makes it an excellent choice for a name
548 that won't conflict. */
549 sprintf (name, " %d", x);
550 return font_char_name_to_index (name);
553 /* Font character metric entries. */
555 /* Ensures room for at least MIN_SIZE metric indexes in deref of
558 check_deref_space (struct font_desc *font, int min_size)
560 if (min_size >= font->deref_size)
562 int i = font->deref_size;
564 font->deref_size = min_size + 16;
565 if (font->deref_size < 256)
566 font->deref_size = 256;
567 font->deref = pool_nrealloc (font->owner, font->deref,
568 font->deref_size, sizeof *font->deref);
569 for (; i < font->deref_size; i++)
574 /* Inserts METRICS for character with code CODE into FONT. */
576 add_char_metric (struct font_desc *font, struct char_metrics *metrics, int code)
578 check_deref_space (font, code);
579 if (font->metric_used >= font->metric_size)
581 font->metric_size += 64;
582 font->metric = pool_nrealloc (font->owner, font->metric,
583 font->metric_size, sizeof *font->metric);
585 font->metric[font->metric_used] = metrics;
586 font->deref[code] = font->metric_used++;
589 /* Copies metric in FONT from character with code SRC to character
592 dup_char_metric (struct font_desc *font, int dest, int src)
594 check_deref_space (font, dest);
595 assert (font->deref[src] != -1);
596 font->deref[dest] = font->deref[src];
601 /* Returns a hash value for characters with codes CH1 and CH2. */
602 #define hash_kern(CH1, CH2) \
603 ((unsigned) (((CH1) << 16) ^ (CH2)))
605 /* Adds an ADJUST-size kern to FONT between characters with codes CH1
608 add_kern (struct font_desc *font, int ch1, int ch2, int adjust)
612 if (font->kern_used >= font->kern_max_used)
614 struct kern_pair *old_kern = font->kern;
615 int old_kern_size = font->kern_size;
618 font->kern_size *= 2;
619 font->kern_max_used = font->kern_size / 2;
620 font->kern = pool_nmalloc (font->owner,
621 font->kern_size, sizeof *font->kern);
622 for (i = 0; i < font->kern_size; i++)
623 font->kern[i].ch1 = -1;
627 for (i = 0; i < old_kern_size; i++)
629 if (old_kern[i].ch1 == -1)
632 j = (hash_kern (old_kern[i].ch1, old_kern[i].ch2)
633 & (font->kern_size - 1));
634 while (font->kern[j].ch1 != -1)
636 j = font->kern_size - 1;
637 font->kern[j] = old_kern[i];
639 pool_free (font->owner, old_kern);
643 for (i = hash_kern (ch1, ch2) & (font->kern_size - 1);
644 font->kern[i].ch1 != -1; )
646 i = font->kern_size - 1;
647 font->kern[i].ch1 = ch1;
648 font->kern[i].ch2 = ch2;
649 font->kern[i].adjust = adjust;
653 /* Finds a font file corresponding to font NAME for device DEV. */
655 find_font_file (const char *dev, const char *name)
657 char *basename = xmalloc (3 + strlen (dev) + 1 + strlen (name) + 1);
662 cp = stpcpy (basename, "dev");
663 cp = stpcpy (cp, dev);
664 *cp++ = DIR_SEPARATOR;
668 1. $STAT_GROFF_FONT_PATH
670 3. GROFF_FONT_PATH from pref.h
673 if ((path = getenv ("STAT_GROFF_FONT_PATH")) != NULL
674 && (filename = fn_search_path (basename, path, NULL)) != NULL)
677 if ((path = getenv ("GROFF_FONT_PATH")) != NULL
678 && (filename = fn_search_path (basename, path, NULL)) != NULL)
681 if ((filename = fn_search_path (basename, groff_font_path, NULL)) != NULL)
684 if ((filename = fn_search_path (basename, config_path, NULL)) != NULL)
687 msg (IE, _("Groff font error: Cannot find \"%s\"."), basename);
694 /* Finds a font for device DEV with name NAME, reads it with
695 groff_read_font(), and returns the resultant font. */
697 groff_find_font (const char *dev, const char *name)
699 char *filename = find_font_file (dev, name);
700 struct font_desc *fd;
704 fd = groff_read_font (filename);
709 /* Reads a DESC file for device DEV and sets the appropriate fields in
710 output driver *DRIVER, which must be previously allocated. Returns
711 nonzero on success. */
713 groff_read_DESC (const char *dev_name, struct groff_device_info * dev)
715 char *filename; /* Full name of DESC file. */
716 FILE *f; /* DESC file. */
718 char *line = NULL; /* Current line. */
719 int line_len; /* Number of chars in current line. */
720 size_t line_size = 0; /* Number of chars allocated for line. */
722 char *token; /* strtok()'d token inside line. */
724 unsigned found = 0; /* Bitmask showing what settings
725 have been encountered. */
727 int m_sizes = 0; /* Number of int[2] items that
728 can fit in driver->sizes. */
730 char *sp; /* Tokenization string pointer. */
731 struct file_locator where;
741 for (i = 0; i < 4; i++)
742 dev->font_name[i] = NULL;
744 filename = find_font_file (dev_name, "DESC");
748 where.filename = filename;
749 where.line_number = 0;
750 err_push_file_locator (&where);
752 msg (VM (1), _("%s: Opening Groff description file..."), filename);
753 f = fopen (filename, "r");
757 while ((line_len = getline (&line, &line_size, f)) != -1)
761 token = strtok_r (line, whitespace, &sp);
765 if (!strcmp (token, "sizes"))
768 font_msg (SW, _("Multiple `sizes' declarations."));
776 token = strtok_r (NULL, whitespace, &sp);
781 if ((line_len = getline (&line, &line_size, f)) != -1)
785 font_msg (SE, _("Unexpected end of file. "
786 "Missing 0 terminator to `sizes' command?"));
791 if (!strcmp (token, "0"))
795 if (0 == (lower = strtol (token, &tail, 0)) || errno == ERANGE)
797 font_msg (SE, _("Bad argument to `sizes'."));
802 if (0 == (upper = strtol (&tail[1], &tail, 0)) || errno == ERANGE)
804 font_msg (SE, _("Bad argument to `sizes'."));
809 font_msg (SE, _("Bad range in argument to `sizes'."));
817 font_msg (SE, _("Bad argument to `sizes'."));
821 if (dev->n_sizes + 2 >= m_sizes)
824 dev->sizes = xnrealloc (dev->sizes,
825 m_sizes, sizeof *dev->sizes);
827 dev->sizes[dev->n_sizes++][0] = lower;
828 dev->sizes[dev->n_sizes][1] = upper;
833 else if (!strcmp (token, "family"))
835 token = strtok_r (NULL, whitespace, &sp);
838 font_msg (SE, _("Family name expected."));
843 font_msg (SE, _("This command already specified."));
846 dev->family = xstrdup (token);
848 else if (!strcmp (token, "charset"))
852 static const char *id[]
853 = {"res", "hor", "vert", "sizescale", "unitwidth", NULL};
857 for (cp = id; *cp; cp++)
858 if (!strcmp (token, *cp))
861 continue; /* completely ignore unrecognized lines */
862 if (found & (1 << (cp - id)))
863 font_msg (SW, _("%s: Device characteristic already defined."), *cp);
865 token = strtok_r (NULL, whitespace, &sp);
867 if (!token || (value = strtol (token, NULL, 0)) <= 0 || errno == ERANGE)
869 font_msg (SE, _("%s: Invalid numeric format."), *cp);
872 found |= (1 << (cp - id));
885 dev->size_scale = value;
888 dev->unit_width = value;
897 if ((found & 0x10011) != 0x10011)
899 font_msg (SE, _("Missing `res', `unitwidth', and/or `sizes' line(s)."));
903 /* Font name = family name + suffix. */
905 static const char *suffix[4] =
906 {"R", "I", "B", "BI"}; /* match OUTP_F_* */
907 int len; /* length of family name */
911 dev->family = xstrdup ("");
912 len = strlen (dev->family);
913 for (i = 0; i < 4; i++)
916 dev->font_name[i] = xmalloc (len + strlen (suffix[i]) + 1);
917 cp = stpcpy (dev->font_name[i], dev->family);
918 strcpy (cp, suffix[i]);
922 dev->sizes[dev->n_sizes][0] = 0;
923 dev->sizes[dev->n_sizes][1] = 0;
925 msg (VM (2), _("Description file read successfully."));
927 err_pop_file_locator (&where);
932 /* Come here on a file error. */
934 msg (ME, "%s: %s", filename, strerror (errno));
936 /* Come here on any error. */
946 #if 0 /* at the moment, no errors can come here when dev->font_name[*] are
948 for (i = 0; i < 4; i++)
950 free (dev->font_name[i]);
951 dev->font_name[i] = NULL;
955 err_pop_file_locator (&where);
957 msg (VM (1), _("Error reading description file."));
962 /* Finds character with index CH (as returned by name_to_index() or
963 number_to_index()) in font FONT and returns the associated metrics.
964 Nonexistent characters have width 0. */
965 struct char_metrics *
966 font_get_char_metrics (const struct font_desc *font, int ch)
970 if (ch < 0 || ch >= font->deref_size)
973 index = font->deref[ch];
977 return font->metric[index];
980 /* Finds kernpair consisting of CH1 and CH2, in that order, in font
981 FONT and returns the associated kerning adjustment. */
983 font_get_kern_adjust (const struct font_desc *font, int ch1, int ch2)
989 for (i = hash_kern (ch1, ch2) & (font->kern_size - 1);
990 font->kern[i].ch1 != -1;)
992 if (font->kern[i].ch1 == ch1 && font->kern[i].ch2 == ch2)
993 return font->kern[i].adjust;
995 i = font->kern_size - 1;
1000 /* Returns a twelve-point fixed-pitch font that can be used as a
1001 last-resort fallback. */
1005 struct pool *font_pool;
1006 static struct font_desc *font;
1010 font_pool = pool_create ();
1011 font = pool_alloc (font_pool, sizeof *font);
1012 font->owner = font_pool;
1014 font->internal_name = pool_strdup (font_pool, _("<<fallback>>"));
1015 font->encoding = pool_strdup (font_pool, "text.enc");
1016 font->space_width = 12000;
1018 font->ligatures = 0;
1020 font->ascent = 8000;
1021 font->descent = 4000;
1023 font->deref_size = 0;
1024 font->metric = NULL;
1025 font->metric_size = 0;
1026 font->metric_used = 0;
1028 font->kern_size = 8;
1029 font->kern_used = 0;
1030 font->kern_max_used = 0;