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., 59 Temple Place - Suite 330, Boston, MA
20 /* AIX requires this to be the first thing in the file. */
23 #define alloca __builtin_alloca
31 #ifndef alloca /* predefined by HP cc +Olibcalls */
38 /*this #if encloses the remainder of the file. */
51 #if TIME_WITH_SYS_TIME
63 #include "bitvector.h"
77 optimize-text-size not implemented.
79 Line buffering is the only possibility; page buffering should also
84 Should add a field to give a file that has a list of fonts
87 Should add an option that tells the driver it can emit %%Include:'s.
89 Should have auto-encode=true stream-edit or whatever to allow
90 addition to list of encodings.
92 Should align fonts of different sizes along their baselines (see
95 /* PostScript driver options: (defaults listed first)
99 data=clean7bit|clean8bit|binary
102 paper-size=letter (see "papersize" file)
103 orientation=portrait|landscape
112 prologue-file=ps-prologue
114 encoding-file=ps-encodings
115 auto-encode=true|false
121 line-style=thick|double
127 optimize-text-size=1|0|2
128 optimize-line-size=1|0
129 max-fonts-simult=0 Max # of fonts in printer memory at once (0=infinite)
132 /* The number of `psus' (PostScript driver UnitS) per inch. Although
133 this is a #define, the value is expected never to change. If it
134 does, review all uses. */
137 /* Magic numbers for PostScript and EPSF drivers. */
147 OTN_PORTRAIT, /* Portrait. */
148 OTN_LANDSCAPE /* Landscape. */
151 /* Output options. */
154 OPO_MIRROR_HORZ = 001, /* 1=Mirror across a horizontal axis. */
155 OPO_MIRROR_VERT = 002, /* 1=Mirror across a vertical axis. */
156 OPO_ROTATE_180 = 004, /* 1=Rotate the page 180 degrees. */
157 OPO_COLOR = 010, /* 1=Enable color. */
158 OPO_HEADERS = 020, /* 1=Draw headers at top of page. */
159 OPO_AUTO_ENCODE = 040, /* 1=Add encodings semi-intelligently. */
160 OPO_DOUBLE_LINE = 0100 /* 1=Double lines instead of thick lines. */
163 /* Data allowed in output. */
166 ODA_CLEAN7BIT, /* 0x09, 0x0a, 0x0d, 0x1b...0x7e */
167 ODA_CLEAN8BIT, /* 0x09, 0x0a, 0x0d, 0x1b...0xff */
168 ODA_BINARY, /* 0x00...0xff */
172 /* Types of lines for purpose of caching. */
175 horz, /* Single horizontal. */
176 dbl_horz, /* Double horizontal. */
177 spl_horz, /* Special horizontal. */
178 vert, /* Single vertical. */
179 dbl_vert, /* Double vertical. */
180 spl_vert, /* Special vertical. */
187 int ind; /* Independent var. Don't reorder. */
188 int mdep; /* Maximum number of dependent var pairs. */
189 int ndep; /* Current number of dependent var pairs. */
190 int dep[1][2]; /* Dependent var pairs. */
193 /* Contents of ps_driver_ext.loaded. */
196 char *dit; /* Font Groff name. */
197 struct font_desc *font; /* Font descriptor. */
200 /* Combines a font with a font size for benefit of generated code. */
203 struct font_entry *font; /* Font. */
204 int size; /* Font size. */
205 int index; /* PostScript index. */
208 /* A font encoding. */
211 char *filename; /* Normalized filename of this encoding. */
212 int index; /* Index value. */
215 /* PostScript output driver extension record. */
218 /* User parameters. */
219 int orientation; /* OTN_PORTRAIT or OTN_LANDSCAPE. */
220 int output_options; /* OPO_*. */
221 int data; /* ODA_*. */
223 int left_margin; /* Left margin in psus. */
224 int right_margin; /* Right margin in psus. */
225 int top_margin; /* Top margin in psus. */
226 int bottom_margin; /* Bottom margin in psus. */
228 char eol[3]; /* End of line--CR, LF, or CRLF. */
230 char *font_dir; /* Font directory relative to font path. */
231 char *prologue_fn; /* Prologue's filename relative to font dir. */
232 char *desc_fn; /* DESC filename relative to font dir. */
233 char *encoding_fn; /* Encoding's filename relative to font dir. */
235 char *prop_family; /* Default proportional font family. */
236 char *fixed_family; /* Default fixed-pitch font family. */
237 int font_size; /* Default font size (psus). */
239 int line_gutter; /* Space around lines. */
240 int line_space; /* Space between lines. */
241 int line_width; /* Width of lines. */
242 int line_width_thick; /* Width of thick lines. */
244 int text_opt; /* Text optimization level. */
245 int line_opt; /* Line optimization level. */
246 int max_fonts; /* Max # of simultaneous fonts (0=infinite). */
248 /* Internal state. */
249 struct file_ext file; /* Output file. */
250 int page_number; /* Current page number. */
251 int file_page_number; /* Page number in this file. */
252 int w, l; /* Paper size. */
253 struct hsh_table *lines[n_line_types]; /* Line buffers. */
255 struct font_entry *prop; /* Default Roman proportional font. */
256 struct font_entry *fixed; /* Default Roman fixed-pitch font. */
257 struct hsh_table *loaded; /* Fonts in memory. */
259 struct hsh_table *combos; /* Combinations of fonts with font sizes. */
260 struct ps_font_combo *last_font; /* PostScript selected font. */
261 int next_combo; /* Next font combo position index. */
263 struct hsh_table *encodings;/* Set of encodings. */
264 int next_encoding; /* Next font encoding index. */
266 /* Currently selected font. */
267 struct font_entry *current; /* Current font. */
268 char *family; /* Font family. */
269 int size; /* Size in psus. */
273 /* Transform logical y-ordinate Y into a page ordinate. */
274 #define YT(Y) (this->length - (Y))
277 static int postopen (struct file_ext *);
278 static int preclose (struct file_ext *);
279 static void draw_headers (struct outp_driver *this);
281 static int compare_font_entry (const void *, const void *, void *param);
282 static unsigned hash_font_entry (const void *, void *param);
283 static void free_font_entry (void *, void *foo);
284 static struct font_entry *load_font (struct outp_driver *, const char *dit);
285 static void init_fonts (void);
287 static void dump_lines (struct outp_driver *this);
289 static void read_ps_encodings (struct outp_driver *this);
290 static int compare_ps_encoding (const void *pa, const void *pb, void *foo);
291 static unsigned hash_ps_encoding (const void *pa, void *foo);
292 static void free_ps_encoding (void *a, void *foo);
293 static void add_encoding (struct outp_driver *this, char *filename);
294 static struct ps_encoding *default_encoding (struct outp_driver *this);
296 static int compare_ps_combo (const void *pa, const void *pb, void *foo);
297 static unsigned hash_ps_combo (const void *pa, void *foo);
298 static void free_ps_combo (void *a, void *foo);
300 static char *quote_ps_name (char *dest, const char *string);
301 static char *quote_ps_string (char *dest, const char *string);
303 /* Driver initialization. */
306 ps_open_global (struct outp_class *this unused)
314 ps_close_global (struct outp_class *this unused)
320 ps_font_sizes (struct outp_class *this unused, int *n_valid_sizes)
322 /* Allow fonts up to 1" in height. */
323 static int valid_sizes[] =
326 assert (n_valid_sizes != NULL);
332 ps_preopen_driver (struct outp_driver *this)
334 struct ps_driver_ext *x;
338 assert (this->driver_open == 0);
339 msg (VM (1), _("PostScript driver initializing as `%s'..."), this->name);
341 this->ext = x = xmalloc (sizeof (struct ps_driver_ext));
343 this->horiz = this->vert = 1;
344 this->width = this->length = 0;
346 x->orientation = OTN_PORTRAIT;
347 x->output_options = OPO_COLOR | OPO_HEADERS | OPO_AUTO_ENCODE;
348 x->data = ODA_CLEAN7BIT;
350 x->left_margin = x->right_margin =
351 x->top_margin = x->bottom_margin = PSUS / 2;
353 strcpy (x->eol, "\n");
356 x->prologue_fn = NULL;
358 x->encoding_fn = NULL;
360 x->prop_family = NULL;
361 x->fixed_family = NULL;
362 x->font_size = PSUS * 10 / 72;
364 x->line_gutter = PSUS / 144;
365 x->line_space = PSUS / 144;
366 x->line_width = PSUS / 144;
367 x->line_width_thick = PSUS / 48;
373 x->file.filename = NULL;
376 x->file.sequence_no = &x->page_number;
377 x->file.param = this;
378 x->file.postopen = postopen;
379 x->file.preclose = preclose;
383 x->file_page_number = 0;
384 for (i = 0; i < n_line_types; i++)
395 x->encodings = hsh_create (31, compare_ps_encoding, hash_ps_encoding,
396 free_ps_encoding, NULL);
397 x->next_encoding = 0;
407 ps_postopen_driver (struct outp_driver *this)
409 struct ps_driver_ext *x = this->ext;
411 assert (this->driver_open == 0);
413 if (this->width == 0)
415 this->width = PSUS * 17 / 2; /* Defaults to 8.5"x11". */
416 this->length = PSUS * 11;
419 if (x->text_opt == -1)
420 x->text_opt = (this->device & OUTP_DEV_SCREEN) ? 0 : 1;
421 if (x->line_opt == -1)
422 x->line_opt = (this->device & OUTP_DEV_SCREEN) ? 0 : 1;
426 if (x->orientation == OTN_LANDSCAPE)
428 int temp = this->width;
429 this->width = this->length;
432 this->width -= x->left_margin + x->right_margin;
433 this->length -= x->top_margin + x->bottom_margin;
434 if (x->output_options & OPO_HEADERS)
436 this->length -= 3 * x->font_size;
437 x->top_margin += 3 * x->font_size;
439 if (NULL == x->file.filename)
440 x->file.filename = xstrdup ("pspp.ps");
442 if (x->font_dir == NULL)
443 x->font_dir = xstrdup ("devps");
444 if (x->prologue_fn == NULL)
445 x->prologue_fn = xstrdup ("ps-prologue");
446 if (x->desc_fn == NULL)
447 x->desc_fn = xstrdup ("DESC");
448 if (x->encoding_fn == NULL)
449 x->encoding_fn = xstrdup ("ps-encodings");
451 if (x->prop_family == NULL)
452 x->prop_family = xstrdup ("H");
453 if (x->fixed_family == NULL)
454 x->fixed_family = xstrdup ("C");
456 read_ps_encodings (this);
461 if (this->length / x->font_size < 15)
463 msg (SE, _("PostScript driver: The defined page is not long "
464 "enough to hold margins and headers, plus least 15 "
465 "lines of the default fonts. In fact, there's only "
466 "room for %d lines of each font at the default size "
467 "of %d.%03d points."),
468 this->length / x->font_size,
469 x->font_size / 1000, x->font_size % 1000);
473 this->driver_open = 1;
474 msg (VM (2), _("%s: Initialization complete."), this->name);
480 ps_close_driver (struct outp_driver *this)
482 struct ps_driver_ext *x = this->ext;
486 assert (this->driver_open == 1);
487 msg (VM (2), _("%s: Beginning closing..."), this->name);
489 fn_close_ext (&x->file);
490 free (x->file.filename);
492 free (x->prologue_fn);
494 free (x->encoding_fn);
495 free (x->prop_family);
496 free (x->fixed_family);
498 for (i = 0; i < n_line_types; i++)
499 hsh_destroy (x->lines[i]);
500 hsh_destroy (x->encodings);
501 hsh_destroy (x->combos);
502 hsh_destroy (x->loaded);
505 this->driver_open = 0;
506 msg (VM (3), _("%s: Finished closing."), this->name);
511 /* font_entry comparison function for hash tables. */
513 compare_font_entry (const void *a, const void *b, void *foobar unused)
515 return strcmp (((struct font_entry *) a)->dit, ((struct font_entry *) b)->dit);
518 /* font_entry hash function for hash tables. */
520 hash_font_entry (const void *a, void *foobar unused)
522 return hashpjw (((struct font_entry *) a)->dit);
525 /* font_entry destructor function for hash tables. */
527 free_font_entry (void *pa, void *foo unused)
529 struct font_entry *a = pa;
534 /* Generic option types. */
544 /* All the options that the PostScript driver supports. */
545 static struct outp_option option_tab[] =
548 {"output-file", 1, 0},
549 {"paper-size", 2, 0},
550 {"orientation", 3, 0},
551 {"color", boolean_arg, 0},
553 {"auto-encode", boolean_arg, 5},
554 {"headers", boolean_arg, 1},
555 {"left-margin", pos_int_arg, 0},
556 {"right-margin", pos_int_arg, 1},
557 {"top-margin", pos_int_arg, 2},
558 {"bottom-margin", pos_int_arg, 3},
559 {"font-dir", string_arg, 0},
560 {"prologue-file", string_arg, 1},
561 {"device-file", string_arg, 2},
562 {"encoding-file", string_arg, 3},
563 {"prop-font-family", string_arg, 5},
564 {"fixed-font-family", string_arg, 6},
565 {"font-size", pos_int_arg, 4},
566 {"optimize-text-size", nonneg_int_arg, 0},
567 {"optimize-line-size", nonneg_int_arg, 1},
568 {"max-fonts-simult", nonneg_int_arg, 2},
570 {"line-style", 7, 0},
571 {"line-width", dimension_arg, 2},
572 {"line-gutter", dimension_arg, 3},
573 {"line-width", dimension_arg, 4},
574 {"line-width-thick", dimension_arg, 5},
578 static struct outp_option_info option_info;
581 ps_option (struct outp_driver *this, const char *key, const struct string *val)
583 struct ps_driver_ext *x = this->ext;
585 char *value = ds_value (val);
587 cat = outp_match_keyword (key, option_tab, &option_info, &subcat);
592 msg (SE, _("Unknown configuration parameter `%s' for PostScript device "
596 free (x->file.filename);
597 x->file.filename = xstrdup (value);
600 outp_get_paper_size (value, &this->width, &this->length);
603 if (!strcmp (value, "portrait"))
604 x->orientation = OTN_PORTRAIT;
605 else if (!strcmp (value, "landscape"))
606 x->orientation = OTN_LANDSCAPE;
608 msg (SE, _("Unknown orientation `%s'. Valid orientations are "
609 "`portrait' and `landscape'."), value);
612 if (!strcmp (value, "clean7bit") || !strcmp (value, "Clean7Bit"))
613 x->data = ODA_CLEAN7BIT;
614 else if (!strcmp (value, "clean8bit")
615 || !strcmp (value, "Clean8Bit"))
616 x->data = ODA_CLEAN8BIT;
617 else if (!strcmp (value, "binary") || !strcmp (value, "Binary"))
618 x->data = ODA_BINARY;
620 msg (SE, _("Unknown value for `data'. Valid values are `clean7bit', "
621 "`clean8bit', and `binary'."));
624 if (!strcmp (value, "lf"))
625 strcpy (x->eol, "\n");
626 else if (!strcmp (value, "crlf"))
627 strcpy (x->eol, "\r\n");
629 msg (SE, _("Unknown value for `line-ends'. Valid values are `lf' and "
633 if (!strcmp (value, "thick"))
634 x->output_options &= ~OPO_DOUBLE_LINE;
635 else if (!strcmp (value, "double"))
636 x->output_options |= OPO_DOUBLE_LINE;
638 msg (SE, _("Unknown value for `line-style'. Valid values are `thick' "
646 if (!strcmp (value, "on") || !strcmp (value, "true")
647 || !strcmp (value, "yes") || atoi (value))
649 else if (!strcmp (value, "off") || !strcmp (value, "false")
650 || !strcmp (value, "no") || !strcmp (value, "0"))
654 msg (SE, _("Boolean value expected for %s."), key);
666 mask = OPO_MIRROR_HORZ;
669 mask = OPO_MIRROR_VERT;
672 mask = OPO_ROTATE_180;
675 mask = OPO_AUTO_ENCODE;
681 x->output_options |= mask;
683 x->output_options &= ~mask;
692 arg = strtol (value, &tail, 0);
693 if (arg < 1 || errno == ERANGE || *tail)
695 msg (SE, _("Positive integer required as value for `%s'."), key);
698 if ((subcat == 4 || subcat == 5) && arg < 1000)
700 msg (SE, _("Default font size must be at least 1 point (value "
701 "of 1000 for key `%s')."), key);
707 x->left_margin = arg;
710 x->right_margin = arg;
716 x->bottom_margin = arg;
728 int dimension = outp_evaluate_dimension (value, NULL);
732 msg (SE, _("Value for `%s' must be a dimension of positive "
733 "length (i.e., `1in')."), key);
739 x->line_width = dimension;
742 x->line_gutter = dimension;
745 x->line_width = dimension;
748 x->line_width_thick = dimension;
764 dest = &x->prologue_fn;
770 dest = &x->encoding_fn;
773 dest = &x->prop_family;
776 dest = &x->fixed_family;
783 *dest = xstrdup (value);
792 arg = strtol (value, &tail, 0);
793 if (arg < 0 || errno == ERANGE || *tail)
795 msg (SE, _("Nonnegative integer required as value for `%s'."), key);
823 /* Looks for a PostScript font file or config file in all the
824 appropriate places. Returns the filename on success, NULL on
826 /* PORTME: Filename operations. */
828 find_ps_file (struct outp_driver *this, const char *name)
830 struct ps_driver_ext *x = this->ext;
833 /* x->font_dir + name: "devps/ps-encodings". */
836 /* Usually equal to groff_font_path. */
839 /* Final filename. */
843 basename = local_alloc (strlen (x->font_dir) + 1 + strlen (name) + 1);
844 cp = stpcpy (basename, x->font_dir);
845 *cp++ = DIR_SEPARATOR;
848 /* Decide on search path. */
850 const char *pre_pathname;
852 pre_pathname = getenv ("STAT_GROFF_FONT_PATH");
853 if (pre_pathname == NULL)
854 pre_pathname = getenv ("GROFF_FONT_PATH");
855 if (pre_pathname == NULL)
856 pre_pathname = groff_font_path;
857 pathname = fn_tilde_expand (pre_pathname);
860 /* Search all possible places for the file. */
861 fn = fn_search_path (basename, pathname, NULL);
863 fn = fn_search_path (basename, config_path, NULL);
865 fn = fn_search_path (name, pathname, NULL);
867 fn = fn_search_path (name, config_path, NULL);
869 local_free (basename);
876 /* Hash table comparison function for ps_encoding's. */
878 compare_ps_encoding (const void *pa, const void *pb, void *foo unused)
880 const struct ps_encoding *a = pa;
881 const struct ps_encoding *b = pb;
883 return strcmp (a->filename, b->filename);
886 /* Hash table hash function for ps_encoding's. */
888 hash_ps_encoding (const void *pa, void *foo unused)
890 const struct ps_encoding *a = pa;
892 return hashpjw (a->filename);
895 /* Hash table free function for ps_encoding's. */
897 free_ps_encoding (void *pa, void *foo unused)
899 struct ps_encoding *a = pa;
905 /* Iterates through the list of encodings used for this driver
906 instance, reads each of them from disk, and writes them as
907 PostScript code to the output file. */
909 output_encodings (struct outp_driver *this)
911 struct ps_driver_ext *x = this->ext;
913 struct hsh_iterator iter;
914 struct ps_encoding *pe;
916 struct string line, buf;
918 ds_init (NULL, &line, 128);
919 ds_init (NULL, &buf, 128);
920 hsh_iterator_init (iter);
921 while ((pe = hsh_foreach (x->encodings, &iter)) != NULL)
925 msg (VM (1), _("%s: %s: Opening PostScript font encoding..."),
926 this->name, pe->filename);
928 f = fopen (pe->filename, "r");
931 msg (IE, _("PostScript driver: Cannot open encoding file `%s': %s. "
932 "Substituting ISOLatin1Encoding for missing encoding."),
933 pe->filename, strerror (errno));
934 fprintf (x->file.file, "/E%x ISOLatin1Encoding def%s",
939 struct file_locator where;
941 const char *tab[256];
948 const char *notdef = ".notdef";
952 for (i = 0; i < 256; i++)
955 where.filename = pe->filename;
956 where.line_number = 0;
957 err_push_file_locator (&where);
959 while (ds_get_config_line (f, &buf, &where))
963 pschar = strtok_r (ds_value (&buf), " \t\r\n", &sp);
964 code = strtok_r (NULL, " \t\r\n", &sp);
965 if (*pschar == 0 || *code == 0)
967 code_val = strtol (code, &fubar, 0);
970 msg (IS, _("PostScript driver: Invalid numeric format."));
973 if (code_val < 0 || code_val > 255)
975 msg (IS, _("PostScript driver: Codes must be between 0 "
976 "and 255. (%d is not allowed.)"), code_val);
979 tab[code_val] = local_alloc (strlen (pschar) + 1);
980 strcpy ((char *) (tab[code_val]), pschar);
982 err_pop_file_locator (&where);
985 ds_printf (&line, "/E%x[", pe->index);
986 for (i = 0; i < 257; i++)
992 quote_ps_name (temp, tab[i]);
993 if (tab[i] != notdef)
997 strcpy (temp, "]def");
999 if (ds_length (&line) + strlen (temp) > 70)
1001 ds_concat (&line, x->eol);
1002 fputs (ds_value (&line), x->file.file);
1005 ds_concat (&line, temp);
1007 ds_concat (&line, x->eol);
1008 fputs (ds_value (&line), x->file.file);
1010 if (fclose (f) == EOF)
1011 msg (MW, _("PostScript driver: Error closing encoding file `%s'."),
1014 msg (VM (2), _("%s: PostScript font encoding read successfully."),
1022 /* Finds the ps_encoding in THIS that corresponds to the file with
1023 name NORM_FILENAME, which must have previously been normalized with
1024 normalize_filename(). */
1025 static struct ps_encoding *
1026 get_encoding (struct outp_driver *this, const char *norm_filename)
1028 struct ps_driver_ext *x = this->ext;
1029 struct ps_encoding *pe;
1031 pe = (struct ps_encoding *) hsh_find (x->encodings, (void *) &norm_filename);
1035 /* Searches the filesystem for an encoding file with name FILENAME;
1036 returns its malloc'd, normalized name if found, otherwise NULL. */
1038 find_encoding_file (struct outp_driver *this, char *filename)
1042 if (filename == NULL)
1044 while (isspace ((unsigned char) *filename))
1046 for (cp = filename; *cp && !isspace ((unsigned char) *cp); cp++)
1052 temp = find_ps_file (this, filename);
1056 filename = fn_normalize (temp);
1057 assert (filename != NULL);
1063 /* Adds the encoding represented by the not-necessarily-normalized
1064 file FILENAME to the list of encodings, if it exists and is not
1065 already in the list. */
1067 add_encoding (struct outp_driver *this, char *filename)
1069 struct ps_driver_ext *x = this->ext;
1071 struct ps_encoding **pe;
1073 filename = find_encoding_file (this, filename);
1077 pe = (struct ps_encoding **) hsh_probe (x->encodings, (void *) &filename);
1083 *pe = xmalloc (sizeof **pe);
1084 (*pe)->filename = filename;
1085 (*pe)->index = x->next_encoding++;
1088 /* Finds the file on disk that contains the list of encodings to
1089 include in the output file, then adds those encodings to the list
1092 read_ps_encodings (struct outp_driver *this)
1094 struct ps_driver_ext *x = this->ext;
1096 /* Encodings file. */
1097 char *encoding_fn; /* `ps-encodings' filename. */
1101 struct file_locator where;
1103 /* It's okay if there's no list of encodings; not everyone cares. */
1104 encoding_fn = find_ps_file (this, x->encoding_fn);
1105 if (encoding_fn == NULL)
1109 msg (VM (1), _("%s: %s: Opening PostScript encoding list file."),
1110 this->name, encoding_fn);
1111 f = fopen (encoding_fn, "r");
1114 msg (IE, _("Opening %s: %s."), encoding_fn, strerror (errno));
1118 where.filename = encoding_fn;
1119 where.line_number = 0;
1120 err_push_file_locator (&where);
1122 ds_init (NULL, &line, 128);
1128 if (!ds_get_config_line (f, &line, &where))
1131 msg (ME, _("Reading %s: %s."), encoding_fn, strerror (errno));
1135 add_encoding (this, bp);
1139 err_pop_file_locator (&where);
1141 if (-1 == fclose (f))
1142 msg (MW, _("Closing %s: %s."), encoding_fn, strerror (errno));
1144 msg (VM (2), _("%s: PostScript encoding list file read successfully."), this->name);
1147 /* Creates a default encoding for driver D that can be substituted for
1148 an unavailable encoding. */
1149 struct ps_encoding *
1150 default_encoding (struct outp_driver *d)
1152 struct ps_driver_ext *x = d->ext;
1153 static struct ps_encoding *enc;
1157 enc = xmalloc (sizeof *enc);
1158 enc->filename = xstrdup (_("<<default encoding>>"));
1159 enc->index = x->next_encoding++;
1164 /* Basic file operations. */
1166 /* Variables for the prologue. */
1173 static struct ps_variable *ps_var_tab;
1175 /* Searches ps_var_tab for a ps_variable with key KEY, and returns the
1176 associated value. */
1178 ps_get_var (const char *key)
1180 struct ps_variable *v;
1182 for (v = ps_var_tab; v->key; v++)
1183 if (!strcmp (key, v->key))
1188 /* Writes the PostScript prologue to file F. */
1190 postopen (struct file_ext *f)
1192 static struct ps_variable dict[] =
1194 {"bounding-box", 0},
1203 {"scale-factor", 0},
1205 {"paper-length", 0},
1209 {"line-width-thick", 0},
1214 char boundbox[INT_DIGITS * 4 + 4];
1218 char scaling[INT_DIGITS + 5];
1222 char paper_width[INT_DIGITS + 1];
1223 char paper_length[INT_DIGITS + 1];
1224 char left_margin[INT_DIGITS + 1];
1225 char top_margin[INT_DIGITS + 1];
1226 char line_width[INT_DIGITS + 1];
1227 char line_width_thick[INT_DIGITS + 1];
1229 struct outp_driver *this = f->param;
1230 struct ps_driver_ext *x = this->ext;
1232 char *prologue_fn = find_ps_file (this, x->prologue_fn);
1233 FILE *prologue_file;
1236 size_t buf_size = 0;
1238 x->loaded = hsh_create (31, compare_font_entry, hash_font_entry,
1239 free_font_entry, NULL);
1242 char *font_name = local_alloc (2 + max (strlen (x->prop_family),
1243 strlen (x->fixed_family)));
1245 strcpy (stpcpy (font_name, x->prop_family), "R");
1246 x->prop = load_font (this, font_name);
1248 strcpy (stpcpy (font_name, x->fixed_family), "R");
1249 x->fixed = load_font (this, font_name);
1251 local_free(font_name);
1254 x->current = x->prop;
1255 x->family = xstrdup (x->prop_family);
1256 x->size = x->font_size;
1259 int *h = this->horiz_line_width, *v = this->vert_line_width;
1261 this->cp_x = this->cp_y = 0;
1262 this->font_height = x->font_size;
1264 struct char_metrics *metric;
1266 metric = font_get_char_metrics (x->prop->font, '0');
1267 this->prop_em_width = ((metric
1268 ? metric->width : x->prop->font->space_width)
1269 * x->font_size / 1000);
1271 metric = font_get_char_metrics (x->fixed->font, '0');
1272 this->fixed_width = ((metric
1273 ? metric->width : x->fixed->font->space_width)
1274 * x->font_size / 1000);
1278 h[1] = v[1] = 2 * x->line_gutter + x->line_width;
1279 if (x->output_options & OPO_DOUBLE_LINE)
1280 h[2] = v[2] = 2 * x->line_gutter + 2 * x->line_width + x->line_space;
1282 h[2] = v[2] = 2 * x->line_gutter + x->line_width_thick;
1283 h[3] = v[3] = 2 * x->line_gutter + x->line_width;
1288 for (i = 0; i < (1 << OUTP_L_COUNT); i++)
1292 /* Maximum width of any line type so far. */
1295 for (bit = 0; bit < OUTP_L_COUNT; bit++)
1296 if ((i & (1 << bit)) && h[bit] > max)
1298 this->horiz_line_spacing[i] = this->vert_line_spacing[i] = max;
1303 if (x->output_options & OPO_AUTO_ENCODE)
1305 /* It's okay if this is done more than once since add_encoding()
1306 is idempotent over identical encodings. */
1307 add_encoding (this, x->prop->font->encoding);
1308 add_encoding (this, x->fixed->font->encoding);
1311 x->file_page_number = 0;
1314 if (prologue_fn == NULL)
1316 msg (IE, _("Cannot find PostScript prologue. The use of `-vv' "
1317 "on the command line is suggested as a debugging aid."));
1321 msg (VM (1), _("%s: %s: Opening PostScript prologue..."),
1322 this->name, prologue_fn);
1323 prologue_file = fopen (prologue_fn, "rb");
1324 if (prologue_file == NULL)
1326 fclose (prologue_file);
1328 msg (IE, "%s: %s", prologue_fn, strerror (errno));
1332 sprintf (boundbox, "0 0 %d %d",
1333 x->w / (PSUS / 72) + (x->w % (PSUS / 72) > 0),
1334 x->l / (PSUS / 72) + (x->l % (PSUS / 72) > 0));
1335 dict[0].value = boundbox;
1337 dict[1].value = (char *) version;
1339 curtime = time (NULL);
1340 loctime = localtime (&curtime);
1341 dict[2].value = asctime (loctime);
1342 cp = strchr (dict[2].value, '\n');
1349 dict[3].value = "Clean7Bit";
1352 dict[3].value = "Clean8Bit";
1355 dict[3].value = "Binary";
1361 if (x->orientation == OTN_PORTRAIT)
1362 dict[4].value = "Portrait";
1364 dict[4].value = "Landscape";
1366 /* PORTME: Determine username, net address. */
1368 dict[5].value = getenv ("LOGNAME");
1370 dict[5].value = getlogin ();
1372 dict[5].value = _("nobody");
1374 if (gethostname (host, 128) == -1)
1376 if (errno == ENAMETOOLONG)
1379 strcpy (host, _("nowhere"));
1381 dict[6].value = host;
1382 #else /* !HAVE_UNISTD_H */
1383 dict[5].value = _("nobody");
1384 dict[6].value = _("nowhere");
1385 #endif /* !HAVE_UNISTD_H */
1387 cp = stpcpy (p = local_alloc (288), "font ");
1388 quote_ps_string (cp, x->prop->font->internal_name);
1391 cp = stpcpy (p = local_alloc (288), "font ");
1392 quote_ps_string (cp, x->fixed->font->internal_name);
1395 sprintf (scaling, "%.3f", PSUS / 72.0);
1396 dict[9].value = scaling;
1398 sprintf (paper_width, "%g", x->w / (PSUS / 72.0));
1399 dict[10].value = paper_width;
1401 sprintf (paper_length, "%g", x->l / (PSUS / 72.0));
1402 dict[11].value = paper_length;
1404 sprintf (left_margin, "%d", x->left_margin);
1405 dict[12].value = left_margin;
1407 sprintf (top_margin, "%d", x->top_margin);
1408 dict[13].value = top_margin;
1410 sprintf (line_width, "%d", x->line_width);
1411 dict[14].value = line_width;
1413 sprintf (line_width, "%d", x->line_width_thick);
1414 dict[15].value = line_width_thick;
1416 getl_location (&dict[17].value, NULL);
1417 if (dict[17].value == NULL)
1418 dict[17].value = "<stdin>";
1422 dict[16].value = cp = local_alloc (strlen (dict[17].value) + 30);
1423 sprintf (cp, "PSPP (%s)", dict[17].value);
1427 dict[16].value = local_alloc (strlen (outp_title) + 1);
1428 strcpy ((char *) (dict[16].value), outp_title);
1432 while (-1 != getline (&buf, &buf_size, prologue_file))
1438 cp = strstr (buf, "!eps");
1441 if (this->class->magic == MAGIC_PS)
1448 cp = strstr (buf, "!ps");
1451 if (this->class->magic == MAGIC_EPSF)
1456 if (strstr (buf, "!!!"))
1461 if (!strncmp (buf, "!encodings", 10))
1462 output_encodings (this);
1466 beg = buf2 = fn_interp_vars (buf, ps_get_var);
1467 len = strlen (buf2);
1468 while (isspace (*beg))
1470 if (beg[len - 1] == '\n')
1472 if (beg[len - 1] == '\r')
1474 fwrite (beg, len, 1, f->file);
1475 fputs (x->eol, f->file);
1479 if (ferror (f->file))
1480 msg (IE, _("Reading `%s': %s."), prologue_fn, strerror (errno));
1481 fclose (prologue_file);
1486 local_free (dict[7].value);
1487 local_free (dict[8].value);
1488 local_free (dict[16].value);
1490 if (ferror (f->file))
1493 msg (VM (2), _("%s: PostScript prologue read successfully."), this->name);
1497 msg (VM (1), _("%s: Error reading PostScript prologue."), this->name);
1501 /* Writes the string STRING to buffer DEST (of at least 288
1502 characters) as a PostScript name object. Returns a pointer
1503 to the null terminator of the resultant string. */
1505 quote_ps_name (char *dest, const char *string)
1509 for (sp = string; *sp; sp++)
1510 switch (*(unsigned char *) sp)
1583 for (sp = string; *sp && dp < &dest[256]; sp++)
1585 sprintf (dp, "%02x", *(unsigned char *) sp);
1588 return stpcpy (dp, ">cvn");
1592 return stpcpy (&dest[1], string);
1595 /* Adds the string STRING to buffer DEST as a PostScript quoted
1596 string; returns a pointer to the null terminator added. Will not
1597 add more than 235 characters. */
1599 quote_ps_string (char *dest, const char *string)
1601 const char *sp = string;
1605 for (; *sp && dp < &dest[235]; sp++)
1607 dp = stpcpy (dp, "\\(");
1608 else if (*sp == ')')
1609 dp = stpcpy (dp, "\\)");
1610 else if (*sp < 32 || *((unsigned char *) sp) > 127)
1611 dp = spprintf (dp, "\\%3o", *sp);
1614 return stpcpy (dp, ")");
1617 /* Writes the PostScript epilogue to file F. */
1619 preclose (struct file_ext *f)
1621 struct outp_driver *this = f->param;
1622 struct ps_driver_ext *x = this->ext;
1623 struct hsh_iterator iter;
1624 struct font_entry *fe;
1629 "%%%%DocumentNeededResources:%s"),
1630 x->eol, x->file_page_number, x->eol, x->eol);
1632 hsh_iterator_init (iter);
1633 while ((fe = hsh_foreach (x->loaded, &iter)) != NULL)
1637 cp = stpcpy (buf, "%%+ font ");
1638 cp = quote_ps_string (cp, fe->font->internal_name);
1639 strcpy (cp, x->eol);
1640 fputs (buf, f->file);
1643 hsh_destroy (x->loaded);
1645 hsh_destroy (x->combos);
1647 x->last_font = NULL;
1650 fprintf (f->file, "%%EOF%s", x->eol);
1651 if (ferror (f->file))
1657 ps_open_page (struct outp_driver *this)
1659 struct ps_driver_ext *x = this->ext;
1661 assert (this->driver_open && !this->page_open);
1664 if (!fn_open_ext (&x->file))
1667 msg (ME, _("PostScript output driver: %s: %s"), x->file.filename,
1671 x->file_page_number++;
1673 hsh_destroy (x->combos);
1674 x->combos = hsh_create (31, compare_ps_combo, hash_ps_combo,
1675 free_ps_combo, NULL);
1676 x->last_font = NULL;
1679 fprintf (x->file.file,
1681 "%%%%BeginPageSetup%s"
1682 "/pg save def 0.001 dup scale%s",
1683 x->page_number, x->file_page_number, x->eol,
1687 if (x->orientation == OTN_LANDSCAPE)
1688 fprintf (x->file.file,
1689 "%d 0 translate 90 rotate%s",
1692 if (x->bottom_margin != 0 || x->left_margin != 0)
1693 fprintf (x->file.file,
1694 "%d %d translate%s",
1695 x->left_margin, x->bottom_margin, x->eol);
1697 fprintf (x->file.file,
1698 "/LW %d def/TW %d def %d setlinewidth%s"
1699 "%%%%EndPageSetup%s",
1700 x->line_width, x->line_width_thick, x->line_width, x->eol,
1703 if (!ferror (x->file.file))
1705 this->page_open = 1;
1706 if (x->output_options & OPO_HEADERS)
1707 draw_headers (this);
1710 return !ferror (x->file.file);
1714 ps_close_page (struct outp_driver *this)
1716 struct ps_driver_ext *x = this->ext;
1718 assert (this->driver_open && this->page_open);
1723 fprintf (x->file.file,
1728 this->page_open = 0;
1729 return !ferror (x->file.file);
1734 /* qsort() comparison function for int tuples. */
1736 int_2_compare (const void *a, const void *b)
1738 return *((const int *) a) - *((const int *) b);
1741 /* Hash table comparison function for cached lines. */
1743 compare_line (const void *a, const void *b, void *foo unused)
1745 return ((struct line_form *) a)->ind - ((struct line_form *) b)->ind;
1748 /* Hash table hash function for cached lines. */
1750 hash_line (const void *pa, void *foo unused)
1752 const struct line_form *a = pa;
1757 /* Hash table free function for cached lines. */
1759 free_line (void *pa, void *foo unused)
1764 /* Writes PostScript code to draw a line from (x1,y1) to (x2,y2) to
1766 #define dump_line(x1, y1, x2, y2) \
1767 fprintf (ext->file.file, "%d %d %d %d L%s", \
1768 x1, YT (y1), x2, YT (y2), ext->eol)
1770 /* Write PostScript code to draw a thick line from (x1,y1) to (x2,y2)
1771 to the output file. */
1772 #define dump_thick_line(x1, y1, x2, y2) \
1773 fprintf (ext->file.file, "%d %d %d %d TL%s", \
1774 x1, YT (y1), x2, YT (y2), ext->eol)
1776 /* Writes a line of type TYPE to THIS driver's output file. The line
1777 (or its center, in the case of double lines) has its independent
1778 axis coordinate at IND; it extends from DEP1 to DEP2 on the
1781 dump_fancy_line (struct outp_driver *this, int type, int ind, int dep1, int dep2)
1783 struct ps_driver_ext *ext = this->ext;
1784 int ofs = ext->line_space / 2 + ext->line_width / 2;
1789 dump_line (dep1, ind, dep2, ind);
1792 if (ext->output_options & OPO_DOUBLE_LINE)
1794 dump_line (dep1, ind - ofs, dep2, ind - ofs);
1795 dump_line (dep1, ind + ofs, dep2, ind + ofs);
1798 dump_thick_line (dep1, ind, dep2, ind);
1803 dump_line (ind, dep1, ind, dep2);
1806 if (ext->output_options & OPO_DOUBLE_LINE)
1808 dump_line (ind - ofs, dep1, ind - ofs, dep2);
1809 dump_line (ind + ofs, dep1, ind + ofs, dep2);
1812 dump_thick_line (ind, dep1, ind, dep2);
1823 /* Writes all the cached lines to the output file, then clears the
1826 dump_lines (struct outp_driver *this)
1828 struct ps_driver_ext *x = this->ext;
1830 struct hsh_iterator iter;
1831 struct line_form *line;
1834 hsh_iterator_init (iter);
1835 for (type = 0; type < n_line_types; type++)
1837 while (NULL != (line = hsh_foreach (x->lines[type], &iter)))
1840 int lo = INT_MIN, hi;
1842 qsort (line->dep, line->ndep, sizeof *line->dep, int_2_compare);
1843 lo = line->dep[0][0];
1844 hi = line->dep[0][1];
1845 for (i = 1; i < line->ndep; i++)
1846 if (line->dep[i][0] <= hi + 1)
1848 int min_hi = line->dep[i][1];
1854 dump_fancy_line (this, type, line->ind, lo, hi);
1855 lo = line->dep[i][0];
1856 hi = line->dep[i][1];
1858 dump_fancy_line (this, type, line->ind, lo, hi);
1861 hsh_destroy (x->lines[type]);
1862 x->lines[type] = NULL;
1866 /* (Same args as dump_fancy_line()). Either dumps the line directly
1867 to the output file, or adds it to the cache, depending on the
1868 user-selected line optimization mode. */
1870 line (struct outp_driver *this, int type, int ind, int dep1, int dep2)
1872 struct ps_driver_ext *ext = this->ext;
1873 struct line_form **f;
1875 assert (dep2 >= dep1);
1876 if (ext->line_opt == 0)
1878 dump_fancy_line (this, type, ind, dep1, dep2);
1882 if (ext->lines[type] == NULL)
1883 ext->lines[type] = hsh_create (31, compare_line, hash_line,
1885 f = (struct line_form **) hsh_probe (ext->lines[type],
1886 (struct line_form *) & ind);
1889 *f = xmalloc (sizeof **f + sizeof (int[15][2]));
1893 (*f)->dep[0][0] = dep1;
1894 (*f)->dep[0][1] = dep2;
1897 if ((*f)->ndep >= (*f)->mdep)
1900 *f = xrealloc (*f, (sizeof **f + sizeof (int[2]) * ((*f)->mdep - 1)));
1902 (*f)->dep[(*f)->ndep][0] = dep1;
1903 (*f)->dep[(*f)->ndep][1] = dep2;
1908 ps_line_horz (struct outp_driver *this, const struct rect *r,
1909 const struct color *c unused, int style)
1911 /* Must match output.h:OUTP_L_*. */
1912 static const int types[OUTP_L_COUNT] =
1913 {-1, horz, dbl_horz, spl_horz};
1915 int y = (r->y1 + r->y2) / 2;
1917 assert (this->driver_open && this->page_open);
1918 assert (style >= 0 && style < OUTP_L_COUNT);
1919 style = types[style];
1921 line (this, style, y, r->x1, r->x2);
1925 ps_line_vert (struct outp_driver *this, const struct rect *r,
1926 const struct color *c unused, int style)
1928 /* Must match output.h:OUTP_L_*. */
1929 static const int types[OUTP_L_COUNT] =
1930 {-1, vert, dbl_vert, spl_vert};
1932 int x = (r->x1 + r->x2) / 2;
1934 assert (this->driver_open && this->page_open);
1935 assert (style >= 0 && style < OUTP_L_COUNT);
1936 style = types[style];
1938 line (this, style, x, r->y1, r->y2);
1941 #define L (style->l != OUTP_L_NONE)
1942 #define R (style->r != OUTP_L_NONE)
1943 #define T (style->t != OUTP_L_NONE)
1944 #define B (style->b != OUTP_L_NONE)
1947 ps_line_intersection (struct outp_driver *this, const struct rect *r,
1948 const struct color *c unused,
1949 const struct outp_styles *style)
1951 struct ps_driver_ext *ext = this->ext;
1953 int x = (r->x1 + r->x2) / 2;
1954 int y = (r->y1 + r->y2) / 2;
1955 int ofs = (ext->line_space + ext->line_width) / 2;
1956 int x1 = x - ofs, x2 = x + ofs;
1957 int y1 = y - ofs, y2 = y + ofs;
1959 assert (this->driver_open && this->page_open);
1960 assert (!((style->l != style->r && style->l != OUTP_L_NONE
1961 && style->r != OUTP_L_NONE)
1962 || (style->t != style->b && style->t != OUTP_L_NONE
1963 && style->b != OUTP_L_NONE)));
1965 switch ((style->l | style->r) | ((style->t | style->b) << 8))
1967 case (OUTP_L_SINGLE) | (OUTP_L_SINGLE << 8):
1968 case (OUTP_L_SINGLE) | (OUTP_L_NONE << 8):
1969 case (OUTP_L_NONE) | (OUTP_L_SINGLE << 8):
1971 line (this, horz, y, r->x1, x);
1973 line (this, horz, y, x, r->x2);
1975 line (this, vert, x, r->y1, y);
1977 line (this, vert, x, y, r->y2);
1979 case (OUTP_L_SINGLE) | (OUTP_L_DOUBLE << 8):
1980 case (OUTP_L_NONE) | (OUTP_L_DOUBLE << 8):
1982 line (this, horz, y, r->x1, x1);
1984 line (this, horz, y, x2, r->x2);
1986 line (this, dbl_vert, x, r->y1, y);
1988 line (this, dbl_vert, x, y, r->y2);
1989 if ((L && R) && !(T && B))
1990 line (this, horz, y, x1, x2);
1992 case (OUTP_L_DOUBLE) | (OUTP_L_SINGLE << 8):
1993 case (OUTP_L_DOUBLE) | (OUTP_L_NONE << 8):
1995 line (this, dbl_horz, y, r->x1, x);
1997 line (this, dbl_horz, y, x, r->x2);
1999 line (this, vert, x, r->y1, y);
2001 line (this, vert, x, y, r->y2);
2002 if ((T && B) && !(L && R))
2003 line (this, vert, x, y1, y2);
2005 case (OUTP_L_DOUBLE) | (OUTP_L_DOUBLE << 8):
2007 line (this, dbl_horz, y, r->x1, x);
2009 line (this, dbl_horz, y, x, r->x2);
2011 line (this, dbl_vert, x, r->y1, y);
2013 line (this, dbl_vert, x, y, r->y2);
2015 line (this, vert, x1, y1, y2);
2017 line (this, vert, x2, y1, y2);
2019 line (this, horz, y1, x1, x2);
2021 line (this, horz, y2, x1, x2);
2029 ps_line_width (struct outp_driver *this, int *width, int *height)
2031 struct ps_driver_ext *x = this->ext;
2033 assert (this->driver_open && this->page_open);
2034 width[0] = height[0] = 0;
2035 width[1] = height[1] = 2 * x->line_gutter + x->line_width;
2036 width[2] = height[2] = (2 * x->line_gutter + 2 * x->line_width
2038 width[3] = height[3] = 2 * x->line_gutter + x->line_width;
2042 ps_box (struct outp_driver *this unused, const struct rect *r unused,
2043 const struct color *bord unused, const struct color *fill unused)
2045 assert (this->driver_open && this->page_open);
2049 ps_polyline_begin (struct outp_driver *this unused,
2050 const struct color *c unused)
2052 assert (this->driver_open && this->page_open);
2055 ps_polyline_point (struct outp_driver *this unused, int x unused, int y unused)
2057 assert (this->driver_open && this->page_open);
2060 ps_polyline_end (struct outp_driver *this unused)
2062 assert (this->driver_open && this->page_open);
2065 /* Returns the width of string S for THIS driver. */
2067 text_width (struct outp_driver *this, char *s)
2069 struct outp_text text;
2071 text.options = OUTP_T_JUST_LEFT;
2072 ls_init (&text.s, s, strlen (s));
2073 this->class->text_metrics (this, &text);
2077 /* Write string S at location (X,Y) with width W for THIS driver. */
2079 out_text_plain (struct outp_driver *this, char *s, int x, int y, int w)
2081 struct outp_text text;
2083 text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT;
2084 ls_init (&text.s, s, strlen (s));
2086 text.v = this->font_height;
2089 this->class->text_draw (this, &text);
2092 /* Draw top of page headers for THIS driver. */
2094 draw_headers (struct outp_driver *this)
2096 struct ps_driver_ext *ext = this->ext;
2098 struct font_entry *old_current = ext->current;
2099 char *old_family = xstrdup (ext->family); /* FIXME */
2100 int old_size = ext->size;
2102 int fh = this->font_height;
2105 fprintf (ext->file.file, "%d %d %d %d GB%s",
2106 0, YT (y), this->width, YT (y + 2 * fh + ext->line_gutter),
2108 this->class->text_set_font_family (this, "T");
2110 y += ext->line_width + ext->line_gutter;
2116 sprintf (buf, _("%s - Page %d"), curdate, ext->page_number);
2117 rh_width = text_width (this, buf);
2119 out_text_plain (this, buf, this->width - this->prop_em_width - rh_width,
2122 if (outp_title && outp_subtitle)
2123 out_text_plain (this, outp_title, this->prop_em_width, y,
2124 this->width - 3 * this->prop_em_width - rh_width);
2132 char *string = outp_subtitle ? outp_subtitle : outp_title;
2134 sprintf (buf, "%s - %s", version, host_system);
2135 rh_width = text_width (this, buf);
2137 out_text_plain (this, buf, this->width - this->prop_em_width - rh_width,
2141 out_text_plain (this, string, this->prop_em_width, y,
2142 this->width - 3 * this->prop_em_width - rh_width);
2147 ext->current = old_current;
2149 ext->family = old_family;
2150 ext->size = old_size;
2157 ps_text_set_font_by_name (struct outp_driver *this, const char *dit)
2159 struct ps_driver_ext *x = this->ext;
2160 struct font_entry *fe;
2162 assert (this->driver_open && this->page_open);
2164 /* Short-circuit common fonts. */
2165 if (!strcmp (dit, "PROP"))
2167 x->current = x->prop;
2168 x->size = x->font_size;
2171 else if (!strcmp (dit, "FIXED"))
2173 x->current = x->fixed;
2174 x->size = x->font_size;
2178 /* Find font_desc corresponding to Groff name dit. */
2179 fe = hsh_find (x->loaded, &dit);
2181 fe = load_font (this, dit);
2186 ps_text_set_font_by_position (struct outp_driver *this, int pos)
2188 struct ps_driver_ext *x = this->ext;
2191 assert (this->driver_open && this->page_open);
2193 /* Determine font name by suffixing position string to font family
2198 dit = local_alloc (strlen (x->family) + 3);
2199 cp = stpcpy (dit, x->family);
2221 /* Find font_desc corresponding to Groff name dit. */
2223 struct font_entry *fe = hsh_find (x->loaded, &dit);
2225 fe = load_font (this, dit);
2233 ps_text_set_font_family (struct outp_driver *this, const char *s)
2235 struct ps_driver_ext *x = this->ext;
2237 assert (this->driver_open && this->page_open);
2240 x->family = xstrdup (s);
2244 ps_text_get_font_name (struct outp_driver *this)
2246 struct ps_driver_ext *x = this->ext;
2248 assert (this->driver_open && this->page_open);
2249 return x->current->font->name;
2253 ps_text_get_font_family (struct outp_driver *this)
2255 struct ps_driver_ext *x = this->ext;
2257 assert (this->driver_open && this->page_open);
2262 ps_text_set_size (struct outp_driver *this, int size)
2264 struct ps_driver_ext *x = this->ext;
2266 assert (this->driver_open && this->page_open);
2267 x->size = PSUS / 72000 * size;
2272 ps_text_get_size (struct outp_driver *this, int *em_width)
2274 struct ps_driver_ext *x = this->ext;
2276 assert (this->driver_open && this->page_open);
2278 *em_width = (x->current->font->space_width * x->size) / 1000;
2279 return x->size / (PSUS / 72000);
2282 /* An output character. */
2285 struct font_entry *font; /* Font of character. */
2286 int size; /* Size of character. */
2287 int x, y; /* Location of character. */
2288 unsigned char ch; /* Character. */
2289 char separate; /* Must be separate from previous char. */
2292 /* Hash table comparison function for ps_combo structs. */
2294 compare_ps_combo (const void *pa, const void *pb, void *foo unused)
2296 const struct ps_font_combo *a = pa;
2297 const struct ps_font_combo *b = pb;
2299 return !((a->font == b->font) && (a->size == b->size));
2302 /* Hash table hash function for ps_combo structs. */
2304 hash_ps_combo (const void *pa, void *foo unused)
2306 const struct ps_font_combo *a = pa;
2308 return hashpjw (a->font->font->internal_name) ^ a->size;
2311 /* Hash table free function for ps_combo structs. */
2313 free_ps_combo (void *a, void *foo unused)
2318 /* Causes PostScript code to be output that switches to the font
2319 CP->FONT and font size CP->SIZE. The first time a particular
2320 font/size combination is used on a particular page, this involves
2321 outputting PostScript code to load the font. */
2323 switch_font (struct outp_driver *this, const struct output_char *cp)
2325 struct ps_driver_ext *ext = this->ext;
2326 struct ps_font_combo srch, **fc;
2328 srch.font = cp->font;
2329 srch.size = cp->size;
2331 fc = (struct ps_font_combo **) hsh_probe (ext->combos, &srch);
2334 fprintf (ext->file.file, "F%x%s", (*fc)->index, ext->eol);
2339 struct ps_encoding *encoding;
2342 *fc = xmalloc (sizeof **fc);
2343 (*fc)->font = cp->font;
2344 (*fc)->size = cp->size;
2345 (*fc)->index = ext->next_combo++;
2347 filename = find_encoding_file (this, cp->font->font->encoding);
2350 encoding = get_encoding (this, filename);
2355 msg (IE, _("PostScript driver: Cannot find encoding `%s' for "
2356 "PostScript font `%s'."), cp->font->font->encoding,
2357 cp->font->font->internal_name);
2358 encoding = default_encoding (this);
2361 if (cp->font != ext->fixed && cp->font != ext->prop)
2363 bp = stpcpy (buf, "%%IncludeResource: font ");
2364 bp = quote_ps_string (bp, cp->font->font->internal_name);
2365 bp = stpcpy (bp, ext->eol);
2370 bp = spprintf (bp, "/F%x E%x %d", (*fc)->index, encoding->index,
2372 bp = quote_ps_name (bp, cp->font->font->internal_name);
2373 sprintf (bp, " SF%s", ext->eol);
2374 fputs (buf, ext->file.file);
2376 ext->last_font = *fc;
2379 /* (write_text) Writes the accumulated line buffer to the output
2381 #define output_line() \
2384 lp = stpcpy (lp, ext->eol); \
2386 fputs (line, ext->file.file); \
2391 /* (write_text) Adds the string representing number X to the line
2392 buffer, flushing the buffer to disk beforehand if necessary. */
2393 #define put_number(X) \
2396 int n = nsprintf (number, "%d", X); \
2397 if (n + lp > &line[75]) \
2399 lp = stpcpy (lp, number); \
2403 /* Outputs PostScript code to THIS driver's output file to display the
2404 characters represented by the output_char's between CP and END,
2405 using the associated outp_text T to determine formatting. WIDTH is
2406 the width of the output region; WIDTH_LEFT is the amount of the
2407 WIDTH that is not taken up by text (so it can be used to determine
2410 write_text (struct outp_driver *this,
2411 const struct output_char *cp, const struct output_char *end,
2412 struct outp_text *t, int width unused, int width_left)
2414 struct ps_driver_ext *ext = this->ext;
2419 char number[INT_DIGITS + 1];
2423 switch (t->options & OUTP_T_JUST_MASK)
2425 case OUTP_T_JUST_LEFT:
2428 case OUTP_T_JUST_RIGHT:
2431 case OUTP_T_JUST_CENTER:
2432 ofs = width_left / 2;
2440 int x = cp->x + ofs;
2441 int y = cp->y + (cp->font->font->ascent * cp->size / 1000);
2443 if (ext->last_font == NULL
2444 || cp->font != ext->last_font->font
2445 || cp->size != ext->last_font->size)
2446 switch_font (this, cp);
2452 static unsigned char literal_chars[ODA_COUNT][32] =
2454 {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff,
2455 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
2456 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2457 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2459 {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff,
2460 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2461 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2462 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2464 {0x7e, 0xd6, 0xff, 0xfb, 0xff, 0xfc, 0xff, 0xff,
2465 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2466 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2467 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2471 if (TEST_BIT (literal_chars[ext->data], cp->ch))
2477 lp = stpcpy (lp, "\\(");
2480 lp = stpcpy (lp, "\\)");
2483 lp = spprintf (lp, "\\%03o", cp->ch);
2488 while (cp < end && lp < &line[70] && cp->separate == 0);
2496 put_number (YT (y));
2507 if (lp >= &line[70])
2517 /* Displays the text in outp_text T, if DRAW is nonzero; or, merely
2518 determine the text metrics, if DRAW is zero. */
2520 text (struct outp_driver *this, struct outp_text *t, int draw)
2522 struct ps_driver_ext *ext = this->ext;
2525 struct output_char *buf; /* Output buffer. */
2526 struct output_char *buf_end; /* End of output buffer. */
2527 struct output_char *buf_loc; /* Current location in output buffer. */
2530 struct font_entry *old_current = ext->current;
2531 char *old_family = xstrdup (ext->family); /* FIXME */
2532 int old_size = ext->size;
2537 /* Current location. */
2540 /* Keeping track of what's left over. */
2541 int width; /* Width available for characters. */
2542 int width_left, height_left; /* Width, height left over. */
2543 int max_height; /* Tallest character on this line so far. */
2545 /* Previous character. */
2548 /* Information about location of previous space. */
2549 char *space_char; /* Character after space. */
2550 struct output_char *space_buf_loc; /* Buffer location after space. */
2551 int space_width_left; /* Width of characters before space. */
2553 /* Name of the current character. */
2554 const char *char_name;
2555 char local_char_name[2] = {0, 0};
2557 local_char_name[0] = local_char_name[1] = 0;
2559 buf = local_alloc (sizeof *buf * 128);
2560 buf_end = &buf[128];
2563 assert (!ls_null_p (&t->s));
2564 cp = ls_value (&t->s);
2565 end = ls_end (&t->s);
2571 width = width_left = (t->options & OUTP_T_HORZ) ? t->h : INT_MAX;
2572 height_left = (t->options & OUTP_T_VERT) ? t->v : INT_MAX;
2577 if (!width || !height_left)
2582 struct char_metrics *metric;
2588 /* Set char_name to the name of the character or ligature at
2590 if (ext->current->font->ligatures && *cp == 'f')
2598 lig = LIG_fi, char_name = "fi";
2601 lig = LIG_fl, char_name = "fl";
2608 lig = LIG_ffi, char_name = "ffi";
2611 lig = LIG_ffl, char_name = "ffl";
2614 lig = LIG_ff, char_name = "ff";
2618 if ((lig & ext->current->font->ligatures) == 0)
2620 local_char_name[0] = *cp++; /* 'f' */
2621 char_name = local_char_name;
2624 cp += strlen (char_name);
2626 else if (*cp == '\n')
2630 write_text (this, buf, buf_loc, t, width, width_left);
2637 height_left -= max_height;
2643 /* FIXME: when we're page buffering it will be necessary to
2644 set separate to 1. */
2649 local_char_name[0] = *cp++;
2650 char_name = local_char_name;
2653 /* Figure out what size this character is, and what kern
2654 adjustment we need. */
2655 cur_char = font_char_name_to_index (char_name);
2656 metric = font_get_char_metrics (ext->current->font, cur_char);
2659 static struct char_metrics m;
2661 m.width = ext->current->font->space_width;
2662 m.code = *char_name;
2664 kern_amt = font_get_kern_adjust (ext->current->font, prev_char,
2668 kern_amt = (kern_amt * ext->size / 1000);
2671 char_width = metric->width * ext->size / 1000;
2673 /* Record the current status if this is a space character. */
2674 if (cur_char == space_index && buf_loc > buf)
2677 space_buf_loc = buf_loc;
2678 space_width_left = width_left;
2681 /* Drop down to a new line if there's no room left on this
2683 if (char_width + kern_amt > width_left)
2685 /* Regress to previous space, if any. */
2689 width_left = space_width_left;
2690 buf_loc = space_buf_loc;
2695 write_text (this, buf, buf_loc, t, width, width_left);
2702 height_left -= max_height;
2710 /* FIXME: when we're page buffering it will be
2711 necessary to set separate to 1. */
2716 if (ext->size > max_height)
2717 max_height = ext->size;
2718 if (max_height > height_left)
2721 /* Actually draw the character. */
2724 if (buf_loc >= buf_end)
2726 int buf_len = buf_end - buf;
2730 struct output_char *new_buf;
2732 new_buf = xmalloc (sizeof *new_buf * 256);
2733 memcpy (new_buf, buf, sizeof *new_buf * 128);
2734 buf_loc = new_buf + 128;
2735 buf_end = new_buf + 256;
2741 buf = xrealloc (buf, sizeof *buf * buf_len * 2);
2742 buf_loc = buf + buf_len;
2743 buf_end = buf + buf_len * 2;
2749 memset (buf_loc, 0, sizeof *buf_loc);
2751 buf_loc->font = ext->current;
2752 buf_loc->size = ext->size;
2755 buf_loc->ch = metric->code;
2756 buf_loc->separate = separate;
2761 /* Prepare for next iteration. */
2762 width_left -= char_width + kern_amt;
2763 prev_char = cur_char;
2765 height_left -= max_height;
2766 if (buf_loc > buf && draw)
2767 write_text (this, buf, buf_loc, t, width, width_left);
2770 if (!(t->options & OUTP_T_HORZ))
2771 t->h = INT_MAX - width_left;
2772 if (!(t->options & OUTP_T_VERT))
2773 t->v = INT_MAX - height_left;
2775 t->v -= height_left;
2776 if (buf_end - buf == 128)
2780 ext->current = old_current;
2782 ext->family = old_family;
2783 ext->size = old_size;
2787 ps_text_metrics (struct outp_driver *this, struct outp_text *t)
2789 assert (this->driver_open && this->page_open);
2794 ps_text_draw (struct outp_driver *this, struct outp_text *t)
2796 assert (this->driver_open && this->page_open);
2802 /* Translate a filename to a font. */
2803 struct filename2font
2805 char *filename; /* Normalized filename. */
2806 struct font_desc *font;
2809 /* Table of `filename2font's. */
2810 static struct hsh_table *ps_fonts;
2812 /* Hash table comparison function for filename2font structs. */
2814 compare_filename2font (const void *a, const void *b, void *param unused)
2816 return strcmp (((struct filename2font *) a)->filename,
2817 ((struct filename2font *) b)->filename);
2820 /* Hash table hash function for filename2font structs. */
2822 hash_filename2font (const void *a, void *param unused)
2824 /* I sure hope this works with long filenames. */
2825 return hashpjw (((struct filename2font *) a)->filename);
2828 /* Initializes the global font list by creating the hash table for
2829 translation of filenames to font_desc structs. */
2833 ps_fonts = hsh_create (31, compare_filename2font, hash_filename2font,
2837 /* Loads the font having Groff name DIT into THIS driver instance.
2838 Specifically, adds it into the THIS driver's `loaded' hash
2840 static struct font_entry *
2841 load_font (struct outp_driver *this, const char *dit)
2843 struct ps_driver_ext *x = this->ext;
2844 char *filename1, *filename2;
2846 struct font_entry *fe;
2848 filename1 = find_ps_file (this, dit);
2850 filename1 = xstrdup (dit);
2851 filename2 = fn_normalize (filename1);
2854 entry = hsh_probe (ps_fonts, &filename2);
2857 struct filename2font *f2f;
2858 struct font_desc *f = groff_read_font (filename2);
2865 f = default_font ();
2868 f2f = xmalloc (sizeof *f2f);
2869 f2f->filename = filename2;
2876 fe = xmalloc (sizeof *fe);
2877 fe->dit = xstrdup (dit);
2878 fe->font = ((struct filename2font *) * entry)->font;
2879 *hsh_probe (x->loaded, &dit) = fe;
2884 /* PostScript driver class. */
2885 struct outp_class postscript_class =
2907 ps_line_intersection,
2914 ps_text_set_font_by_name,
2915 ps_text_set_font_by_position,
2916 ps_text_set_font_family,
2917 ps_text_get_font_name,
2918 ps_text_get_font_family,
2925 /* EPSF driver class. FIXME: Probably doesn't work right. */
2926 struct outp_class epsf_class =
2948 ps_line_intersection,
2955 ps_text_set_font_by_name,
2956 ps_text_set_font_by_position,
2957 ps_text_set_font_family,
2958 ps_text_get_font_name,
2959 ps_text_get_font_family,
2966 #endif /* NO_POSTSCRIPT */