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