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