c5bde5e65a4f2aaa30d1f572c61195834aba499a
[pspp-builds.git] / src / postscript.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21
22 /*this #if encloses the remainder of the file. */
23 #if !NO_POSTSCRIPT
24
25 #include <ctype.h>
26 #include "error.h"
27 #include <errno.h>
28 #include <limits.h>
29 #include <stdlib.h>
30
31 #if HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34
35 #if TIME_WITH_SYS_TIME
36 #include <sys/time.h>
37 #include <time.h>
38 #else
39 #if HAVE_SYS_TIME_H
40 #include <sys/time.h>
41 #else
42 #include <time.h>
43 #endif
44 #endif
45
46 #include "alloc.h"
47 #include "bitvector.h"
48 #include "error.h"
49 #include "filename.h"
50 #include "font.h"
51 #include "getline.h"
52 #include "hash.h"
53 #include "main.h"
54 #include "misc.h"
55 #include "misc.h"
56 #include "output.h"
57 #include "som.h"
58 #include "version.h"
59
60 /* FIXMEs:
61
62    optimize-text-size not implemented.
63    
64    Line buffering is the only possibility; page buffering should also
65    be possible.
66
67    max-fonts-simult
68    
69    Should add a field to give a file that has a list of fonts
70    typically used.
71    
72    Should add an option that tells the driver it can emit %%Include:'s.
73    
74    Should have auto-encode=true stream-edit or whatever to allow
75    addition to list of encodings.
76    
77    Should align fonts of different sizes along their baselines (see
78    text()).  */
79
80 /* PostScript driver options: (defaults listed first)
81
82    output-file="pspp.ps"
83    color=yes|no
84    data=clean7bit|clean8bit|binary
85    line-ends=lf|crlf
86
87    paper-size=letter (see "papersize" file)
88    orientation=portrait|landscape
89    headers=on|off
90    
91    left-margin=0.5in
92    right-margin=0.5in
93    top-margin=0.5in
94    bottom-margin=0.5in
95
96    font-dir=devps
97    prologue-file=ps-prologue
98    device-file=DESC
99    encoding-file=ps-encodings
100    auto-encode=true|false
101
102    prop-font-family=T
103    fixed-font-family=C
104    font-size=10000
105
106    line-style=thick|double
107    line-gutter=0.5pt
108    line-spacing=0.5pt
109    line-width=0.5pt
110    line-width-thick=1pt
111
112    optimize-text-size=1|0|2
113    optimize-line-size=1|0
114    max-fonts-simult=0     Max # of fonts in printer memory at once (0=infinite)
115  */
116
117 /* The number of `psus' (PostScript driver UnitS) per inch.  Although
118    this is a #define, the value is expected never to change.  If it
119    does, review all uses.  */
120 #define PSUS 72000
121
122 /* Magic numbers for PostScript and EPSF drivers. */
123 enum
124   {
125     MAGIC_PS,
126     MAGIC_EPSF
127   };
128
129 /* Orientations. */
130 enum
131   {
132     OTN_PORTRAIT,               /* Portrait. */
133     OTN_LANDSCAPE               /* Landscape. */
134   };
135
136 /* Output options. */
137 enum
138   {
139     OPO_MIRROR_HORZ = 001,      /* 1=Mirror across a horizontal axis. */
140     OPO_MIRROR_VERT = 002,      /* 1=Mirror across a vertical axis. */
141     OPO_ROTATE_180 = 004,       /* 1=Rotate the page 180 degrees. */
142     OPO_COLOR = 010,            /* 1=Enable color. */
143     OPO_HEADERS = 020,          /* 1=Draw headers at top of page. */
144     OPO_AUTO_ENCODE = 040,      /* 1=Add encodings semi-intelligently. */
145     OPO_DOUBLE_LINE = 0100      /* 1=Double lines instead of thick lines. */
146   };
147
148 /* Data allowed in output. */
149 enum
150   {
151     ODA_CLEAN7BIT,              /* 0x09, 0x0a, 0x0d, 0x1b...0x7e */
152     ODA_CLEAN8BIT,              /* 0x09, 0x0a, 0x0d, 0x1b...0xff */
153     ODA_BINARY,                 /* 0x00...0xff */
154     ODA_COUNT
155   };
156
157 /* Types of lines for purpose of caching. */
158 enum
159   {
160     horz,                       /* Single horizontal. */
161     dbl_horz,                   /* Double horizontal. */
162     spl_horz,                   /* Special horizontal. */
163     vert,                       /* Single vertical. */
164     dbl_vert,                   /* Double vertical. */
165     spl_vert,                   /* Special vertical. */
166     n_line_types
167   };
168
169 /* Cached line. */
170 struct line_form
171   {
172     int ind;                    /* Independent var.  Don't reorder. */
173     int mdep;                   /* Maximum number of dependent var pairs. */
174     int ndep;                   /* Current number of dependent var pairs. */
175     int dep[1][2];              /* Dependent var pairs. */
176   };
177
178 /* Contents of ps_driver_ext.loaded. */
179 struct font_entry
180   {
181     char *dit;                  /* Font Groff name. */
182     struct font_desc *font;     /* Font descriptor. */
183   };
184
185 /* Combines a font with a font size for benefit of generated code. */
186 struct ps_font_combo
187   {
188     struct font_entry *font;    /* Font. */
189     int size;                   /* Font size. */
190     int index;                  /* PostScript index. */
191   };
192
193 /* A font encoding. */
194 struct ps_encoding
195   {
196     char *filename;             /* Normalized filename of this encoding. */
197     int index;                  /* Index value. */
198   };
199
200 /* PostScript output driver extension record. */
201 struct ps_driver_ext
202   {
203     /* User parameters. */
204     int orientation;            /* OTN_PORTRAIT or OTN_LANDSCAPE. */
205     int output_options;         /* OPO_*. */
206     int data;                   /* ODA_*. */
207
208     int left_margin;            /* Left margin in psus. */
209     int right_margin;           /* Right margin in psus. */
210     int top_margin;             /* Top margin in psus. */
211     int bottom_margin;          /* Bottom margin in psus. */
212
213     char eol[3];                /* End of line--CR, LF, or CRLF. */
214     
215     char *font_dir;             /* Font directory relative to font path. */
216     char *prologue_fn;          /* Prologue's filename relative to font dir. */
217     char *desc_fn;              /* DESC filename relative to font dir. */
218     char *encoding_fn;          /* Encoding's filename relative to font dir. */
219
220     char *prop_family;          /* Default proportional font family. */
221     char *fixed_family;         /* Default fixed-pitch font family. */
222     int font_size;              /* Default font size (psus). */
223
224     int line_gutter;            /* Space around lines. */
225     int line_space;             /* Space between lines. */
226     int line_width;             /* Width of lines. */
227     int line_width_thick;       /* Width of thick lines. */
228
229     int text_opt;               /* Text optimization level. */
230     int line_opt;               /* Line optimization level. */
231     int max_fonts;              /* Max # of simultaneous fonts (0=infinite). */
232
233     /* Internal state. */
234     struct file_ext file;       /* Output file. */
235     int page_number;            /* Current page number. */
236     int file_page_number;       /* Page number in this file. */
237     int w, l;                   /* Paper size. */
238     struct hsh_table *lines[n_line_types];      /* Line buffers. */
239     
240     struct font_entry *prop;    /* Default Roman proportional font. */
241     struct font_entry *fixed;   /* Default Roman fixed-pitch font. */
242     struct hsh_table *loaded;   /* Fonts in memory. */
243
244     struct hsh_table *combos;   /* Combinations of fonts with font sizes. */
245     struct ps_font_combo *last_font;    /* PostScript selected font. */
246     int next_combo;             /* Next font combo position index. */
247
248     struct hsh_table *encodings;/* Set of encodings. */
249     int next_encoding;          /* Next font encoding index. */
250
251     /* Currently selected font. */
252     struct font_entry *current; /* Current font. */
253     char *family;               /* Font family. */
254     int size;                   /* Size in psus. */
255   }
256 ps_driver_ext;
257
258 /* Transform logical y-ordinate Y into a page ordinate. */
259 #define YT(Y) (this->length - (Y))
260
261 /* Prototypes. */
262 static int postopen (struct file_ext *);
263 static int preclose (struct file_ext *);
264 static void draw_headers (struct outp_driver *this);
265
266 static int compare_font_entry (const void *, const void *, void *param);
267 static unsigned hash_font_entry (const void *, void *param);
268 static void free_font_entry (void *, void *foo);
269 static struct font_entry *load_font (struct outp_driver *, const char *dit);
270 static void init_fonts (void);
271 static void done_fonts (void);
272
273 static void dump_lines (struct outp_driver *this);
274
275 static void read_ps_encodings (struct outp_driver *this);
276 static int compare_ps_encoding (const void *pa, const void *pb, void *foo);
277 static unsigned hash_ps_encoding (const void *pa, void *foo);
278 static void free_ps_encoding (void *a, void *foo);
279 static void add_encoding (struct outp_driver *this, char *filename);
280 static struct ps_encoding *default_encoding (struct outp_driver *this);
281
282 static int compare_ps_combo (const void *pa, const void *pb, void *foo);
283 static unsigned hash_ps_combo (const void *pa, void *foo);
284 static void free_ps_combo (void *a, void *foo);
285
286 static char *quote_ps_name (char *dest, const char *string);
287 static char *quote_ps_string (char *dest, const char *string);
288 \f
289 /* Driver initialization. */
290
291 static int
292 ps_open_global (struct outp_class *this UNUSED)
293 {
294   init_fonts ();
295   groff_init ();
296   return 1;
297 }
298
299 static int
300 ps_close_global (struct outp_class *this UNUSED)
301 {
302   groff_done ();
303   done_fonts ();
304   return 1;
305 }
306
307 static int *
308 ps_font_sizes (struct outp_class *this UNUSED, int *n_valid_sizes)
309 {
310   /* Allow fonts up to 1" in height. */
311   static int valid_sizes[] =
312   {1, PSUS, 0, 0};
313
314   assert (n_valid_sizes != NULL);
315   *n_valid_sizes = 1;
316   return valid_sizes;
317 }
318
319 static int
320 ps_preopen_driver (struct outp_driver *this)
321 {
322   struct ps_driver_ext *x;
323   
324   int i;
325
326   assert (this->driver_open == 0);
327   msg (VM (1), _("PostScript driver initializing as `%s'..."), this->name);
328         
329   this->ext = x = xmalloc (sizeof (struct ps_driver_ext));
330   this->res = PSUS;
331   this->horiz = this->vert = 1;
332   this->width = this->length = 0;
333
334   x->orientation = OTN_PORTRAIT;
335   x->output_options = OPO_COLOR | OPO_HEADERS | OPO_AUTO_ENCODE;
336   x->data = ODA_CLEAN7BIT;
337         
338   x->left_margin = x->right_margin =
339     x->top_margin = x->bottom_margin = PSUS / 2;
340         
341   strcpy (x->eol, "\n");
342
343   x->font_dir = NULL;
344   x->prologue_fn = NULL;
345   x->desc_fn = NULL;
346   x->encoding_fn = NULL;
347
348   x->prop_family = NULL;
349   x->fixed_family = NULL;
350   x->font_size = PSUS * 10 / 72;
351
352   x->line_gutter = PSUS / 144;
353   x->line_space = PSUS / 144;
354   x->line_width = PSUS / 144;
355   x->line_width_thick = PSUS / 48;
356
357   x->text_opt = -1;
358   x->line_opt = -1;
359   x->max_fonts = 0;
360
361   x->file.filename = NULL;
362   x->file.mode = "wb";
363   x->file.file = NULL;
364   x->file.sequence_no = &x->page_number;
365   x->file.param = this;
366   x->file.postopen = postopen;
367   x->file.preclose = preclose;
368   x->page_number = 0;
369   x->w = x->l = 0;
370
371   x->file_page_number = 0;
372   for (i = 0; i < n_line_types; i++)
373     x->lines[i] = NULL;
374   x->last_font = NULL;
375
376   x->prop = NULL;
377   x->fixed = NULL;
378   x->loaded = NULL;
379
380   x->next_combo = 0;
381   x->combos = NULL;
382
383   x->encodings = hsh_create (31, compare_ps_encoding, hash_ps_encoding,
384                              free_ps_encoding, NULL);
385   x->next_encoding = 0;
386
387   x->current = NULL;
388   x->family = NULL;
389   x->size = 0;
390
391   return 1;
392 }
393
394 static int
395 ps_postopen_driver (struct outp_driver *this)
396 {
397   struct ps_driver_ext *x = this->ext;
398   
399   assert (this->driver_open == 0);
400
401   if (this->width == 0)
402     {
403       this->width = PSUS * 17 / 2;      /* Defaults to 8.5"x11". */
404       this->length = PSUS * 11;
405     }
406
407   if (x->text_opt == -1)
408     x->text_opt = (this->device & OUTP_DEV_SCREEN) ? 0 : 1;
409   if (x->line_opt == -1)
410     x->line_opt = (this->device & OUTP_DEV_SCREEN) ? 0 : 1;
411
412   x->w = this->width;
413   x->l = this->length;
414   if (x->orientation == OTN_LANDSCAPE)
415     {
416       int temp = this->width;
417       this->width = this->length;
418       this->length = temp;
419     }
420   this->width -= x->left_margin + x->right_margin;
421   this->length -= x->top_margin + x->bottom_margin;
422   if (x->output_options & OPO_HEADERS)
423     {
424       this->length -= 3 * x->font_size;
425       x->top_margin += 3 * x->font_size;
426     }
427   if (NULL == x->file.filename)
428     x->file.filename = xstrdup ("pspp.ps");
429
430   if (x->font_dir == NULL)
431     x->font_dir = xstrdup ("devps");
432   if (x->prologue_fn == NULL)
433     x->prologue_fn = xstrdup ("ps-prologue");
434   if (x->desc_fn == NULL)
435     x->desc_fn = xstrdup ("DESC");
436   if (x->encoding_fn == NULL)
437     x->encoding_fn = xstrdup ("ps-encodings");
438
439   if (x->prop_family == NULL)
440     x->prop_family = xstrdup ("H");
441   if (x->fixed_family == NULL)
442     x->fixed_family = xstrdup ("C");
443
444   read_ps_encodings (this);
445
446   x->family = NULL;
447   x->size = PSUS / 6;
448
449   if (this->length / x->font_size < 15)
450     {
451       msg (SE, _("PostScript driver: The defined page is not long "
452                  "enough to hold margins and headers, plus least 15 "
453                  "lines of the default fonts.  In fact, there's only "
454                  "room for %d lines of each font at the default size "
455                  "of %d.%03d points."),
456            this->length / x->font_size,
457            x->font_size / 1000, x->font_size % 1000);
458       return 0;
459     }
460
461   this->driver_open = 1;
462   msg (VM (2), _("%s: Initialization complete."), this->name);
463
464   return 1;
465 }
466
467 static int
468 ps_close_driver (struct outp_driver *this)
469 {
470   struct ps_driver_ext *x = this->ext;
471   
472   int i;
473
474   assert (this->driver_open == 1);
475   msg (VM (2), _("%s: Beginning closing..."), this->name);
476   
477   fn_close_ext (&x->file);
478   free (x->file.filename);
479   free (x->font_dir);
480   free (x->prologue_fn);
481   free (x->desc_fn);
482   free (x->encoding_fn);
483   free (x->prop_family);
484   free (x->fixed_family);
485   free (x->family);
486   for (i = 0; i < n_line_types; i++)
487     hsh_destroy (x->lines[i]);
488   hsh_destroy (x->encodings);
489   hsh_destroy (x->combos);
490   hsh_destroy (x->loaded);
491   free (x);
492   
493   this->driver_open = 0;
494   msg (VM (3), _("%s: Finished closing."), this->name);
495
496   return 1;
497 }
498
499 /* font_entry comparison function for hash tables. */
500 static int
501 compare_font_entry (const void *a, const void *b, void *foobar UNUSED)
502 {
503   return strcmp (((struct font_entry *) a)->dit, ((struct font_entry *) b)->dit);
504 }
505
506 /* font_entry hash function for hash tables. */
507 static unsigned
508 hash_font_entry (const void *fe_, void *foobar UNUSED)
509 {
510   const struct font_entry *fe = fe_;
511   return hsh_hash_string (fe->dit);
512 }
513
514 /* font_entry destructor function for hash tables. */
515 static void
516 free_font_entry (void *pa, void *foo UNUSED)
517 {
518   struct font_entry *a = pa;
519   free (a->dit);
520   free (a);
521 }
522
523 /* Generic option types. */
524 enum
525 {
526   boolean_arg = -10,
527   pos_int_arg,
528   dimension_arg,
529   string_arg,
530   nonneg_int_arg
531 };
532
533 /* All the options that the PostScript driver supports. */
534 static struct outp_option option_tab[] =
535 {
536   /* *INDENT-OFF* */
537   {"output-file",               1,              0},
538   {"paper-size",                2,              0},
539   {"orientation",               3,              0},
540   {"color",                     boolean_arg,    0},
541   {"data",                      4,              0},
542   {"auto-encode",               boolean_arg,    5},
543   {"headers",                   boolean_arg,    1},
544   {"left-margin",               pos_int_arg,    0},
545   {"right-margin",              pos_int_arg,    1},
546   {"top-margin",                pos_int_arg,    2},
547   {"bottom-margin",             pos_int_arg,    3},
548   {"font-dir",                  string_arg,     0},
549   {"prologue-file",             string_arg,     1},
550   {"device-file",               string_arg,     2},
551   {"encoding-file",             string_arg,     3},
552   {"prop-font-family",          string_arg,     5},
553   {"fixed-font-family",         string_arg,     6},
554   {"font-size",                 pos_int_arg,    4},
555   {"optimize-text-size",        nonneg_int_arg, 0},
556   {"optimize-line-size",        nonneg_int_arg, 1},
557   {"max-fonts-simult",          nonneg_int_arg, 2},
558   {"line-ends",                 6,              0},
559   {"line-style",                7,              0},
560   {"line-width",                dimension_arg,  2},
561   {"line-gutter",               dimension_arg,  3},
562   {"line-width",                dimension_arg,  4},
563   {"line-width-thick",          dimension_arg,  5},
564   {"", 0, 0},
565   /* *INDENT-ON* */
566 };
567 static struct outp_option_info option_info;
568
569 static void
570 ps_option (struct outp_driver *this, const char *key, const struct string *val)
571 {
572   struct ps_driver_ext *x = this->ext;
573   int cat, subcat;
574   char *value = ds_c_str (val);
575
576   cat = outp_match_keyword (key, option_tab, &option_info, &subcat);
577
578   switch (cat)
579     {
580     case 0:
581       msg (SE, _("Unknown configuration parameter `%s' for PostScript device "
582            "driver."), key);
583       break;
584     case 1:
585       free (x->file.filename);
586       x->file.filename = xstrdup (value);
587       break;
588     case 2:
589       outp_get_paper_size (value, &this->width, &this->length);
590       break;
591     case 3:
592       if (!strcmp (value, "portrait"))
593         x->orientation = OTN_PORTRAIT;
594       else if (!strcmp (value, "landscape"))
595         x->orientation = OTN_LANDSCAPE;
596       else
597         msg (SE, _("Unknown orientation `%s'.  Valid orientations are "
598              "`portrait' and `landscape'."), value);
599       break;
600     case 4:
601       if (!strcmp (value, "clean7bit") || !strcmp (value, "Clean7Bit"))
602         x->data = ODA_CLEAN7BIT;
603       else if (!strcmp (value, "clean8bit")
604                || !strcmp (value, "Clean8Bit"))
605         x->data = ODA_CLEAN8BIT;
606       else if (!strcmp (value, "binary") || !strcmp (value, "Binary"))
607         x->data = ODA_BINARY;
608       else
609         msg (SE, _("Unknown value for `data'.  Valid values are `clean7bit', "
610              "`clean8bit', and `binary'."));
611       break;
612     case 6:
613       if (!strcmp (value, "lf"))
614         strcpy (x->eol, "\n");
615       else if (!strcmp (value, "crlf"))
616         strcpy (x->eol, "\r\n");
617       else
618         msg (SE, _("Unknown value for `line-ends'.  Valid values are `lf' and "
619                    "`crlf'."));
620       break;
621     case 7:
622       if (!strcmp (value, "thick"))
623         x->output_options &= ~OPO_DOUBLE_LINE;
624       else if (!strcmp (value, "double"))
625         x->output_options |= OPO_DOUBLE_LINE;
626       else
627         msg (SE, _("Unknown value for `line-style'.  Valid values are `thick' "
628                    "and `double'."));
629       break;
630     case boolean_arg:
631       {
632         int setting;
633         int mask;
634
635         if (!strcmp (value, "on") || !strcmp (value, "true")
636             || !strcmp (value, "yes") || atoi (value))
637           setting = 1;
638         else if (!strcmp (value, "off") || !strcmp (value, "false")
639                  || !strcmp (value, "no") || !strcmp (value, "0"))
640           setting = 0;
641         else
642           {
643             msg (SE, _("Boolean value expected for %s."), key);
644             return;
645           }
646         switch (subcat)
647           {
648           case 0:
649             mask = OPO_COLOR;
650             break;
651           case 1:
652             mask = OPO_HEADERS;
653             break;
654           case 2:
655             mask = OPO_MIRROR_HORZ;
656             break;
657           case 3:
658             mask = OPO_MIRROR_VERT;
659             break;
660           case 4:
661             mask = OPO_ROTATE_180;
662             break;
663           case 5:
664             mask = OPO_AUTO_ENCODE;
665             break;
666           default:
667             assert (0);
668             abort ();
669           }
670         if (setting)
671           x->output_options |= mask;
672         else
673           x->output_options &= ~mask;
674       }
675       break;
676     case pos_int_arg:
677       {
678         char *tail;
679         int arg;
680
681         errno = 0;
682         arg = strtol (value, &tail, 0);
683         if (arg < 1 || errno == ERANGE || *tail)
684           {
685             msg (SE, _("Positive integer required as value for `%s'."), key);
686             break;
687           }
688         if ((subcat == 4 || subcat == 5) && arg < 1000)
689           {
690             msg (SE, _("Default font size must be at least 1 point (value "
691                  "of 1000 for key `%s')."), key);
692             break;
693           }
694         switch (subcat)
695           {
696           case 0:
697             x->left_margin = arg;
698             break;
699           case 1:
700             x->right_margin = arg;
701             break;
702           case 2:
703             x->top_margin = arg;
704             break;
705           case 3:
706             x->bottom_margin = arg;
707             break;
708           case 4:
709             x->font_size = arg;
710             break;
711           default:
712             assert (0);
713           }
714       }
715       break;
716     case dimension_arg:
717       {
718         int dimension = outp_evaluate_dimension (value, NULL);
719
720         if (dimension <= 0)
721           {
722             msg (SE, _("Value for `%s' must be a dimension of positive "
723                  "length (i.e., `1in')."), key);
724             break;
725           }
726         switch (subcat)
727           {
728           case 2:
729             x->line_width = dimension;
730             break;
731           case 3:
732             x->line_gutter = dimension;
733             break;
734           case 4:
735             x->line_width = dimension;
736             break;
737           case 5:
738             x->line_width_thick = dimension;
739             break;
740           default:
741             assert (0);
742           }
743       }
744       break;
745     case string_arg:
746       {
747         char **dest;
748         switch (subcat)
749           {
750           case 0:
751             dest = &x->font_dir;
752             break;
753           case 1:
754             dest = &x->prologue_fn;
755             break;
756           case 2:
757             dest = &x->desc_fn;
758             break;
759           case 3:
760             dest = &x->encoding_fn;
761             break;
762           case 5:
763             dest = &x->prop_family;
764             break;
765           case 6:
766             dest = &x->fixed_family;
767             break;
768           default:
769             assert (0);
770             abort ();
771           }
772         if (*dest)
773           free (*dest);
774         *dest = xstrdup (value);
775       }
776       break;
777     case nonneg_int_arg:
778       {
779         char *tail;
780         int arg;
781
782         errno = 0;
783         arg = strtol (value, &tail, 0);
784         if (arg < 0 || errno == ERANGE || *tail)
785           {
786             msg (SE, _("Nonnegative integer required as value for `%s'."), key);
787             break;
788           }
789         switch (subcat)
790           {
791           case 0:
792             x->text_opt = arg;
793             break;
794           case 1:
795             x->line_opt = arg;
796             break;
797           case 2:
798             x->max_fonts = arg;
799             break;
800           default:
801             assert (0);
802           }
803       }
804       break;
805     default:
806       assert (0);
807     }
808 }
809
810 /* Looks for a PostScript font file or config file in all the
811    appropriate places.  Returns the filename on success, NULL on
812    failure. */
813 /* PORTME: Filename operations. */
814 static char *
815 find_ps_file (struct outp_driver *this, const char *name)
816 {
817   struct ps_driver_ext *x = this->ext;
818   char *cp;
819
820   /* x->font_dir + name: "devps/ps-encodings". */
821   char *basename;
822
823   /* Usually equal to groff_font_path. */
824   char *pathname;
825
826   /* Final filename. */
827   char *fn;
828
829   /* Make basename. */
830   basename = local_alloc (strlen (x->font_dir) + 1 + strlen (name) + 1);
831   cp = stpcpy (basename, x->font_dir);
832   *cp++ = DIR_SEPARATOR;
833   strcpy (cp, name);
834
835   /* Decide on search path. */
836   {
837     const char *pre_pathname;
838     
839     pre_pathname = getenv ("STAT_GROFF_FONT_PATH");
840     if (pre_pathname == NULL)
841       pre_pathname = getenv ("GROFF_FONT_PATH");
842     if (pre_pathname == NULL)
843       pre_pathname = groff_font_path;
844     pathname = fn_tilde_expand (pre_pathname);
845   }
846
847   /* Search all possible places for the file. */
848   fn = fn_search_path (basename, pathname, NULL);
849   if (fn == NULL)
850     fn = fn_search_path (basename, config_path, NULL);
851   if (fn == NULL)
852     fn = fn_search_path (name, pathname, NULL);
853   if (fn == NULL)
854     fn = fn_search_path (name, config_path, NULL);
855   free (pathname);
856   local_free (basename);
857
858   return fn;
859 }
860 \f
861 /* Encodings. */
862
863 /* Hash table comparison function for ps_encoding's. */
864 static int
865 compare_ps_encoding (const void *pa, const void *pb, void *foo UNUSED)
866 {
867   const struct ps_encoding *a = pa;
868   const struct ps_encoding *b = pb;
869
870   return strcmp (a->filename, b->filename);
871 }
872
873 /* Hash table hash function for ps_encoding's. */
874 static unsigned
875 hash_ps_encoding (const void *pa, void *foo UNUSED)
876 {
877   const struct ps_encoding *a = pa;
878
879   return hsh_hash_string (a->filename);
880 }
881
882 /* Hash table free function for ps_encoding's. */
883 static void
884 free_ps_encoding (void *pa, void *foo UNUSED)
885 {
886   struct ps_encoding *a = pa;
887
888   free (a->filename);
889   free (a);
890 }
891
892 /* Iterates through the list of encodings used for this driver
893    instance, reads each of them from disk, and writes them as
894    PostScript code to the output file. */
895 static void
896 output_encodings (struct outp_driver *this)
897 {
898   struct ps_driver_ext *x = this->ext;
899
900   struct hsh_iterator iter;
901   struct ps_encoding *pe;
902
903   struct string line, buf;
904
905   ds_init (&line, 128);
906   ds_init (&buf, 128);
907   for (pe = hsh_first (x->encodings, &iter); pe != NULL;
908        pe = hsh_next (x->encodings, &iter)) 
909     {
910       FILE *f;
911
912       msg (VM (1), _("%s: %s: Opening PostScript font encoding..."),
913            this->name, pe->filename);
914       
915       f = fopen (pe->filename, "r");
916       if (!f)
917         {
918           msg (IE, _("PostScript driver: Cannot open encoding file `%s': %s.  "
919                "Substituting ISOLatin1Encoding for missing encoding."),
920                pe->filename, strerror (errno));
921           fprintf (x->file.file, "/E%x ISOLatin1Encoding def%s",
922                    pe->index, x->eol);
923         }
924       else
925         {
926           struct file_locator where;
927           
928           const char *tab[256];
929
930           char *pschar;
931           char *code;
932           int code_val;
933           char *fubar;
934
935           const char *notdef = ".notdef";
936
937           int i;
938
939           for (i = 0; i < 256; i++)
940             tab[i] = notdef;
941
942           where.filename = pe->filename;
943           where.line_number = 0;
944           err_push_file_locator (&where);
945
946           while (ds_get_config_line (f, &buf, &where))
947             {
948               char *sp; 
949
950               if (buf.length == 0) 
951                 continue;
952
953               pschar = strtok_r (ds_c_str (&buf), " \t\r\n", &sp);
954               code = strtok_r (NULL, " \t\r\n", &sp);
955               if (*pschar == 0 || *code == 0)
956                 continue;
957               code_val = strtol (code, &fubar, 0);
958               if (*fubar)
959                 {
960                   msg (IS, _("PostScript driver: Invalid numeric format."));
961                   continue;
962                 }
963               if (code_val < 0 || code_val > 255)
964                 {
965                   msg (IS, _("PostScript driver: Codes must be between 0 "
966                              "and 255.  (%d is not allowed.)"), code_val);
967                   break;
968                 }
969               tab[code_val] = local_alloc (strlen (pschar) + 1);
970               strcpy ((char *) (tab[code_val]), pschar);
971             }
972           err_pop_file_locator (&where);
973
974           ds_clear (&line);
975           ds_printf (&line, "/E%x[", pe->index);
976           for (i = 0; i < 257; i++)
977             {
978               char temp[288];
979
980               if (i < 256)
981                 {
982                   quote_ps_name (temp, tab[i]);
983                   if (tab[i] != notdef)
984                     local_free (tab[i]);
985                 }
986               else
987                 strcpy (temp, "]def");
988               
989               if (ds_length (&line) + strlen (temp) > 70)
990                 {
991                   ds_puts (&line, x->eol);
992                   fputs (ds_c_str (&line), x->file.file);
993                   ds_clear (&line);
994                 }
995               ds_puts (&line, temp);
996             }
997           ds_puts (&line, x->eol);
998           fputs (ds_c_str (&line), x->file.file);
999
1000           if (fclose (f) == EOF)
1001             msg (MW, _("PostScript driver: Error closing encoding file `%s'."),
1002                  pe->filename);
1003
1004           msg (VM (2), _("%s: PostScript font encoding read successfully."),
1005                this->name);
1006         }
1007     }
1008   ds_destroy (&line);
1009   ds_destroy (&buf);
1010 }
1011
1012 /* Finds the ps_encoding in THIS that corresponds to the file with
1013    name NORM_FILENAME, which must have previously been normalized with
1014    normalize_filename(). */
1015 static struct ps_encoding *
1016 get_encoding (struct outp_driver *this, const char *norm_filename)
1017 {
1018   struct ps_driver_ext *x = this->ext;
1019   struct ps_encoding *pe;
1020
1021   pe = (struct ps_encoding *) hsh_find (x->encodings, (void *) &norm_filename);
1022   return pe;
1023 }
1024
1025 /* Searches the filesystem for an encoding file with name FILENAME;
1026    returns its malloc'd, normalized name if found, otherwise NULL. */
1027 static char *
1028 find_encoding_file (struct outp_driver *this, char *filename)
1029 {
1030   char *cp, *temp;
1031
1032   if (filename == NULL)
1033     return NULL;
1034   while (isspace ((unsigned char) *filename))
1035     filename++;
1036   for (cp = filename; *cp && !isspace ((unsigned char) *cp); cp++)
1037     ;
1038   if (cp == filename)
1039     return NULL;
1040   *cp = 0;
1041
1042   temp = find_ps_file (this, filename);
1043   if (temp == NULL)
1044     return NULL;
1045
1046   filename = fn_normalize (temp);
1047   assert (filename != NULL);
1048   free (temp);
1049
1050   return filename;
1051 }
1052
1053 /* Adds the encoding represented by the not-necessarily-normalized
1054    file FILENAME to the list of encodings, if it exists and is not
1055    already in the list. */
1056 static void
1057 add_encoding (struct outp_driver *this, char *filename)
1058 {
1059   struct ps_driver_ext *x = this->ext;
1060   struct ps_encoding **pe;
1061
1062   filename = find_encoding_file (this, filename);
1063   if (!filename)
1064     return;
1065
1066   pe = (struct ps_encoding **) hsh_probe (x->encodings, &filename);
1067   if (*pe)
1068     {
1069       free (filename);
1070       return;
1071     }
1072   *pe = xmalloc (sizeof **pe);
1073   (*pe)->filename = filename;
1074   (*pe)->index = x->next_encoding++;
1075 }
1076
1077 /* Finds the file on disk that contains the list of encodings to
1078    include in the output file, then adds those encodings to the list
1079    of encodings. */
1080 static void
1081 read_ps_encodings (struct outp_driver *this)
1082 {
1083   struct ps_driver_ext *x = this->ext;
1084
1085   /* Encodings file. */
1086   char *encoding_fn;            /* `ps-encodings' filename. */
1087   FILE *f;
1088
1089   struct string line;
1090   struct file_locator where;
1091
1092   /* It's okay if there's no list of encodings; not everyone cares. */
1093   encoding_fn = find_ps_file (this, x->encoding_fn);
1094   if (encoding_fn == NULL)
1095     return;
1096   free (encoding_fn);
1097
1098   msg (VM (1), _("%s: %s: Opening PostScript encoding list file."),
1099        this->name, encoding_fn);
1100   f = fopen (encoding_fn, "r");
1101   if (!f)
1102     {
1103       msg (IE, _("Opening %s: %s."), encoding_fn, strerror (errno));
1104       return;
1105     }
1106
1107   where.filename = encoding_fn;
1108   where.line_number = 0;
1109   err_push_file_locator (&where);
1110
1111   ds_init (&line, 128);
1112     
1113   for (;;)
1114     {
1115       if (!ds_get_config_line (f, &line, &where))
1116         {
1117           if (ferror (f))
1118             msg (ME, _("Reading %s: %s."), encoding_fn, strerror (errno));
1119           break;
1120         }
1121
1122       add_encoding (this, line.string);
1123     }
1124
1125   ds_destroy (&line);
1126   err_pop_file_locator (&where);
1127   
1128   if (-1 == fclose (f))
1129     msg (MW, _("Closing %s: %s."), encoding_fn, strerror (errno));
1130
1131   msg (VM (2), _("%s: PostScript encoding list file read successfully."), this->name);
1132 }
1133
1134 /* Creates a default encoding for driver D that can be substituted for
1135    an unavailable encoding. */
1136 struct ps_encoding *
1137 default_encoding (struct outp_driver *d)
1138 {
1139   struct ps_driver_ext *x = d->ext;
1140   static struct ps_encoding *enc;
1141
1142   if (!enc)
1143     {
1144       enc = xmalloc (sizeof *enc);
1145       enc->filename = xstrdup (_("<<default encoding>>"));
1146       enc->index = x->next_encoding++;
1147     }
1148   return enc;
1149 }
1150 \f
1151 /* Basic file operations. */
1152
1153 /* Variables for the prologue. */
1154 struct ps_variable
1155   {
1156     const char *key;
1157     const char *value;
1158   };
1159
1160 static struct ps_variable *ps_var_tab;
1161
1162 /* Searches ps_var_tab for a ps_variable with key KEY, and returns the
1163    associated value. */
1164 static const char *
1165 ps_get_var (const char *key)
1166 {
1167   struct ps_variable *v;
1168
1169   for (v = ps_var_tab; v->key; v++)
1170     if (!strcmp (key, v->key))
1171       return v->value;
1172   return NULL;
1173 }
1174
1175 /* Writes the PostScript prologue to file F. */
1176 static int
1177 postopen (struct file_ext *f)
1178 {
1179   static struct ps_variable dict[] =
1180   {
1181     {"bounding-box", 0},
1182     {"creator", 0},
1183     {"date", 0},
1184     {"data", 0},
1185     {"orientation", 0},
1186     {"user", 0},
1187     {"host", 0},
1188     {"prop-font", 0},
1189     {"fixed-font", 0},
1190     {"scale-factor", 0},
1191     {"paper-width", 0},
1192     {"paper-length", 0},
1193     {"left-margin", 0},
1194     {"top-margin", 0},
1195     {"line-width", 0},
1196     {"line-width-thick", 0},
1197     {"title", 0},
1198     {"source-file", 0},
1199     {0, 0},
1200   };
1201   char boundbox[INT_DIGITS * 4 + 4];
1202 #if HAVE_UNISTD_H
1203   char host[128];
1204 #endif
1205   char scaling[INT_DIGITS + 5];
1206   time_t curtime;
1207   struct tm *loctime;
1208   char *p, *cp;
1209   char paper_width[INT_DIGITS + 1];
1210   char paper_length[INT_DIGITS + 1];
1211   char left_margin[INT_DIGITS + 1];
1212   char top_margin[INT_DIGITS + 1];
1213   char line_width[INT_DIGITS + 1];
1214   char line_width_thick[INT_DIGITS + 1];
1215
1216   struct outp_driver *this = f->param;
1217   struct ps_driver_ext *x = this->ext;
1218
1219   char *prologue_fn = find_ps_file (this, x->prologue_fn);
1220   FILE *prologue_file;
1221
1222   char *buf = NULL;
1223   size_t buf_size = 0;
1224
1225   x->loaded = hsh_create (31, compare_font_entry, hash_font_entry,
1226                           free_font_entry, NULL);
1227   
1228   {
1229     char *font_name = local_alloc (2 + max (strlen (x->prop_family),
1230                                             strlen (x->fixed_family)));
1231     
1232     strcpy (stpcpy (font_name, x->prop_family), "R");
1233     x->prop = load_font (this, font_name);
1234
1235     strcpy (stpcpy (font_name, x->fixed_family), "R");
1236     x->fixed = load_font (this, font_name);
1237
1238     local_free(font_name);
1239   }
1240
1241   x->current = x->prop;
1242   x->family = xstrdup (x->prop_family);
1243   x->size = x->font_size;
1244   
1245   {
1246     int *h = this->horiz_line_width, *v = this->vert_line_width;
1247     
1248     this->cp_x = this->cp_y = 0;
1249     this->font_height = x->font_size;
1250     {
1251       struct char_metrics *metric;
1252
1253       metric = font_get_char_metrics (x->prop->font, '0');
1254       this->prop_em_width = ((metric
1255                               ? metric->width : x->prop->font->space_width)
1256                              * x->font_size / 1000);
1257
1258       metric = font_get_char_metrics (x->fixed->font, '0');
1259       this->fixed_width = ((metric
1260                             ? metric->width : x->fixed->font->space_width)
1261                            * x->font_size / 1000);
1262     }
1263         
1264     h[0] = v[0] = 0;
1265     h[1] = v[1] = 2 * x->line_gutter + x->line_width;
1266     if (x->output_options & OPO_DOUBLE_LINE)
1267       h[2] = v[2] = 2 * x->line_gutter + 2 * x->line_width + x->line_space;
1268     else
1269       h[2] = v[2] = 2 * x->line_gutter + x->line_width_thick;
1270     h[3] = v[3] = 2 * x->line_gutter + x->line_width;
1271     
1272     {
1273       int i;
1274       
1275       for (i = 0; i < (1 << OUTP_L_COUNT); i++)
1276         {
1277           int bit;
1278
1279           /* Maximum width of any line type so far. */
1280           int max = 0;
1281
1282           for (bit = 0; bit < OUTP_L_COUNT; bit++)
1283             if ((i & (1 << bit)) && h[bit] > max)
1284               max = h[bit];
1285           this->horiz_line_spacing[i] = this->vert_line_spacing[i] = max;
1286         }
1287     }
1288   }
1289
1290   if (x->output_options & OPO_AUTO_ENCODE)
1291     {
1292       /* It's okay if this is done more than once since add_encoding()
1293          is idempotent over identical encodings. */
1294       add_encoding (this, x->prop->font->encoding);
1295       add_encoding (this, x->fixed->font->encoding);
1296     }
1297
1298   x->file_page_number = 0;
1299
1300   errno = 0;
1301   if (prologue_fn == NULL)
1302     {
1303       msg (IE, _("Cannot find PostScript prologue.  The use of `-vv' "
1304                  "on the command line is suggested as a debugging aid."));
1305       return 0;
1306     }
1307
1308   msg (VM (1), _("%s: %s: Opening PostScript prologue..."),
1309        this->name, prologue_fn);
1310   prologue_file = fopen (prologue_fn, "rb");
1311   if (prologue_file == NULL)
1312     {
1313       fclose (prologue_file);
1314       free (prologue_fn);
1315       msg (IE, "%s: %s", prologue_fn, strerror (errno));
1316       goto error;
1317     }
1318
1319   sprintf (boundbox, "0 0 %d %d",
1320            x->w / (PSUS / 72) + (x->w % (PSUS / 72) > 0),
1321            x->l / (PSUS / 72) + (x->l % (PSUS / 72) > 0));
1322   dict[0].value = boundbox;
1323
1324   dict[1].value = (char *) version;
1325
1326   curtime = time (NULL);
1327   loctime = localtime (&curtime);
1328   dict[2].value = asctime (loctime);
1329   cp = strchr (dict[2].value, '\n');
1330   if (cp)
1331     *cp = 0;
1332
1333   switch (x->data)
1334     {
1335     case ODA_CLEAN7BIT:
1336       dict[3].value = "Clean7Bit";
1337       break;
1338     case ODA_CLEAN8BIT:
1339       dict[3].value = "Clean8Bit";
1340       break;
1341     case ODA_BINARY:
1342       dict[3].value = "Binary";
1343       break;
1344     default:
1345       assert (0);
1346     }
1347
1348   if (x->orientation == OTN_PORTRAIT)
1349     dict[4].value = "Portrait";
1350   else
1351     dict[4].value = "Landscape";
1352
1353   /* PORTME: Determine username, net address. */
1354 #if HAVE_UNISTD_H
1355   dict[5].value = getenv ("LOGNAME");
1356   if (!dict[5].value)
1357     dict[5].value = getlogin ();
1358   if (!dict[5].value)
1359     dict[5].value = _("nobody");
1360
1361   if (gethostname (host, 128) == -1)
1362     {
1363       if (errno == ENAMETOOLONG)
1364         host[127] = 0;
1365       else
1366         strcpy (host, _("nowhere"));
1367     }
1368   dict[6].value = host;
1369 #else /* !HAVE_UNISTD_H */
1370   dict[5].value = _("nobody");
1371   dict[6].value = _("nowhere");
1372 #endif /* !HAVE_UNISTD_H */
1373
1374   cp = stpcpy (p = local_alloc (288), "font ");
1375   quote_ps_string (cp, x->prop->font->internal_name);
1376   dict[7].value = p;
1377
1378   cp = stpcpy (p = local_alloc (288), "font ");
1379   quote_ps_string (cp, x->fixed->font->internal_name);
1380   dict[8].value = p;
1381
1382   sprintf (scaling, "%.3f", PSUS / 72.0);
1383   dict[9].value = scaling;
1384
1385   sprintf (paper_width, "%g", x->w / (PSUS / 72.0));
1386   dict[10].value = paper_width;
1387
1388   sprintf (paper_length, "%g", x->l / (PSUS / 72.0));
1389   dict[11].value = paper_length;
1390
1391   sprintf (left_margin, "%d", x->left_margin);
1392   dict[12].value = left_margin;
1393
1394   sprintf (top_margin, "%d", x->top_margin);
1395   dict[13].value = top_margin;
1396
1397   sprintf (line_width, "%d", x->line_width);
1398   dict[14].value = line_width;
1399
1400   sprintf (line_width, "%d", x->line_width_thick);
1401   dict[15].value = line_width_thick;
1402   
1403   getl_location (&dict[17].value, NULL);
1404   if (dict[17].value == NULL)
1405     dict[17].value = "<stdin>";
1406
1407   if (!outp_title)
1408     {
1409       dict[16].value = cp = local_alloc (strlen (dict[17].value) + 30);
1410       sprintf (cp, "PSPP (%s)", dict[17].value);
1411     }
1412   else
1413     {
1414       dict[16].value = local_alloc (strlen (outp_title) + 1);
1415       strcpy ((char *) (dict[16].value), outp_title);
1416     }
1417   
1418   ps_var_tab = dict;
1419   while (-1 != getline (&buf, &buf_size, prologue_file))
1420     {
1421       char *cp;
1422       char *buf2;
1423       int len;
1424
1425       cp = strstr (buf, "!eps");
1426       if (cp)
1427         {
1428           if (this->class->magic == MAGIC_PS)
1429             continue;
1430           else
1431             *cp = '\0';
1432         }
1433       else
1434         {
1435           cp = strstr (buf, "!ps");
1436           if (cp)
1437             {
1438               if (this->class->magic == MAGIC_EPSF)
1439                 continue;
1440               else
1441                 *cp = '\0';
1442             } else {
1443               if (strstr (buf, "!!!"))
1444                 continue;
1445             }
1446         }
1447
1448       if (!strncmp (buf, "!encodings", 10))
1449         output_encodings (this);
1450       else
1451         {
1452           char *beg;
1453           beg = buf2 = fn_interp_vars (buf, ps_get_var);
1454           len = strlen (buf2);
1455           while (isspace ((unsigned char) *beg))
1456             beg++, len--;
1457           if (beg[len - 1] == '\n')
1458             len--;
1459           if (beg[len - 1] == '\r')
1460             len--;
1461           fwrite (beg, len, 1, f->file);
1462           fputs (x->eol, f->file);
1463           free (buf2);
1464         }
1465     }
1466   if (ferror (f->file))
1467     msg (IE, _("Reading `%s': %s."), prologue_fn, strerror (errno));
1468   fclose (prologue_file);
1469
1470   free (prologue_fn);
1471   free (buf);
1472
1473   local_free (dict[7].value);
1474   local_free (dict[8].value);
1475   local_free (dict[16].value);
1476
1477   if (ferror (f->file))
1478     goto error;
1479
1480   msg (VM (2), _("%s: PostScript prologue read successfully."), this->name);
1481   return 1;
1482
1483 error:
1484   msg (VM (1), _("%s: Error reading PostScript prologue."), this->name);
1485   return 0;
1486 }
1487
1488 /* Writes the string STRING to buffer DEST (of at least 288
1489    characters) as a PostScript name object.  Returns a pointer
1490    to the null terminator of the resultant string. */
1491 static char *
1492 quote_ps_name (char *dest, const char *string)
1493 {
1494   const char *sp;
1495
1496   for (sp = string; *sp; sp++)
1497     switch (*(unsigned char *) sp)
1498       {
1499       case 'a':
1500       case 'f':
1501       case 'k':
1502       case 'p':
1503       case 'u':
1504       case 'b':
1505       case 'g':
1506       case 'l':
1507       case 'q':
1508       case 'v':
1509       case 'c':
1510       case 'h':
1511       case 'm':
1512       case 'r':
1513       case 'w':
1514       case 'd':
1515       case 'i':
1516       case 'n':
1517       case 's':
1518       case 'x':
1519       case 'e':
1520       case 'j':
1521       case 'o':
1522       case 't':
1523       case 'y':
1524       case 'z':
1525       case 'A':
1526       case 'F':
1527       case 'K':
1528       case 'P':
1529       case 'U':
1530       case 'B':
1531       case 'G':
1532       case 'L':
1533       case 'Q':
1534       case 'V':
1535       case 'C':
1536       case 'H':
1537       case 'M':
1538       case 'R':
1539       case 'W':
1540       case 'D':
1541       case 'I':
1542       case 'N':
1543       case 'S':
1544       case 'X':
1545       case 'E':
1546       case 'J':
1547       case 'O':
1548       case 'T':
1549       case 'Y':
1550       case 'Z':
1551       case '@':
1552       case '^':
1553       case '_':
1554       case '|':
1555       case '!':
1556       case '$':
1557       case '&':
1558       case ':':
1559       case ';':
1560       case '.':
1561       case ',':
1562       case '-':
1563       case '+':
1564         break;
1565       default:
1566         {
1567           char *dp = dest;
1568
1569           *dp++ = '<';
1570           for (sp = string; *sp && dp < &dest[256]; sp++)
1571             {
1572               sprintf (dp, "%02x", *(unsigned char *) sp);
1573               dp += 2;
1574             }
1575           return stpcpy (dp, ">cvn");
1576         }
1577       }
1578   dest[0] = '/';
1579   return stpcpy (&dest[1], string);
1580 }
1581
1582 /* Adds the string STRING to buffer DEST as a PostScript quoted
1583    string; returns a pointer to the null terminator added.  Will not
1584    add more than 235 characters. */
1585 static char *
1586 quote_ps_string (char *dest, const char *string)
1587 {
1588   const char *sp = string;
1589   char *dp = dest;
1590
1591   *dp++ = '(';
1592   for (; *sp && dp < &dest[235]; sp++)
1593     if (*sp == '(')
1594       dp = stpcpy (dp, "\\(");
1595     else if (*sp == ')')
1596       dp = stpcpy (dp, "\\)");
1597     else if (*sp < 32 || *((unsigned char *) sp) > 127)
1598       dp = spprintf (dp, "\\%3o", *sp);
1599     else
1600       *dp++ = *sp;
1601   return stpcpy (dp, ")");
1602 }
1603
1604 /* Writes the PostScript epilogue to file F. */
1605 static int
1606 preclose (struct file_ext *f)
1607 {
1608   struct outp_driver *this = f->param;
1609   struct ps_driver_ext *x = this->ext;
1610   struct hsh_iterator iter;
1611   struct font_entry *fe;
1612
1613   fprintf (f->file,
1614            ("%%%%Trailer%s"
1615             "%%%%Pages: %d%s"
1616             "%%%%DocumentNeededResources:%s"),
1617            x->eol, x->file_page_number, x->eol, x->eol);
1618
1619   for (fe = hsh_first (x->loaded, &iter); fe != NULL;
1620        fe = hsh_next (x->loaded, &iter)) 
1621     {
1622       char buf[256], *cp;
1623
1624       cp = stpcpy (buf, "%%+ font ");
1625       cp = quote_ps_string (cp, fe->font->internal_name);
1626       strcpy (cp, x->eol);
1627       fputs (buf, f->file);
1628     }
1629
1630   hsh_destroy (x->loaded);
1631   x->loaded = NULL;
1632   hsh_destroy (x->combos);
1633   x->combos = NULL;
1634   x->last_font = NULL;
1635   x->next_combo = 0;
1636
1637   fprintf (f->file, "%%EOF%s", x->eol);
1638   if (ferror (f->file))
1639     return 0;
1640   return 1;
1641 }
1642
1643 static int
1644 ps_open_page (struct outp_driver *this)
1645 {
1646   struct ps_driver_ext *x = this->ext;
1647
1648   assert (this->driver_open && !this->page_open);
1649       
1650   x->page_number++;
1651   if (!fn_open_ext (&x->file))
1652     {
1653       if (errno)
1654         msg (ME, _("PostScript output driver: %s: %s"), x->file.filename,
1655              strerror (errno));
1656       return 0;
1657     }
1658   x->file_page_number++;
1659
1660   hsh_destroy (x->combos);
1661   x->combos = hsh_create (31, compare_ps_combo, hash_ps_combo,
1662                           free_ps_combo, NULL);
1663   x->last_font = NULL;
1664   x->next_combo = 0;
1665
1666   fprintf (x->file.file,
1667            "%%%%Page: %d %d%s"
1668            "%%%%BeginPageSetup%s"
1669            "/pg save def 0.001 dup scale%s",
1670            x->page_number, x->file_page_number, x->eol,
1671            x->eol,
1672            x->eol);
1673
1674   if (x->orientation == OTN_LANDSCAPE)
1675     fprintf (x->file.file,
1676              "%d 0 translate 90 rotate%s",
1677              x->w, x->eol);
1678
1679   if (x->bottom_margin != 0 || x->left_margin != 0)
1680     fprintf (x->file.file,
1681              "%d %d translate%s",
1682              x->left_margin, x->bottom_margin, x->eol);
1683
1684   fprintf (x->file.file,
1685            "/LW %d def/TW %d def %d setlinewidth%s"
1686            "%%%%EndPageSetup%s",
1687            x->line_width, x->line_width_thick, x->line_width, x->eol,
1688            x->eol);
1689
1690   if (!ferror (x->file.file))
1691     {
1692       this->page_open = 1;
1693       if (x->output_options & OPO_HEADERS)
1694         draw_headers (this);
1695     }
1696
1697   this->cp_y = 0;
1698
1699   return !ferror (x->file.file);
1700 }
1701
1702 static int
1703 ps_close_page (struct outp_driver *this)
1704 {
1705   struct ps_driver_ext *x = this->ext;
1706
1707   assert (this->driver_open && this->page_open);
1708   
1709   if (x->line_opt)
1710     dump_lines (this);
1711
1712   fprintf (x->file.file,
1713            "%%PageTrailer%s"
1714            "EP%s",
1715            x->eol, x->eol);
1716
1717   this->page_open = 0;
1718   return !ferror (x->file.file);
1719 }
1720
1721 static void
1722 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
1723 {
1724   switch (s->type) 
1725     {
1726     case SOM_CHART:
1727       break;
1728     default:
1729       assert(0);
1730       break;
1731     }
1732 }
1733 \f
1734 /* Lines. */
1735
1736 /* qsort() comparison function for int tuples. */
1737 static int
1738 int_2_compare (const void *a_, const void *b_)
1739 {
1740   const int *a = a_;
1741   const int *b = b_;
1742
1743   return *a < *b ? -1 : *a > *b;
1744 }
1745
1746 /* Hash table comparison function for cached lines. */
1747 static int
1748 compare_line (const void *a_, const void *b_, void *foo UNUSED)
1749 {
1750   const struct line_form *a = a_;
1751   const struct line_form *b = b_;
1752
1753   return a->ind < b->ind ? -1 : a->ind > b->ind;
1754 }
1755
1756 /* Hash table hash function for cached lines. */
1757 static unsigned
1758 hash_line (const void *pa, void *foo UNUSED)
1759 {
1760   const struct line_form *a = pa;
1761
1762   return a->ind;
1763 }
1764
1765 /* Hash table free function for cached lines. */
1766 static void
1767 free_line (void *pa, void *foo UNUSED)
1768 {
1769   free (pa);
1770 }
1771
1772 /* Writes PostScript code to draw a line from (x1,y1) to (x2,y2) to
1773    the output file. */
1774 #define dump_line(x1, y1, x2, y2)                       \
1775         fprintf (ext->file.file, "%d %d %d %d L%s",     \
1776                  x1, YT (y1), x2, YT (y2), ext->eol)
1777
1778 /* Write PostScript code to draw a thick line from (x1,y1) to (x2,y2)
1779    to the output file. */
1780 #define dump_thick_line(x1, y1, x2, y2)                 \
1781         fprintf (ext->file.file, "%d %d %d %d TL%s",    \
1782                  x1, YT (y1), x2, YT (y2), ext->eol)
1783
1784 /* Writes a line of type TYPE to THIS driver's output file.  The line
1785    (or its center, in the case of double lines) has its independent
1786    axis coordinate at IND; it extends from DEP1 to DEP2 on the
1787    dependent axis. */
1788 static void
1789 dump_fancy_line (struct outp_driver *this, int type, int ind, int dep1, int dep2)
1790 {
1791   struct ps_driver_ext *ext = this->ext;
1792   int ofs = ext->line_space / 2 + ext->line_width / 2;
1793
1794   switch (type)
1795     {
1796     case horz:
1797       dump_line (dep1, ind, dep2, ind);
1798       break;
1799     case dbl_horz:
1800       if (ext->output_options & OPO_DOUBLE_LINE)
1801         {
1802           dump_line (dep1, ind - ofs, dep2, ind - ofs);
1803           dump_line (dep1, ind + ofs, dep2, ind + ofs);
1804         }
1805       else
1806         dump_thick_line (dep1, ind, dep2, ind);
1807       break;
1808     case spl_horz:
1809       assert (0);
1810     case vert:
1811       dump_line (ind, dep1, ind, dep2);
1812       break;
1813     case dbl_vert:
1814       if (ext->output_options & OPO_DOUBLE_LINE)
1815         {
1816           dump_line (ind - ofs, dep1, ind - ofs, dep2);
1817           dump_line (ind + ofs, dep1, ind + ofs, dep2);
1818         }
1819       else
1820         dump_thick_line (ind, dep1, ind, dep2);
1821       break;
1822     case spl_vert:
1823       assert (0);
1824     default:
1825       assert (0);
1826     }
1827 }
1828
1829 #undef dump_line
1830
1831 /* Writes all the cached lines to the output file, then clears the
1832    cache. */
1833 static void
1834 dump_lines (struct outp_driver *this)
1835 {
1836   struct ps_driver_ext *x = this->ext;
1837
1838   struct hsh_iterator iter;
1839   int type;
1840
1841   for (type = 0; type < n_line_types; type++)
1842     {
1843       struct line_form *line;
1844
1845       if (x->lines[type] == NULL) 
1846         continue;
1847
1848       for (line = hsh_first (x->lines[type], &iter); line != NULL;
1849            line = hsh_next (x->lines[type], &iter)) 
1850         {
1851           int i;
1852           int lo = INT_MIN, hi;
1853
1854           qsort (line->dep, line->ndep, sizeof *line->dep, int_2_compare);
1855           lo = line->dep[0][0];
1856           hi = line->dep[0][1];
1857           for (i = 1; i < line->ndep; i++)
1858             if (line->dep[i][0] <= hi + 1)
1859               {
1860                 int min_hi = line->dep[i][1];
1861                 if (min_hi > hi)
1862                   hi = min_hi;
1863               }
1864             else
1865               {
1866                 dump_fancy_line (this, type, line->ind, lo, hi);
1867                 lo = line->dep[i][0];
1868                 hi = line->dep[i][1];
1869               }
1870           dump_fancy_line (this, type, line->ind, lo, hi);
1871         }
1872
1873       hsh_destroy (x->lines[type]);
1874       x->lines[type] = NULL;
1875     }
1876 }
1877
1878 /* (Same args as dump_fancy_line()).  Either dumps the line directly
1879    to the output file, or adds it to the cache, depending on the
1880    user-selected line optimization mode. */
1881 static void
1882 line (struct outp_driver *this, int type, int ind, int dep1, int dep2)
1883 {
1884   struct ps_driver_ext *ext = this->ext;
1885   struct line_form **f;
1886
1887   assert (dep2 >= dep1);
1888   if (ext->line_opt == 0)
1889     {
1890       dump_fancy_line (this, type, ind, dep1, dep2);
1891       return;
1892     }
1893
1894   if (ext->lines[type] == NULL)
1895     ext->lines[type] = hsh_create (31, compare_line, hash_line,
1896                                    free_line, NULL);
1897   f = (struct line_form **) hsh_probe (ext->lines[type], &ind);
1898   if (*f == NULL)
1899     {
1900       *f = xmalloc (sizeof **f + sizeof (int[15][2]));
1901       (*f)->ind = ind;
1902       (*f)->mdep = 16;
1903       (*f)->ndep = 1;
1904       (*f)->dep[0][0] = dep1;
1905       (*f)->dep[0][1] = dep2;
1906       return;
1907     }
1908   if ((*f)->ndep >= (*f)->mdep)
1909     {
1910       (*f)->mdep += 16;
1911       *f = xrealloc (*f, (sizeof **f + sizeof (int[2]) * ((*f)->mdep - 1)));
1912     }
1913   (*f)->dep[(*f)->ndep][0] = dep1;
1914   (*f)->dep[(*f)->ndep][1] = dep2;
1915   (*f)->ndep++;
1916 }
1917
1918 static void
1919 ps_line_horz (struct outp_driver *this, const struct rect *r,
1920               const struct color *c UNUSED, int style)
1921 {
1922   /* Must match output.h:OUTP_L_*. */
1923   static const int types[OUTP_L_COUNT] =
1924   {-1, horz, dbl_horz, spl_horz};
1925
1926   int y = (r->y1 + r->y2) / 2;
1927
1928   assert (this->driver_open && this->page_open);
1929   assert (style >= 0 && style < OUTP_L_COUNT);
1930   style = types[style];
1931   if (style != -1)
1932     line (this, style, y, r->x1, r->x2);
1933 }
1934
1935 static void
1936 ps_line_vert (struct outp_driver *this, const struct rect *r,
1937               const struct color *c UNUSED, int style)
1938 {
1939   /* Must match output.h:OUTP_L_*. */
1940   static const int types[OUTP_L_COUNT] =
1941   {-1, vert, dbl_vert, spl_vert};
1942
1943   int x = (r->x1 + r->x2) / 2;
1944
1945   assert (this->driver_open && this->page_open);
1946   assert (style >= 0 && style < OUTP_L_COUNT);
1947   style = types[style];
1948   if (style != -1)
1949     line (this, style, x, r->y1, r->y2);
1950 }
1951
1952 #define L (style->l != OUTP_L_NONE)
1953 #define R (style->r != OUTP_L_NONE)
1954 #define T (style->t != OUTP_L_NONE)
1955 #define B (style->b != OUTP_L_NONE)
1956
1957 static void
1958 ps_line_intersection (struct outp_driver *this, const struct rect *r,
1959                       const struct color *c UNUSED,
1960                       const struct outp_styles *style)
1961 {
1962   struct ps_driver_ext *ext = this->ext;
1963
1964   int x = (r->x1 + r->x2) / 2;
1965   int y = (r->y1 + r->y2) / 2;
1966   int ofs = (ext->line_space + ext->line_width) / 2;
1967   int x1 = x - ofs, x2 = x + ofs;
1968   int y1 = y - ofs, y2 = y + ofs;
1969
1970   assert (this->driver_open && this->page_open);
1971   assert (!((style->l != style->r && style->l != OUTP_L_NONE
1972              && style->r != OUTP_L_NONE)
1973             || (style->t != style->b && style->t != OUTP_L_NONE
1974                 && style->b != OUTP_L_NONE)));
1975
1976   switch ((style->l | style->r) | ((style->t | style->b) << 8))
1977     {
1978     case (OUTP_L_SINGLE) | (OUTP_L_SINGLE << 8):
1979     case (OUTP_L_SINGLE) | (OUTP_L_NONE << 8):
1980     case (OUTP_L_NONE) | (OUTP_L_SINGLE << 8):
1981       if (L)
1982         line (this, horz, y, r->x1, x);
1983       if (R)
1984         line (this, horz, y, x, r->x2);
1985       if (T)
1986         line (this, vert, x, r->y1, y);
1987       if (B)
1988         line (this, vert, x, y, r->y2);
1989       break;
1990     case (OUTP_L_SINGLE) | (OUTP_L_DOUBLE << 8):
1991     case (OUTP_L_NONE) | (OUTP_L_DOUBLE << 8):
1992       if (L)
1993         line (this, horz, y, r->x1, x1);
1994       if (R)
1995         line (this, horz, y, x2, r->x2);
1996       if (T)
1997         line (this, dbl_vert, x, r->y1, y);
1998       if (B)
1999         line (this, dbl_vert, x, y, r->y2);
2000       if ((L && R) && !(T && B))
2001         line (this, horz, y, x1, x2);
2002       break;
2003     case (OUTP_L_DOUBLE) | (OUTP_L_SINGLE << 8):
2004     case (OUTP_L_DOUBLE) | (OUTP_L_NONE << 8):
2005       if (L)
2006         line (this, dbl_horz, y, r->x1, x);
2007       if (R)
2008         line (this, dbl_horz, y, x, r->x2);
2009       if (T)
2010         line (this, vert, x, r->y1, y);
2011       if (B)
2012         line (this, vert, x, y, r->y2);
2013       if ((T && B) && !(L && R))
2014         line (this, vert, x, y1, y2);
2015       break;
2016     case (OUTP_L_DOUBLE) | (OUTP_L_DOUBLE << 8):
2017       if (L)
2018         line (this, dbl_horz, y, r->x1, x);
2019       if (R)
2020         line (this, dbl_horz, y, x, r->x2);
2021       if (T)
2022         line (this, dbl_vert, x, r->y1, y);
2023       if (B)
2024         line (this, dbl_vert, x, y, r->y2);
2025       if (T && B && !L)
2026         line (this, vert, x1, y1, y2);
2027       if (T && B && !R)
2028         line (this, vert, x2, y1, y2);
2029       if (L && R && !T)
2030         line (this, horz, y1, x1, x2);
2031       if (L && R && !B)
2032         line (this, horz, y2, x1, x2);
2033       break;
2034     default:
2035       assert (0);
2036     }
2037 }
2038
2039 static void
2040 ps_box (struct outp_driver *this UNUSED, const struct rect *r UNUSED,
2041         const struct color *bord UNUSED, const struct color *fill UNUSED)
2042 {
2043   assert (this->driver_open && this->page_open);
2044 }
2045
2046 static void 
2047 ps_polyline_begin (struct outp_driver *this UNUSED,
2048                    const struct color *c UNUSED)
2049 {
2050   assert (this->driver_open && this->page_open);
2051 }
2052 static void 
2053 ps_polyline_point (struct outp_driver *this UNUSED, int x UNUSED, int y UNUSED)
2054 {
2055   assert (this->driver_open && this->page_open);
2056 }
2057 static void 
2058 ps_polyline_end (struct outp_driver *this UNUSED)
2059 {
2060   assert (this->driver_open && this->page_open);
2061 }
2062
2063 /* Returns the width of string S for THIS driver. */
2064 static int
2065 text_width (struct outp_driver *this, char *s)
2066 {
2067   struct outp_text text;
2068
2069   text.options = OUTP_T_JUST_LEFT;
2070   ls_init (&text.s, s, strlen (s));
2071   this->class->text_metrics (this, &text);
2072   return text.h;
2073 }
2074
2075 /* Write string S at location (X,Y) with width W for THIS driver. */
2076 static void
2077 out_text_plain (struct outp_driver *this, char *s, int x, int y, int w)
2078 {
2079   struct outp_text text;
2080
2081   text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT;
2082   ls_init (&text.s, s, strlen (s));
2083   text.h = w;
2084   text.v = this->font_height;
2085   text.x = x;
2086   text.y = y;
2087   this->class->text_draw (this, &text);
2088 }
2089
2090 /* Draw top of page headers for THIS driver. */
2091 static void
2092 draw_headers (struct outp_driver *this)
2093 {
2094   struct ps_driver_ext *ext = this->ext;
2095   
2096   struct font_entry *old_current = ext->current;
2097   char *old_family = xstrdup (ext->family); /* FIXME */
2098   int old_size = ext->size;
2099
2100   int fh = this->font_height;
2101   int y = -3 * fh;
2102
2103   fprintf (ext->file.file, "%d %d %d %d GB%s",
2104            0, YT (y), this->width, YT (y + 2 * fh + ext->line_gutter),
2105            ext->eol);
2106   this->class->text_set_font_family (this, "T");
2107
2108   y += ext->line_width + ext->line_gutter;
2109   
2110   {
2111     int rh_width;
2112     char buf[128];
2113
2114     sprintf (buf, _("%s - Page %d"), curdate, ext->page_number);
2115     rh_width = text_width (this, buf);
2116
2117     out_text_plain (this, buf, this->width - this->prop_em_width - rh_width,
2118                     y, rh_width);
2119
2120     if (outp_title && outp_subtitle)
2121       out_text_plain (this, outp_title, this->prop_em_width, y,
2122                       this->width - 3 * this->prop_em_width - rh_width);
2123
2124     y += fh;
2125   }
2126   
2127   {
2128     int rh_width;
2129     char buf[128];
2130     char *string = outp_subtitle ? outp_subtitle : outp_title;
2131
2132     sprintf (buf, "%s - %s", version, host_system);
2133     rh_width = text_width (this, buf);
2134     
2135     out_text_plain (this, buf, this->width - this->prop_em_width - rh_width,
2136                     y, rh_width);
2137
2138     if (string)
2139       out_text_plain (this, string, this->prop_em_width, y,
2140                       this->width - 3 * this->prop_em_width - rh_width);
2141
2142     y += fh;
2143   }
2144
2145   ext->current = old_current;
2146   free (ext->family);
2147   ext->family = old_family;
2148   ext->size = old_size;
2149 }
2150
2151 \f
2152 /* Text. */
2153
2154 static void
2155 ps_text_set_font_by_name (struct outp_driver *this, const char *dit)
2156 {
2157   struct ps_driver_ext *x = this->ext;
2158   struct font_entry *fe;
2159
2160   assert (this->driver_open && this->page_open);
2161   
2162   /* Short-circuit common fonts. */
2163   if (!strcmp (dit, "PROP"))
2164     {
2165       x->current = x->prop;
2166       x->size = x->font_size;
2167       return;
2168     }
2169   else if (!strcmp (dit, "FIXED"))
2170     {
2171       x->current = x->fixed;
2172       x->size = x->font_size;
2173       return;
2174     }
2175
2176   /* Find font_desc corresponding to Groff name dit. */
2177   fe = hsh_find (x->loaded, &dit);
2178   if (fe == NULL)
2179     fe = load_font (this, dit);
2180   x->current = fe;
2181 }
2182
2183 static void
2184 ps_text_set_font_by_position (struct outp_driver *this, int pos)
2185 {
2186   struct ps_driver_ext *x = this->ext;
2187   char *dit;
2188
2189   assert (this->driver_open && this->page_open);
2190
2191   /* Determine font name by suffixing position string to font family
2192      name. */
2193   {
2194     char *cp;
2195
2196     dit = local_alloc (strlen (x->family) + 3);
2197     cp = stpcpy (dit, x->family);
2198     switch (pos)
2199       {
2200       case OUTP_F_R:
2201         *cp++ = 'R';
2202         break;
2203       case OUTP_F_I:
2204         *cp++ = 'I';
2205         break;
2206       case OUTP_F_B:
2207         *cp++ = 'B';
2208         break;
2209       case OUTP_F_BI:
2210         *cp++ = 'B';
2211         *cp++ = 'I';
2212         break;
2213       default:
2214         assert(0);
2215       }
2216     *cp++ = 0;
2217   }
2218   
2219   /* Find font_desc corresponding to Groff name dit. */
2220   {
2221     struct font_entry *fe = hsh_find (x->loaded, &dit);
2222     if (fe == NULL)
2223       fe = load_font (this, dit);
2224     x->current = fe;
2225   }
2226
2227   local_free (dit);
2228 }
2229
2230 static void
2231 ps_text_set_font_family (struct outp_driver *this, const char *s)
2232 {
2233   struct ps_driver_ext *x = this->ext;
2234
2235   assert (this->driver_open && this->page_open);
2236   
2237   free(x->family);
2238   x->family = xstrdup (s);
2239 }
2240
2241 static const char *
2242 ps_text_get_font_name (struct outp_driver *this)
2243 {
2244   struct ps_driver_ext *x = this->ext;
2245
2246   assert (this->driver_open && this->page_open);
2247   return x->current->font->name;
2248 }
2249
2250 static const char *
2251 ps_text_get_font_family (struct outp_driver *this)
2252 {
2253   struct ps_driver_ext *x = this->ext;
2254   
2255   assert (this->driver_open && this->page_open);
2256   return x->family;
2257 }
2258
2259 static int
2260 ps_text_set_size (struct outp_driver *this, int size)
2261 {
2262   struct ps_driver_ext *x = this->ext;
2263
2264   assert (this->driver_open && this->page_open);
2265   x->size = PSUS / 72000 * size;
2266   return 1;
2267 }
2268
2269 static int
2270 ps_text_get_size (struct outp_driver *this, int *em_width)
2271 {
2272   struct ps_driver_ext *x = this->ext;
2273
2274   assert (this->driver_open && this->page_open);
2275   if (em_width)
2276     *em_width = (x->current->font->space_width * x->size) / 1000;
2277   return x->size / (PSUS / 72000);
2278 }
2279
2280 /* An output character. */
2281 struct output_char
2282   {
2283     struct font_entry *font;    /* Font of character. */
2284     int size;                   /* Size of character. */
2285     int x, y;                   /* Location of character. */
2286     unsigned char ch;           /* Character. */
2287     char separate;              /* Must be separate from previous char. */
2288   };
2289
2290 /* Hash table comparison function for ps_combo structs. */
2291 static int
2292 compare_ps_combo (const void *pa, const void *pb, void *foo UNUSED)
2293 {
2294   const struct ps_font_combo *a = pa;
2295   const struct ps_font_combo *b = pb;
2296
2297   return !((a->font == b->font) && (a->size == b->size));
2298 }
2299
2300 /* Hash table hash function for ps_combo structs. */
2301 static unsigned
2302 hash_ps_combo (const void *pa, void *foo UNUSED)
2303 {
2304   const struct ps_font_combo *a = pa;
2305   unsigned name_hash = hsh_hash_string (a->font->font->internal_name);
2306   return name_hash ^ hsh_hash_int (a->size);
2307 }
2308
2309 /* Hash table free function for ps_combo structs. */
2310 static void
2311 free_ps_combo (void *a, void *foo UNUSED)
2312 {
2313   free (a);
2314 }
2315
2316 /* Causes PostScript code to be output that switches to the font
2317    CP->FONT and font size CP->SIZE.  The first time a particular
2318    font/size combination is used on a particular page, this involves
2319    outputting PostScript code to load the font. */
2320 static void
2321 switch_font (struct outp_driver *this, const struct output_char *cp)
2322 {
2323   struct ps_driver_ext *ext = this->ext;
2324   struct ps_font_combo srch, **fc;
2325
2326   srch.font = cp->font;
2327   srch.size = cp->size;
2328
2329   fc = (struct ps_font_combo **) hsh_probe (ext->combos, &srch);
2330   if (*fc)
2331     {
2332       fprintf (ext->file.file, "F%x%s", (*fc)->index, ext->eol);
2333     }
2334   else
2335     {
2336       char *filename;
2337       struct ps_encoding *encoding;
2338       char buf[512], *bp;
2339
2340       *fc = xmalloc (sizeof **fc);
2341       (*fc)->font = cp->font;
2342       (*fc)->size = cp->size;
2343       (*fc)->index = ext->next_combo++;
2344
2345       filename = find_encoding_file (this, cp->font->font->encoding);
2346       if (filename)
2347         {
2348           encoding = get_encoding (this, filename);
2349           free (filename);
2350         }
2351       else
2352         {
2353           msg (IE, _("PostScript driver: Cannot find encoding `%s' for "
2354                "PostScript font `%s'."), cp->font->font->encoding,
2355                cp->font->font->internal_name);
2356           encoding = default_encoding (this);
2357         }
2358
2359       if (cp->font != ext->fixed && cp->font != ext->prop)
2360         {
2361           bp = stpcpy (buf, "%%IncludeResource: font ");
2362           bp = quote_ps_string (bp, cp->font->font->internal_name);
2363           bp = stpcpy (bp, ext->eol);
2364         }
2365       else
2366         bp = buf;
2367
2368       bp = spprintf (bp, "/F%x E%x %d", (*fc)->index, encoding->index,
2369                      cp->size);
2370       bp = quote_ps_name (bp, cp->font->font->internal_name);
2371       sprintf (bp, " SF%s", ext->eol);
2372       fputs (buf, ext->file.file);
2373     }
2374   ext->last_font = *fc;
2375 }
2376
2377 /* (write_text) Writes the accumulated line buffer to the output
2378    file. */
2379 #define output_line()                           \
2380         do                                      \
2381           {                                     \
2382             lp = stpcpy (lp, ext->eol);         \
2383             *lp = 0;                            \
2384             fputs (line, ext->file.file);       \
2385             lp = line;                          \
2386           }                                     \
2387         while (0)
2388
2389 /* (write_text) Adds the string representing number X to the line
2390    buffer, flushing the buffer to disk beforehand if necessary. */
2391 #define put_number(X)                           \
2392         do                                      \
2393           {                                     \
2394             int n = nsprintf (number, "%d", X); \
2395             if (n + lp > &line[75])             \
2396               output_line ();                   \
2397             lp = stpcpy (lp, number);           \
2398           }                                     \
2399         while (0)
2400
2401 /* Outputs PostScript code to THIS driver's output file to display the
2402    characters represented by the output_char's between CP and END,
2403    using the associated outp_text T to determine formatting.  WIDTH is
2404    the width of the output region; WIDTH_LEFT is the amount of the
2405    WIDTH that is not taken up by text (so it can be used to determine
2406    justification). */
2407 static void
2408 write_text (struct outp_driver *this,
2409             const struct output_char *cp, const struct output_char *end,
2410             struct outp_text *t, int width UNUSED, int width_left)
2411 {
2412   struct ps_driver_ext *ext = this->ext;
2413   int ofs;
2414
2415   int last_y;
2416
2417   char number[INT_DIGITS + 1];
2418   char line[80];
2419   char *lp;
2420
2421   switch (t->options & OUTP_T_JUST_MASK)
2422     {
2423     case OUTP_T_JUST_LEFT:
2424       ofs = 0;
2425       break;
2426     case OUTP_T_JUST_RIGHT:
2427       ofs = width_left;
2428       break;
2429     case OUTP_T_JUST_CENTER:
2430       ofs = width_left / 2;
2431       break;
2432     default:
2433       assert (0);
2434       abort ();
2435     }
2436
2437   lp = line;
2438   last_y = INT_MIN;
2439   while (cp < end)
2440     {
2441       int x = cp->x + ofs;
2442       int y = cp->y + (cp->font->font->ascent * cp->size / 1000);
2443
2444       if (ext->last_font == NULL
2445           || cp->font != ext->last_font->font
2446           || cp->size != ext->last_font->size)
2447         switch_font (this, cp);
2448
2449       *lp++ = '(';
2450       do
2451         {
2452           /* PORTME! */
2453           static unsigned char literal_chars[ODA_COUNT][32] =
2454           {
2455             {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff,
2456              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
2457              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2458              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2459             },
2460             {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff,
2461              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2462              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2463              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2464             },
2465             {0x7e, 0xd6, 0xff, 0xfb, 0xff, 0xfc, 0xff, 0xff,
2466              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2467              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2468              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2469             }
2470           };
2471
2472           if (TEST_BIT (literal_chars[ext->data], cp->ch))
2473             *lp++ = cp->ch;
2474           else
2475             switch (cp->ch)
2476               {
2477               case '(':
2478                 lp = stpcpy (lp, "\\(");
2479                 break;
2480               case ')':
2481                 lp = stpcpy (lp, "\\)");
2482                 break;
2483               default:
2484                 lp = spprintf (lp, "\\%03o", cp->ch);
2485                 break;
2486               }
2487           cp++;
2488         }
2489       while (cp < end && lp < &line[70] && cp->separate == 0);
2490       *lp++ = ')';
2491
2492       put_number (x);
2493
2494       if (y != last_y)
2495         {
2496           *lp++ = ' ';
2497           put_number (YT (y));
2498           *lp++ = ' ';
2499           *lp++ = 'S';
2500           last_y = y;
2501         }
2502       else
2503         {
2504           *lp++ = ' ';
2505           *lp++ = 'T';
2506         }
2507
2508       if (lp >= &line[70])
2509         output_line ();
2510     }
2511   if (lp != line)
2512     output_line ();
2513 }
2514
2515 #undef output_line
2516 #undef put_number
2517
2518 /* Displays the text in outp_text T, if DRAW is nonzero; or, merely
2519    determine the text metrics, if DRAW is zero. */
2520 static void
2521 text (struct outp_driver *this, struct outp_text *t, int draw)
2522 {
2523   struct ps_driver_ext *ext = this->ext;
2524
2525   /* Output. */
2526   struct output_char *buf;      /* Output buffer. */
2527   struct output_char *buf_end;  /* End of output buffer. */
2528   struct output_char *buf_loc;  /* Current location in output buffer. */
2529
2530   /* Saved state. */
2531   struct font_entry *old_current = ext->current;
2532   char *old_family = xstrdup (ext->family); /* FIXME */
2533   int old_size = ext->size;
2534
2535   /* Input string. */
2536   char *cp, *end;
2537
2538   /* Current location. */
2539   int x, y;
2540
2541   /* Keeping track of what's left over. */
2542   int width;                    /* Width available for characters. */
2543   int width_left, height_left;  /* Width, height left over. */
2544   int max_height;               /* Tallest character on this line so far. */
2545
2546   /* Previous character. */
2547   int prev_char;
2548
2549   /* Information about location of previous space. */
2550   char *space_char;             /* Character after space. */
2551   struct output_char *space_buf_loc; /* Buffer location after space. */
2552   int space_width_left;         /* Width of characters before space. */
2553
2554   /* Name of the current character. */
2555   const char *char_name;
2556   char local_char_name[2] = {0, 0};
2557
2558   local_char_name[0] = local_char_name[1] = 0;
2559
2560   buf = local_alloc (sizeof *buf * 128);
2561   buf_end = &buf[128];
2562   buf_loc = buf;
2563
2564   assert (!ls_null_p (&t->s));
2565   cp = ls_c_str (&t->s);
2566   end = ls_end (&t->s);
2567   if (draw)
2568     {
2569       x = t->x;
2570       y = t->y;
2571     }
2572   else
2573     x = y = 0;
2574   width = width_left = (t->options & OUTP_T_HORZ) ? t->h : INT_MAX;
2575   height_left = (t->options & OUTP_T_VERT) ? t->v : INT_MAX;
2576   max_height = 0;
2577   prev_char = -1;
2578   space_char = NULL;
2579   space_buf_loc = NULL;
2580   space_width_left = 0;
2581   
2582
2583   if (!width || !height_left)
2584     goto exit;
2585
2586   while (cp < end)
2587     {
2588       struct char_metrics *metric;
2589       int cur_char;
2590       int kern_amt;
2591       int char_width;
2592       int separate = 0;
2593
2594       /* Set char_name to the name of the character or ligature at
2595          *cp. */
2596       local_char_name[0] = *cp;
2597       char_name = local_char_name;
2598       if (ext->current->font->ligatures && *cp == 'f')
2599         {
2600           int lig = 0;
2601           char_name = NULL;
2602
2603           if (cp < end - 1)
2604             switch (cp[1])
2605               {
2606               case 'i':
2607                 lig = LIG_fi, char_name = "fi";
2608                 break;
2609               case 'l':
2610                 lig = LIG_fl, char_name = "fl";
2611                 break;
2612               case 'f':
2613                 if (cp < end - 2)
2614                   switch (cp[2])
2615                     {
2616                     case 'i':
2617                       lig = LIG_ffi, char_name = "ffi";
2618                       goto got_ligature;
2619                     case 'l':
2620                       lig = LIG_ffl, char_name = "ffl";
2621                       goto got_ligature;
2622                     }
2623                 lig = LIG_ff, char_name = "ff";
2624               got_ligature:
2625                 break;
2626               }
2627           if ((lig & ext->current->font->ligatures) == 0)
2628             {
2629               local_char_name[0] = *cp; /* 'f' */
2630               char_name = local_char_name;
2631             }
2632         }
2633       else if (*cp == '\n')
2634         {
2635           if (draw)
2636             {
2637               write_text (this, buf, buf_loc, t, width, width_left);
2638               buf_loc = buf;
2639               x = t->x;
2640               y += max_height;
2641             }
2642
2643           width_left = width;
2644           height_left -= max_height;
2645           max_height = 0;
2646           kern_amt = 0;
2647           separate = 1;
2648           cp++;
2649
2650           /* FIXME: when we're page buffering it will be necessary to
2651              set separate to 1. */
2652           continue;
2653         }
2654       cp += strlen (char_name);
2655
2656       /* Figure out what size this character is, and what kern
2657          adjustment we need. */
2658       cur_char = font_char_name_to_index (char_name);
2659       metric = font_get_char_metrics (ext->current->font, cur_char);
2660       if (!metric)
2661         {
2662           static struct char_metrics m;
2663           metric = &m;
2664           m.width = ext->current->font->space_width;
2665           m.code = *char_name;
2666         }
2667       kern_amt = font_get_kern_adjust (ext->current->font, prev_char,
2668                                        cur_char);
2669       if (kern_amt)
2670         {
2671           kern_amt = (kern_amt * ext->size / 1000);
2672           separate = 1;
2673         }
2674       char_width = metric->width * ext->size / 1000;
2675
2676       /* Record the current status if this is a space character. */
2677       if (cur_char == space_index && buf_loc > buf)
2678         {
2679           space_char = cp;
2680           space_buf_loc = buf_loc;
2681           space_width_left = width_left;
2682         }
2683
2684       /* Drop down to a new line if there's no room left on this
2685          line. */
2686       if (char_width + kern_amt > width_left)
2687         {
2688           /* Regress to previous space, if any. */
2689           if (space_char)
2690             {
2691               cp = space_char;
2692               width_left = space_width_left;
2693               buf_loc = space_buf_loc;
2694             }
2695
2696           if (draw)
2697             {
2698               write_text (this, buf, buf_loc, t, width, width_left);
2699               buf_loc = buf;
2700               x = t->x;
2701               y += max_height;
2702             }
2703
2704           width_left = width;
2705           height_left -= max_height;
2706           max_height = 0;
2707           kern_amt = 0;
2708
2709           if (space_char)
2710             {
2711               space_char = NULL;
2712               prev_char = -1;
2713               /* FIXME: when we're page buffering it will be
2714                  necessary to set separate to 1. */
2715               continue;
2716             }
2717           separate = 1;
2718         }
2719       if (ext->size > max_height)
2720         max_height = ext->size;
2721       if (max_height > height_left)
2722         goto exit;
2723
2724       /* Actually draw the character. */
2725       if (draw)
2726         {
2727           if (buf_loc >= buf_end)
2728             {
2729               int buf_len = buf_end - buf;
2730
2731               if (buf_len == 128)
2732                 {
2733                   struct output_char *new_buf;
2734
2735                   new_buf = xmalloc (sizeof *new_buf * 256);
2736                   memcpy (new_buf, buf, sizeof *new_buf * 128);
2737                   buf_loc = new_buf + 128;
2738                   buf_end = new_buf + 256;
2739                   local_free (buf);
2740                   buf = new_buf;
2741                 }
2742               else
2743                 {
2744                   buf = xrealloc (buf, sizeof *buf * buf_len * 2);
2745                   buf_loc = buf + buf_len;
2746                   buf_end = buf + buf_len * 2;
2747                 }
2748             }
2749
2750           x += kern_amt;
2751           buf_loc->font = ext->current;
2752           buf_loc->size = ext->size;
2753           buf_loc->x = x;
2754           buf_loc->y = y;
2755           buf_loc->ch = metric->code;
2756           buf_loc->separate = separate;
2757           buf_loc++;
2758           x += char_width;
2759         }
2760
2761       /* Prepare for next iteration. */
2762       width_left -= char_width + kern_amt;
2763       prev_char = cur_char;
2764     }
2765   height_left -= max_height;
2766   if (buf_loc > buf && draw)
2767     write_text (this, buf, buf_loc, t, width, width_left);
2768
2769 exit:
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;
2774   else
2775     t->v -= height_left;
2776   if (buf_end - buf == 128)
2777     local_free (buf);
2778   else
2779     free (buf);
2780   ext->current = old_current;
2781   free (ext->family);
2782   ext->family = old_family;
2783   ext->size = old_size;
2784 }
2785
2786 static void
2787 ps_text_metrics (struct outp_driver *this, struct outp_text *t)
2788 {
2789   assert (this->driver_open && this->page_open);
2790   text (this, t, 0);
2791 }
2792
2793 static void
2794 ps_text_draw (struct outp_driver *this, struct outp_text *t)
2795 {
2796   assert (this->driver_open && this->page_open);
2797   text (this, t, 1);
2798 }
2799 \f
2800 /* Font loader. */
2801
2802 /* Translate a filename to a font. */
2803 struct filename2font
2804   {
2805     char *filename;             /* Normalized filename. */
2806     struct font_desc *font;
2807   };
2808
2809 /* Table of `filename2font's. */
2810 static struct hsh_table *ps_fonts;
2811
2812 /* Hash table comparison function for filename2font structs. */
2813 static int
2814 compare_filename2font (const void *a, const void *b, void *param UNUSED)
2815 {
2816   return strcmp (((struct filename2font *) a)->filename,
2817                  ((struct filename2font *) b)->filename);
2818 }
2819
2820 /* Hash table hash function for filename2font structs. */
2821 static unsigned
2822 hash_filename2font (const void *f2f_, void *param UNUSED)
2823 {
2824   const struct filename2font *f2f = f2f_;
2825   return hsh_hash_string (f2f->filename);
2826 }
2827
2828 /* Initializes the global font list by creating the hash table for
2829    translation of filenames to font_desc structs. */
2830 static void
2831 init_fonts (void)
2832 {
2833   ps_fonts = hsh_create (31, compare_filename2font, hash_filename2font,
2834                          NULL, NULL);
2835 }
2836
2837 static void
2838 done_fonts (void)
2839 {
2840  hsh_destroy (ps_fonts);
2841 }
2842
2843 /* Loads the font having Groff name DIT into THIS driver instance.
2844    Specifically, adds it into the THIS driver's `loaded' hash
2845    table. */
2846 static struct font_entry *
2847 load_font (struct outp_driver *this, const char *dit)
2848 {
2849   struct ps_driver_ext *x = this->ext;
2850   char *filename1, *filename2;
2851   void **entry;
2852   struct font_entry *fe;
2853
2854   filename1 = find_ps_file (this, dit);
2855   if (!filename1)
2856     filename1 = xstrdup (dit);
2857   filename2 = fn_normalize (filename1);
2858   free (filename1);
2859
2860   entry = hsh_probe (ps_fonts, &filename2);
2861   if (*entry == NULL)
2862     {
2863       struct filename2font *f2f;
2864       struct font_desc *f = groff_read_font (filename2);
2865
2866       if (f == NULL)
2867         {
2868           if (x->fixed)
2869             f = x->fixed->font;
2870           else
2871             f = default_font ();
2872         }
2873       
2874       f2f = xmalloc (sizeof *f2f);
2875       f2f->filename = filename2;
2876       f2f->font = f;
2877       *entry = f2f;
2878     }
2879   else
2880     free (filename2);
2881
2882   fe = xmalloc (sizeof *fe);
2883   fe->dit = xstrdup (dit);
2884   fe->font = ((struct filename2font *) * entry)->font;
2885   *hsh_probe (x->loaded, &dit) = fe;
2886
2887   return fe;
2888 }
2889
2890 static void
2891 ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
2892 {
2893 #ifdef NO_CHARTS
2894   ch->lp = NULL;
2895 #else
2896   struct ps_driver_ext *x = this->ext;
2897   char page_size[128];
2898   int size;
2899   int x_origin, y_origin;
2900
2901   ch->file = tmpfile ();
2902   if (ch->file == NULL) 
2903     {
2904       ch->lp = NULL;
2905       return;
2906     }
2907   
2908   size = this->width < this->length ? this->width : this->length;
2909   x_origin = x->left_margin + (size - this->width) / 2;
2910   y_origin = x->bottom_margin + (size - this->length) / 2;
2911
2912   snprintf (page_size, sizeof page_size,
2913             "a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
2914             (double) size / PSUS, (double) size / PSUS,
2915             (double) x_origin / PSUS, (double) y_origin / PSUS);
2916
2917   ch->pl_params = pl_newplparams ();
2918   pl_setplparam (ch->pl_params, "PAGESIZE", page_size);
2919   ch->lp = pl_newpl_r ("ps", NULL, ch->file, stderr, ch->pl_params);
2920 #endif
2921 }
2922
2923 static void 
2924 ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
2925 {
2926 #ifndef NO_CHARTS
2927   struct ps_driver_ext *x = this->ext;
2928   char buf[BUFSIZ];
2929   static int doc_num = 0;
2930
2931   if (this->page_open) 
2932     {
2933       this->class->close_page (this);
2934       this->page_open = 0; 
2935     }
2936   this->class->open_page (this);
2937   fprintf (x->file.file,
2938            "/sp save def%s"
2939            "%d %d translate 1000 dup scale%s"
2940            "userdict begin%s"
2941            "/showpage { } def%s"
2942            "0 setgray 0 setlinecap 1 setlinewidth%s"
2943            "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear%s"
2944            "%%%%BeginDocument: %d%s",
2945            x->eol,
2946            -x->left_margin, -x->bottom_margin, x->eol,
2947            x->eol,
2948            x->eol,
2949            x->eol,
2950            x->eol,
2951            doc_num++, x->eol);
2952
2953   rewind (ch->file);
2954   while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file.file))
2955     continue;
2956   fclose (ch->file);
2957
2958   fprintf (x->file.file,
2959            "%%%%EndDocument%s"
2960            "end%s"
2961            "sp restore%s",
2962            x->eol,
2963            x->eol,
2964            x->eol);
2965   this->class->close_page (this);
2966   this->page_open = 0;
2967 #endif
2968 }
2969
2970 /* PostScript driver class. */
2971 struct outp_class postscript_class =
2972 {
2973   "postscript",
2974   MAGIC_PS,
2975   0,
2976
2977   ps_open_global,
2978   ps_close_global,
2979   ps_font_sizes,
2980
2981   ps_preopen_driver,
2982   ps_option,
2983   ps_postopen_driver,
2984   ps_close_driver,
2985
2986   ps_open_page,
2987   ps_close_page,
2988
2989   ps_submit,
2990
2991   ps_line_horz,
2992   ps_line_vert,
2993   ps_line_intersection,
2994
2995   ps_box,
2996   ps_polyline_begin,
2997   ps_polyline_point,
2998   ps_polyline_end,
2999
3000   ps_text_set_font_by_name,
3001   ps_text_set_font_by_position,
3002   ps_text_set_font_family,
3003   ps_text_get_font_name,
3004   ps_text_get_font_family,
3005   ps_text_set_size,
3006   ps_text_get_size,
3007   ps_text_metrics,
3008   ps_text_draw,
3009
3010   ps_chart_initialise,
3011   ps_chart_finalise
3012 };
3013
3014 /* EPSF driver class.  FIXME: Probably doesn't work right. */
3015 struct outp_class epsf_class =
3016 {
3017   "epsf",
3018   MAGIC_EPSF,
3019   0,
3020
3021   ps_open_global,
3022   ps_close_global,
3023   ps_font_sizes,
3024
3025   ps_preopen_driver,
3026   ps_option,
3027   ps_postopen_driver,
3028   ps_close_driver,
3029
3030   ps_open_page,
3031   ps_close_page,
3032
3033   ps_submit,
3034
3035   ps_line_horz,
3036   ps_line_vert,
3037   ps_line_intersection,
3038
3039   ps_box,
3040   ps_polyline_begin,
3041   ps_polyline_point,
3042   ps_polyline_end,
3043
3044   ps_text_set_font_by_name,
3045   ps_text_set_font_by_position,
3046   ps_text_set_font_family,
3047   ps_text_get_font_name,
3048   ps_text_get_font_family,
3049   ps_text_set_size,
3050   ps_text_get_size,
3051   ps_text_metrics,
3052   ps_text_draw,
3053
3054   ps_chart_initialise,
3055   ps_chart_finalise
3056
3057 };
3058
3059 #endif /* NO_POSTSCRIPT */