Eliminate temp_case, and a few other cleanups.
[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 <assert.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           }
665         if (setting)
666           x->output_options |= mask;
667         else
668           x->output_options &= ~mask;
669       }
670       break;
671     case pos_int_arg:
672       {
673         char *tail;
674         int arg;
675
676         errno = 0;
677         arg = strtol (value, &tail, 0);
678         if (arg < 1 || errno == ERANGE || *tail)
679           {
680             msg (SE, _("Positive integer required as value for `%s'."), key);
681             break;
682           }
683         if ((subcat == 4 || subcat == 5) && arg < 1000)
684           {
685             msg (SE, _("Default font size must be at least 1 point (value "
686                  "of 1000 for key `%s')."), key);
687             break;
688           }
689         switch (subcat)
690           {
691           case 0:
692             x->left_margin = arg;
693             break;
694           case 1:
695             x->right_margin = arg;
696             break;
697           case 2:
698             x->top_margin = arg;
699             break;
700           case 3:
701             x->bottom_margin = arg;
702             break;
703           case 4:
704             x->font_size = arg;
705             break;
706           default:
707             assert (0);
708           }
709       }
710       break;
711     case dimension_arg:
712       {
713         int dimension = outp_evaluate_dimension (value, NULL);
714
715         if (dimension <= 0)
716           {
717             msg (SE, _("Value for `%s' must be a dimension of positive "
718                  "length (i.e., `1in')."), key);
719             break;
720           }
721         switch (subcat)
722           {
723           case 2:
724             x->line_width = dimension;
725             break;
726           case 3:
727             x->line_gutter = dimension;
728             break;
729           case 4:
730             x->line_width = dimension;
731             break;
732           case 5:
733             x->line_width_thick = dimension;
734             break;
735           default:
736             assert (0);
737           }
738       }
739       break;
740     case string_arg:
741       {
742         char **dest;
743         switch (subcat)
744           {
745           case 0:
746             dest = &x->font_dir;
747             break;
748           case 1:
749             dest = &x->prologue_fn;
750             break;
751           case 2:
752             dest = &x->desc_fn;
753             break;
754           case 3:
755             dest = &x->encoding_fn;
756             break;
757           case 5:
758             dest = &x->prop_family;
759             break;
760           case 6:
761             dest = &x->fixed_family;
762             break;
763           default:
764             assert (0);
765           }
766         if (*dest)
767           free (*dest);
768         *dest = xstrdup (value);
769       }
770       break;
771     case nonneg_int_arg:
772       {
773         char *tail;
774         int arg;
775
776         errno = 0;
777         arg = strtol (value, &tail, 0);
778         if (arg < 0 || errno == ERANGE || *tail)
779           {
780             msg (SE, _("Nonnegative integer required as value for `%s'."), key);
781             break;
782           }
783         switch (subcat)
784           {
785           case 0:
786             x->text_opt = arg;
787             break;
788           case 1:
789             x->line_opt = arg;
790             break;
791           case 2:
792             x->max_fonts = arg;
793             break;
794           default:
795             assert (0);
796           }
797       }
798       break;
799     default:
800       assert (0);
801     }
802 }
803
804 /* Looks for a PostScript font file or config file in all the
805    appropriate places.  Returns the filename on success, NULL on
806    failure. */
807 /* PORTME: Filename operations. */
808 static char *
809 find_ps_file (struct outp_driver *this, const char *name)
810 {
811   struct ps_driver_ext *x = this->ext;
812   char *cp;
813
814   /* x->font_dir + name: "devps/ps-encodings". */
815   char *basename;
816
817   /* Usually equal to groff_font_path. */
818   char *pathname;
819
820   /* Final filename. */
821   char *fn;
822
823   /* Make basename. */
824   basename = local_alloc (strlen (x->font_dir) + 1 + strlen (name) + 1);
825   cp = stpcpy (basename, x->font_dir);
826   *cp++ = DIR_SEPARATOR;
827   strcpy (cp, name);
828
829   /* Decide on search path. */
830   {
831     const char *pre_pathname;
832     
833     pre_pathname = getenv ("STAT_GROFF_FONT_PATH");
834     if (pre_pathname == NULL)
835       pre_pathname = getenv ("GROFF_FONT_PATH");
836     if (pre_pathname == NULL)
837       pre_pathname = groff_font_path;
838     pathname = fn_tilde_expand (pre_pathname);
839   }
840
841   /* Search all possible places for the file. */
842   fn = fn_search_path (basename, pathname, NULL);
843   if (fn == NULL)
844     fn = fn_search_path (basename, config_path, NULL);
845   if (fn == NULL)
846     fn = fn_search_path (name, pathname, NULL);
847   if (fn == NULL)
848     fn = fn_search_path (name, config_path, NULL);
849   free (pathname);
850   local_free (basename);
851
852   return fn;
853 }
854 \f
855 /* Encodings. */
856
857 /* Hash table comparison function for ps_encoding's. */
858 static int
859 compare_ps_encoding (const void *pa, const void *pb, void *foo UNUSED)
860 {
861   const struct ps_encoding *a = pa;
862   const struct ps_encoding *b = pb;
863
864   return strcmp (a->filename, b->filename);
865 }
866
867 /* Hash table hash function for ps_encoding's. */
868 static unsigned
869 hash_ps_encoding (const void *pa, void *foo UNUSED)
870 {
871   const struct ps_encoding *a = pa;
872
873   return hsh_hash_string (a->filename);
874 }
875
876 /* Hash table free function for ps_encoding's. */
877 static void
878 free_ps_encoding (void *pa, void *foo UNUSED)
879 {
880   struct ps_encoding *a = pa;
881
882   free (a->filename);
883   free (a);
884 }
885
886 /* Iterates through the list of encodings used for this driver
887    instance, reads each of them from disk, and writes them as
888    PostScript code to the output file. */
889 static void
890 output_encodings (struct outp_driver *this)
891 {
892   struct ps_driver_ext *x = this->ext;
893
894   struct hsh_iterator iter;
895   struct ps_encoding *pe;
896
897   struct string line, buf;
898
899   ds_init (NULL, &line, 128);
900   ds_init (NULL, &buf, 128);
901   for (pe = hsh_first (x->encodings, &iter); pe != NULL;
902        pe = hsh_next (x->encodings, &iter)) 
903     {
904       FILE *f;
905
906       msg (VM (1), _("%s: %s: Opening PostScript font encoding..."),
907            this->name, pe->filename);
908       
909       f = fopen (pe->filename, "r");
910       if (!f)
911         {
912           msg (IE, _("PostScript driver: Cannot open encoding file `%s': %s.  "
913                "Substituting ISOLatin1Encoding for missing encoding."),
914                pe->filename, strerror (errno));
915           fprintf (x->file.file, "/E%x ISOLatin1Encoding def%s",
916                    pe->index, x->eol);
917         }
918       else
919         {
920           struct file_locator where;
921           
922           const char *tab[256];
923
924           char *pschar;
925           char *code;
926           int code_val;
927           char *fubar;
928
929           const char *notdef = ".notdef";
930
931           int i;
932
933           for (i = 0; i < 256; i++)
934             tab[i] = notdef;
935
936           where.filename = pe->filename;
937           where.line_number = 0;
938           err_push_file_locator (&where);
939
940           while (ds_get_config_line (f, &buf, &where))
941             {
942               char *sp; 
943
944               if (buf.length == 0) 
945                 continue;
946
947               pschar = strtok_r (ds_value (&buf), " \t\r\n", &sp);
948               code = strtok_r (NULL, " \t\r\n", &sp);
949               if (*pschar == 0 || *code == 0)
950                 continue;
951               code_val = strtol (code, &fubar, 0);
952               if (*fubar)
953                 {
954                   msg (IS, _("PostScript driver: Invalid numeric format."));
955                   continue;
956                 }
957               if (code_val < 0 || code_val > 255)
958                 {
959                   msg (IS, _("PostScript driver: Codes must be between 0 "
960                              "and 255.  (%d is not allowed.)"), code_val);
961                   break;
962                 }
963               tab[code_val] = local_alloc (strlen (pschar) + 1);
964               strcpy ((char *) (tab[code_val]), pschar);
965             }
966           err_pop_file_locator (&where);
967
968           ds_clear (&line);
969           ds_printf (&line, "/E%x[", pe->index);
970           for (i = 0; i < 257; i++)
971             {
972               char temp[288];
973
974               if (i < 256)
975                 {
976                   quote_ps_name (temp, tab[i]);
977                   if (tab[i] != notdef)
978                     local_free (tab[i]);
979                 }
980               else
981                 strcpy (temp, "]def");
982               
983               if (ds_length (&line) + strlen (temp) > 70)
984                 {
985                   ds_concat (&line, x->eol);
986                   fputs (ds_value (&line), x->file.file);
987                   ds_clear (&line);
988                 }
989               ds_concat (&line, temp);
990             }
991           ds_concat (&line, x->eol);
992           fputs (ds_value (&line), x->file.file);
993
994           if (fclose (f) == EOF)
995             msg (MW, _("PostScript driver: Error closing encoding file `%s'."),
996                  pe->filename);
997
998           msg (VM (2), _("%s: PostScript font encoding read successfully."),
999                this->name);
1000         }
1001     }
1002   ds_destroy (&line);
1003   ds_destroy (&buf);
1004 }
1005
1006 /* Finds the ps_encoding in THIS that corresponds to the file with
1007    name NORM_FILENAME, which must have previously been normalized with
1008    normalize_filename(). */
1009 static struct ps_encoding *
1010 get_encoding (struct outp_driver *this, const char *norm_filename)
1011 {
1012   struct ps_driver_ext *x = this->ext;
1013   struct ps_encoding *pe;
1014
1015   pe = (struct ps_encoding *) hsh_find (x->encodings, (void *) &norm_filename);
1016   return pe;
1017 }
1018
1019 /* Searches the filesystem for an encoding file with name FILENAME;
1020    returns its malloc'd, normalized name if found, otherwise NULL. */
1021 static char *
1022 find_encoding_file (struct outp_driver *this, char *filename)
1023 {
1024   char *cp, *temp;
1025
1026   if (filename == NULL)
1027     return NULL;
1028   while (isspace ((unsigned char) *filename))
1029     filename++;
1030   for (cp = filename; *cp && !isspace ((unsigned char) *cp); cp++)
1031     ;
1032   if (cp == filename)
1033     return NULL;
1034   *cp = 0;
1035
1036   temp = find_ps_file (this, filename);
1037   if (temp == NULL)
1038     return NULL;
1039
1040   filename = fn_normalize (temp);
1041   assert (filename != NULL);
1042   free (temp);
1043
1044   return filename;
1045 }
1046
1047 /* Adds the encoding represented by the not-necessarily-normalized
1048    file FILENAME to the list of encodings, if it exists and is not
1049    already in the list. */
1050 static void
1051 add_encoding (struct outp_driver *this, char *filename)
1052 {
1053   struct ps_driver_ext *x = this->ext;
1054   struct ps_encoding **pe;
1055
1056   filename = find_encoding_file (this, filename);
1057   if (!filename)
1058     return;
1059
1060   pe = (struct ps_encoding **) hsh_probe (x->encodings, &filename);
1061   if (*pe)
1062     {
1063       free (filename);
1064       return;
1065     }
1066   *pe = xmalloc (sizeof **pe);
1067   (*pe)->filename = filename;
1068   (*pe)->index = x->next_encoding++;
1069 }
1070
1071 /* Finds the file on disk that contains the list of encodings to
1072    include in the output file, then adds those encodings to the list
1073    of encodings. */
1074 static void
1075 read_ps_encodings (struct outp_driver *this)
1076 {
1077   struct ps_driver_ext *x = this->ext;
1078
1079   /* Encodings file. */
1080   char *encoding_fn;            /* `ps-encodings' filename. */
1081   FILE *f;
1082
1083   struct string line;
1084   struct file_locator where;
1085
1086   /* It's okay if there's no list of encodings; not everyone cares. */
1087   encoding_fn = find_ps_file (this, x->encoding_fn);
1088   if (encoding_fn == NULL)
1089     return;
1090   free (encoding_fn);
1091
1092   msg (VM (1), _("%s: %s: Opening PostScript encoding list file."),
1093        this->name, encoding_fn);
1094   f = fopen (encoding_fn, "r");
1095   if (!f)
1096     {
1097       msg (IE, _("Opening %s: %s."), encoding_fn, strerror (errno));
1098       return;
1099     }
1100
1101   where.filename = encoding_fn;
1102   where.line_number = 0;
1103   err_push_file_locator (&where);
1104
1105   ds_init (NULL, &line, 128);
1106     
1107   for (;;)
1108     {
1109       if (!ds_get_config_line (f, &line, &where))
1110         {
1111           if (ferror (f))
1112             msg (ME, _("Reading %s: %s."), encoding_fn, strerror (errno));
1113           break;
1114         }
1115
1116       add_encoding (this, line.string);
1117     }
1118
1119   ds_destroy (&line);
1120   err_pop_file_locator (&where);
1121   
1122   if (-1 == fclose (f))
1123     msg (MW, _("Closing %s: %s."), encoding_fn, strerror (errno));
1124
1125   msg (VM (2), _("%s: PostScript encoding list file read successfully."), this->name);
1126 }
1127
1128 /* Creates a default encoding for driver D that can be substituted for
1129    an unavailable encoding. */
1130 struct ps_encoding *
1131 default_encoding (struct outp_driver *d)
1132 {
1133   struct ps_driver_ext *x = d->ext;
1134   static struct ps_encoding *enc;
1135
1136   if (!enc)
1137     {
1138       enc = xmalloc (sizeof *enc);
1139       enc->filename = xstrdup (_("<<default encoding>>"));
1140       enc->index = x->next_encoding++;
1141     }
1142   return enc;
1143 }
1144 \f
1145 /* Basic file operations. */
1146
1147 /* Variables for the prologue. */
1148 struct ps_variable
1149   {
1150     const char *key;
1151     const char *value;
1152   };
1153
1154 static struct ps_variable *ps_var_tab;
1155
1156 /* Searches ps_var_tab for a ps_variable with key KEY, and returns the
1157    associated value. */
1158 static const char *
1159 ps_get_var (const char *key)
1160 {
1161   struct ps_variable *v;
1162
1163   for (v = ps_var_tab; v->key; v++)
1164     if (!strcmp (key, v->key))
1165       return v->value;
1166   return NULL;
1167 }
1168
1169 /* Writes the PostScript prologue to file F. */
1170 static int
1171 postopen (struct file_ext *f)
1172 {
1173   static struct ps_variable dict[] =
1174   {
1175     {"bounding-box", 0},
1176     {"creator", 0},
1177     {"date", 0},
1178     {"data", 0},
1179     {"orientation", 0},
1180     {"user", 0},
1181     {"host", 0},
1182     {"prop-font", 0},
1183     {"fixed-font", 0},
1184     {"scale-factor", 0},
1185     {"paper-width", 0},
1186     {"paper-length", 0},
1187     {"left-margin", 0},
1188     {"top-margin", 0},
1189     {"line-width", 0},
1190     {"line-width-thick", 0},
1191     {"title", 0},
1192     {"source-file", 0},
1193     {0, 0},
1194   };
1195   char boundbox[INT_DIGITS * 4 + 4];
1196 #if HAVE_UNISTD_H
1197   char host[128];
1198 #endif
1199   char scaling[INT_DIGITS + 5];
1200   time_t curtime;
1201   struct tm *loctime;
1202   char *p, *cp;
1203   char paper_width[INT_DIGITS + 1];
1204   char paper_length[INT_DIGITS + 1];
1205   char left_margin[INT_DIGITS + 1];
1206   char top_margin[INT_DIGITS + 1];
1207   char line_width[INT_DIGITS + 1];
1208   char line_width_thick[INT_DIGITS + 1];
1209
1210   struct outp_driver *this = f->param;
1211   struct ps_driver_ext *x = this->ext;
1212
1213   char *prologue_fn = find_ps_file (this, x->prologue_fn);
1214   FILE *prologue_file;
1215
1216   char *buf = NULL;
1217   size_t buf_size = 0;
1218
1219   x->loaded = hsh_create (31, compare_font_entry, hash_font_entry,
1220                           free_font_entry, NULL);
1221   
1222   {
1223     char *font_name = local_alloc (2 + max (strlen (x->prop_family),
1224                                             strlen (x->fixed_family)));
1225     
1226     strcpy (stpcpy (font_name, x->prop_family), "R");
1227     x->prop = load_font (this, font_name);
1228
1229     strcpy (stpcpy (font_name, x->fixed_family), "R");
1230     x->fixed = load_font (this, font_name);
1231
1232     local_free(font_name);
1233   }
1234
1235   x->current = x->prop;
1236   x->family = xstrdup (x->prop_family);
1237   x->size = x->font_size;
1238   
1239   {
1240     int *h = this->horiz_line_width, *v = this->vert_line_width;
1241     
1242     this->cp_x = this->cp_y = 0;
1243     this->font_height = x->font_size;
1244     {
1245       struct char_metrics *metric;
1246
1247       metric = font_get_char_metrics (x->prop->font, '0');
1248       this->prop_em_width = ((metric
1249                               ? metric->width : x->prop->font->space_width)
1250                              * x->font_size / 1000);
1251
1252       metric = font_get_char_metrics (x->fixed->font, '0');
1253       this->fixed_width = ((metric
1254                             ? metric->width : x->fixed->font->space_width)
1255                            * x->font_size / 1000);
1256     }
1257         
1258     h[0] = v[0] = 0;
1259     h[1] = v[1] = 2 * x->line_gutter + x->line_width;
1260     if (x->output_options & OPO_DOUBLE_LINE)
1261       h[2] = v[2] = 2 * x->line_gutter + 2 * x->line_width + x->line_space;
1262     else
1263       h[2] = v[2] = 2 * x->line_gutter + x->line_width_thick;
1264     h[3] = v[3] = 2 * x->line_gutter + x->line_width;
1265     
1266     {
1267       int i;
1268       
1269       for (i = 0; i < (1 << OUTP_L_COUNT); i++)
1270         {
1271           int bit;
1272
1273           /* Maximum width of any line type so far. */
1274           int max = 0;
1275
1276           for (bit = 0; bit < OUTP_L_COUNT; bit++)
1277             if ((i & (1 << bit)) && h[bit] > max)
1278               max = h[bit];
1279           this->horiz_line_spacing[i] = this->vert_line_spacing[i] = max;
1280         }
1281     }
1282   }
1283
1284   if (x->output_options & OPO_AUTO_ENCODE)
1285     {
1286       /* It's okay if this is done more than once since add_encoding()
1287          is idempotent over identical encodings. */
1288       add_encoding (this, x->prop->font->encoding);
1289       add_encoding (this, x->fixed->font->encoding);
1290     }
1291
1292   x->file_page_number = 0;
1293
1294   errno = 0;
1295   if (prologue_fn == NULL)
1296     {
1297       msg (IE, _("Cannot find PostScript prologue.  The use of `-vv' "
1298                  "on the command line is suggested as a debugging aid."));
1299       return 0;
1300     }
1301
1302   msg (VM (1), _("%s: %s: Opening PostScript prologue..."),
1303        this->name, prologue_fn);
1304   prologue_file = fopen (prologue_fn, "rb");
1305   if (prologue_file == NULL)
1306     {
1307       fclose (prologue_file);
1308       free (prologue_fn);
1309       msg (IE, "%s: %s", prologue_fn, strerror (errno));
1310       goto error;
1311     }
1312
1313   sprintf (boundbox, "0 0 %d %d",
1314            x->w / (PSUS / 72) + (x->w % (PSUS / 72) > 0),
1315            x->l / (PSUS / 72) + (x->l % (PSUS / 72) > 0));
1316   dict[0].value = boundbox;
1317
1318   dict[1].value = (char *) version;
1319
1320   curtime = time (NULL);
1321   loctime = localtime (&curtime);
1322   dict[2].value = asctime (loctime);
1323   cp = strchr (dict[2].value, '\n');
1324   if (cp)
1325     *cp = 0;
1326
1327   switch (x->data)
1328     {
1329     case ODA_CLEAN7BIT:
1330       dict[3].value = "Clean7Bit";
1331       break;
1332     case ODA_CLEAN8BIT:
1333       dict[3].value = "Clean8Bit";
1334       break;
1335     case ODA_BINARY:
1336       dict[3].value = "Binary";
1337       break;
1338     default:
1339       assert (0);
1340     }
1341
1342   if (x->orientation == OTN_PORTRAIT)
1343     dict[4].value = "Portrait";
1344   else
1345     dict[4].value = "Landscape";
1346
1347   /* PORTME: Determine username, net address. */
1348 #if HAVE_UNISTD_H
1349   dict[5].value = getenv ("LOGNAME");
1350   if (!dict[5].value)
1351     dict[5].value = getlogin ();
1352   if (!dict[5].value)
1353     dict[5].value = _("nobody");
1354
1355   if (gethostname (host, 128) == -1)
1356     {
1357       if (errno == ENAMETOOLONG)
1358         host[127] = 0;
1359       else
1360         strcpy (host, _("nowhere"));
1361     }
1362   dict[6].value = host;
1363 #else /* !HAVE_UNISTD_H */
1364   dict[5].value = _("nobody");
1365   dict[6].value = _("nowhere");
1366 #endif /* !HAVE_UNISTD_H */
1367
1368   cp = stpcpy (p = local_alloc (288), "font ");
1369   quote_ps_string (cp, x->prop->font->internal_name);
1370   dict[7].value = p;
1371
1372   cp = stpcpy (p = local_alloc (288), "font ");
1373   quote_ps_string (cp, x->fixed->font->internal_name);
1374   dict[8].value = p;
1375
1376   sprintf (scaling, "%.3f", PSUS / 72.0);
1377   dict[9].value = scaling;
1378
1379   sprintf (paper_width, "%g", x->w / (PSUS / 72.0));
1380   dict[10].value = paper_width;
1381
1382   sprintf (paper_length, "%g", x->l / (PSUS / 72.0));
1383   dict[11].value = paper_length;
1384
1385   sprintf (left_margin, "%d", x->left_margin);
1386   dict[12].value = left_margin;
1387
1388   sprintf (top_margin, "%d", x->top_margin);
1389   dict[13].value = top_margin;
1390
1391   sprintf (line_width, "%d", x->line_width);
1392   dict[14].value = line_width;
1393
1394   sprintf (line_width, "%d", x->line_width_thick);
1395   dict[15].value = line_width_thick;
1396   
1397   getl_location (&dict[17].value, NULL);
1398   if (dict[17].value == NULL)
1399     dict[17].value = "<stdin>";
1400
1401   if (!outp_title)
1402     {
1403       dict[16].value = cp = local_alloc (strlen (dict[17].value) + 30);
1404       sprintf (cp, "PSPP (%s)", dict[17].value);
1405     }
1406   else
1407     {
1408       dict[16].value = local_alloc (strlen (outp_title) + 1);
1409       strcpy ((char *) (dict[16].value), outp_title);
1410     }
1411   
1412   ps_var_tab = dict;
1413   while (-1 != getline (&buf, &buf_size, prologue_file))
1414     {
1415       char *cp;
1416       char *buf2;
1417       int len;
1418
1419       cp = strstr (buf, "!eps");
1420       if (cp)
1421         {
1422           if (this->class->magic == MAGIC_PS)
1423             continue;
1424           else
1425             *cp = '\0';
1426         }
1427       else
1428         {
1429           cp = strstr (buf, "!ps");
1430           if (cp)
1431             {
1432               if (this->class->magic == MAGIC_EPSF)
1433                 continue;
1434               else
1435                 *cp = '\0';
1436             } else {
1437               if (strstr (buf, "!!!"))
1438                 continue;
1439             }
1440         }
1441
1442       if (!strncmp (buf, "!encodings", 10))
1443         output_encodings (this);
1444       else
1445         {
1446           char *beg;
1447           beg = buf2 = fn_interp_vars (buf, ps_get_var);
1448           len = strlen (buf2);
1449           while (isspace (*beg))
1450             beg++, len--;
1451           if (beg[len - 1] == '\n')
1452             len--;
1453           if (beg[len - 1] == '\r')
1454             len--;
1455           fwrite (beg, len, 1, f->file);
1456           fputs (x->eol, f->file);
1457           free (buf2);
1458         }
1459     }
1460   if (ferror (f->file))
1461     msg (IE, _("Reading `%s': %s."), prologue_fn, strerror (errno));
1462   fclose (prologue_file);
1463
1464   free (prologue_fn);
1465   free (buf);
1466
1467   local_free (dict[7].value);
1468   local_free (dict[8].value);
1469   local_free (dict[16].value);
1470
1471   if (ferror (f->file))
1472     goto error;
1473
1474   msg (VM (2), _("%s: PostScript prologue read successfully."), this->name);
1475   return 1;
1476
1477 error:
1478   msg (VM (1), _("%s: Error reading PostScript prologue."), this->name);
1479   return 0;
1480 }
1481
1482 /* Writes the string STRING to buffer DEST (of at least 288
1483    characters) as a PostScript name object.  Returns a pointer
1484    to the null terminator of the resultant string. */
1485 static char *
1486 quote_ps_name (char *dest, const char *string)
1487 {
1488   const char *sp;
1489
1490   for (sp = string; *sp; sp++)
1491     switch (*(unsigned char *) sp)
1492       {
1493       case 'a':
1494       case 'f':
1495       case 'k':
1496       case 'p':
1497       case 'u':
1498       case 'b':
1499       case 'g':
1500       case 'l':
1501       case 'q':
1502       case 'v':
1503       case 'c':
1504       case 'h':
1505       case 'm':
1506       case 'r':
1507       case 'w':
1508       case 'd':
1509       case 'i':
1510       case 'n':
1511       case 's':
1512       case 'x':
1513       case 'e':
1514       case 'j':
1515       case 'o':
1516       case 't':
1517       case 'y':
1518       case 'z':
1519       case 'A':
1520       case 'F':
1521       case 'K':
1522       case 'P':
1523       case 'U':
1524       case 'B':
1525       case 'G':
1526       case 'L':
1527       case 'Q':
1528       case 'V':
1529       case 'C':
1530       case 'H':
1531       case 'M':
1532       case 'R':
1533       case 'W':
1534       case 'D':
1535       case 'I':
1536       case 'N':
1537       case 'S':
1538       case 'X':
1539       case 'E':
1540       case 'J':
1541       case 'O':
1542       case 'T':
1543       case 'Y':
1544       case 'Z':
1545       case '@':
1546       case '^':
1547       case '_':
1548       case '|':
1549       case '!':
1550       case '$':
1551       case '&':
1552       case ':':
1553       case ';':
1554       case '.':
1555       case ',':
1556       case '-':
1557       case '+':
1558         break;
1559       default:
1560         {
1561           char *dp = dest;
1562
1563           *dp++ = '<';
1564           for (sp = string; *sp && dp < &dest[256]; sp++)
1565             {
1566               sprintf (dp, "%02x", *(unsigned char *) sp);
1567               dp += 2;
1568             }
1569           return stpcpy (dp, ">cvn");
1570         }
1571       }
1572   dest[0] = '/';
1573   return stpcpy (&dest[1], string);
1574 }
1575
1576 /* Adds the string STRING to buffer DEST as a PostScript quoted
1577    string; returns a pointer to the null terminator added.  Will not
1578    add more than 235 characters. */
1579 static char *
1580 quote_ps_string (char *dest, const char *string)
1581 {
1582   const char *sp = string;
1583   char *dp = dest;
1584
1585   *dp++ = '(';
1586   for (; *sp && dp < &dest[235]; sp++)
1587     if (*sp == '(')
1588       dp = stpcpy (dp, "\\(");
1589     else if (*sp == ')')
1590       dp = stpcpy (dp, "\\)");
1591     else if (*sp < 32 || *((unsigned char *) sp) > 127)
1592       dp = spprintf (dp, "\\%3o", *sp);
1593     else
1594       *dp++ = *sp;
1595   return stpcpy (dp, ")");
1596 }
1597
1598 /* Writes the PostScript epilogue to file F. */
1599 static int
1600 preclose (struct file_ext *f)
1601 {
1602   struct outp_driver *this = f->param;
1603   struct ps_driver_ext *x = this->ext;
1604   struct hsh_iterator iter;
1605   struct font_entry *fe;
1606
1607   fprintf (f->file,
1608            ("%%%%Trailer%s"
1609             "%%%%Pages: %d%s"
1610             "%%%%DocumentNeededResources:%s"),
1611            x->eol, x->file_page_number, x->eol, x->eol);
1612
1613   for (fe = hsh_first (x->loaded, &iter); fe != NULL;
1614        fe = hsh_next (x->loaded, &iter)) 
1615     {
1616       char buf[256], *cp;
1617
1618       cp = stpcpy (buf, "%%+ font ");
1619       cp = quote_ps_string (cp, fe->font->internal_name);
1620       strcpy (cp, x->eol);
1621       fputs (buf, f->file);
1622     }
1623
1624   hsh_destroy (x->loaded);
1625   x->loaded = NULL;
1626   hsh_destroy (x->combos);
1627   x->combos = NULL;
1628   x->last_font = NULL;
1629   x->next_combo = 0;
1630
1631   fprintf (f->file, "%%EOF%s", x->eol);
1632   if (ferror (f->file))
1633     return 0;
1634   return 1;
1635 }
1636
1637 static int
1638 ps_open_page (struct outp_driver *this)
1639 {
1640   struct ps_driver_ext *x = this->ext;
1641
1642   assert (this->driver_open && !this->page_open);
1643       
1644   x->page_number++;
1645   if (!fn_open_ext (&x->file))
1646     {
1647       if (errno)
1648         msg (ME, _("PostScript output driver: %s: %s"), x->file.filename,
1649              strerror (errno));
1650       return 0;
1651     }
1652   x->file_page_number++;
1653
1654   hsh_destroy (x->combos);
1655   x->combos = hsh_create (31, compare_ps_combo, hash_ps_combo,
1656                           free_ps_combo, NULL);
1657   x->last_font = NULL;
1658   x->next_combo = 0;
1659
1660   fprintf (x->file.file,
1661            "%%%%Page: %d %d%s"
1662            "%%%%BeginPageSetup%s"
1663            "/pg save def 0.001 dup scale%s",
1664            x->page_number, x->file_page_number, x->eol,
1665            x->eol,
1666            x->eol);
1667
1668   if (x->orientation == OTN_LANDSCAPE)
1669     fprintf (x->file.file,
1670              "%d 0 translate 90 rotate%s",
1671              x->w, x->eol);
1672
1673   if (x->bottom_margin != 0 || x->left_margin != 0)
1674     fprintf (x->file.file,
1675              "%d %d translate%s",
1676              x->left_margin, x->bottom_margin, x->eol);
1677
1678   fprintf (x->file.file,
1679            "/LW %d def/TW %d def %d setlinewidth%s"
1680            "%%%%EndPageSetup%s",
1681            x->line_width, x->line_width_thick, x->line_width, x->eol,
1682            x->eol);
1683
1684   if (!ferror (x->file.file))
1685     {
1686       this->page_open = 1;
1687       if (x->output_options & OPO_HEADERS)
1688         draw_headers (this);
1689     }
1690
1691   return !ferror (x->file.file);
1692 }
1693
1694 static int
1695 ps_close_page (struct outp_driver *this)
1696 {
1697   struct ps_driver_ext *x = this->ext;
1698
1699   assert (this->driver_open && this->page_open);
1700   
1701   if (x->line_opt)
1702     dump_lines (this);
1703
1704   fprintf (x->file.file,
1705            "%%PageTrailer%s"
1706            "EP%s",
1707            x->eol, x->eol);
1708
1709   this->page_open = 0;
1710   return !ferror (x->file.file);
1711 }
1712 \f
1713 /* Lines. */
1714
1715 /* qsort() comparison function for int tuples. */
1716 static int
1717 int_2_compare (const void *a_, const void *b_)
1718 {
1719   const int *a = a_;
1720   const int *b = b_;
1721
1722   return *a < *b ? -1 : *a > *b;
1723 }
1724
1725 /* Hash table comparison function for cached lines. */
1726 static int
1727 compare_line (const void *a_, const void *b_, void *foo UNUSED)
1728 {
1729   const struct line_form *a = a_;
1730   const struct line_form *b = b_;
1731
1732   return a->ind < b->ind ? -1 : a->ind > b->ind;
1733 }
1734
1735 /* Hash table hash function for cached lines. */
1736 static unsigned
1737 hash_line (const void *pa, void *foo UNUSED)
1738 {
1739   const struct line_form *a = pa;
1740
1741   return a->ind;
1742 }
1743
1744 /* Hash table free function for cached lines. */
1745 static void
1746 free_line (void *pa, void *foo UNUSED)
1747 {
1748   free (pa);
1749 }
1750
1751 /* Writes PostScript code to draw a line from (x1,y1) to (x2,y2) to
1752    the output file. */
1753 #define dump_line(x1, y1, x2, y2)                       \
1754         fprintf (ext->file.file, "%d %d %d %d L%s",     \
1755                  x1, YT (y1), x2, YT (y2), ext->eol)
1756
1757 /* Write PostScript code to draw a thick line from (x1,y1) to (x2,y2)
1758    to the output file. */
1759 #define dump_thick_line(x1, y1, x2, y2)                 \
1760         fprintf (ext->file.file, "%d %d %d %d TL%s",    \
1761                  x1, YT (y1), x2, YT (y2), ext->eol)
1762
1763 /* Writes a line of type TYPE to THIS driver's output file.  The line
1764    (or its center, in the case of double lines) has its independent
1765    axis coordinate at IND; it extends from DEP1 to DEP2 on the
1766    dependent axis. */
1767 static void
1768 dump_fancy_line (struct outp_driver *this, int type, int ind, int dep1, int dep2)
1769 {
1770   struct ps_driver_ext *ext = this->ext;
1771   int ofs = ext->line_space / 2 + ext->line_width / 2;
1772
1773   switch (type)
1774     {
1775     case horz:
1776       dump_line (dep1, ind, dep2, ind);
1777       break;
1778     case dbl_horz:
1779       if (ext->output_options & OPO_DOUBLE_LINE)
1780         {
1781           dump_line (dep1, ind - ofs, dep2, ind - ofs);
1782           dump_line (dep1, ind + ofs, dep2, ind + ofs);
1783         }
1784       else
1785         dump_thick_line (dep1, ind, dep2, ind);
1786       break;
1787     case spl_horz:
1788       assert (0);
1789     case vert:
1790       dump_line (ind, dep1, ind, dep2);
1791       break;
1792     case dbl_vert:
1793       if (ext->output_options & OPO_DOUBLE_LINE)
1794         {
1795           dump_line (ind - ofs, dep1, ind - ofs, dep2);
1796           dump_line (ind + ofs, dep1, ind + ofs, dep2);
1797         }
1798       else
1799         dump_thick_line (ind, dep1, ind, dep2);
1800       break;
1801     case spl_vert:
1802       assert (0);
1803     default:
1804       assert (0);
1805     }
1806 }
1807
1808 #undef dump_line
1809
1810 /* Writes all the cached lines to the output file, then clears the
1811    cache. */
1812 static void
1813 dump_lines (struct outp_driver *this)
1814 {
1815   struct ps_driver_ext *x = this->ext;
1816
1817   struct hsh_iterator iter;
1818   int type;
1819
1820   for (type = 0; type < n_line_types; type++)
1821     {
1822       struct line_form *line;
1823
1824       if (x->lines[type] == NULL) 
1825         continue;
1826
1827       for (line = hsh_first (x->lines[type], &iter); line != NULL;
1828            line = hsh_next (x->lines[type], &iter)) 
1829         {
1830           int i;
1831           int lo = INT_MIN, hi;
1832
1833           qsort (line->dep, line->ndep, sizeof *line->dep, int_2_compare);
1834           lo = line->dep[0][0];
1835           hi = line->dep[0][1];
1836           for (i = 1; i < line->ndep; i++)
1837             if (line->dep[i][0] <= hi + 1)
1838               {
1839                 int min_hi = line->dep[i][1];
1840                 if (min_hi > hi)
1841                   hi = min_hi;
1842               }
1843             else
1844               {
1845                 dump_fancy_line (this, type, line->ind, lo, hi);
1846                 lo = line->dep[i][0];
1847                 hi = line->dep[i][1];
1848               }
1849           dump_fancy_line (this, type, line->ind, lo, hi);
1850         }
1851
1852       hsh_destroy (x->lines[type]);
1853       x->lines[type] = NULL;
1854     }
1855 }
1856
1857 /* (Same args as dump_fancy_line()).  Either dumps the line directly
1858    to the output file, or adds it to the cache, depending on the
1859    user-selected line optimization mode. */
1860 static void
1861 line (struct outp_driver *this, int type, int ind, int dep1, int dep2)
1862 {
1863   struct ps_driver_ext *ext = this->ext;
1864   struct line_form **f;
1865
1866   assert (dep2 >= dep1);
1867   if (ext->line_opt == 0)
1868     {
1869       dump_fancy_line (this, type, ind, dep1, dep2);
1870       return;
1871     }
1872
1873   if (ext->lines[type] == NULL)
1874     ext->lines[type] = hsh_create (31, compare_line, hash_line,
1875                                    free_line, NULL);
1876   f = (struct line_form **) hsh_probe (ext->lines[type], &ind);
1877   if (*f == NULL)
1878     {
1879       *f = xmalloc (sizeof **f + sizeof (int[15][2]));
1880       (*f)->ind = ind;
1881       (*f)->mdep = 16;
1882       (*f)->ndep = 1;
1883       (*f)->dep[0][0] = dep1;
1884       (*f)->dep[0][1] = dep2;
1885       return;
1886     }
1887   if ((*f)->ndep >= (*f)->mdep)
1888     {
1889       (*f)->mdep += 16;
1890       *f = xrealloc (*f, (sizeof **f + sizeof (int[2]) * ((*f)->mdep - 1)));
1891     }
1892   (*f)->dep[(*f)->ndep][0] = dep1;
1893   (*f)->dep[(*f)->ndep][1] = dep2;
1894   (*f)->ndep++;
1895 }
1896
1897 static void
1898 ps_line_horz (struct outp_driver *this, const struct rect *r,
1899               const struct color *c UNUSED, int style)
1900 {
1901   /* Must match output.h:OUTP_L_*. */
1902   static const int types[OUTP_L_COUNT] =
1903   {-1, horz, dbl_horz, spl_horz};
1904
1905   int y = (r->y1 + r->y2) / 2;
1906
1907   assert (this->driver_open && this->page_open);
1908   assert (style >= 0 && style < OUTP_L_COUNT);
1909   style = types[style];
1910   if (style != -1)
1911     line (this, style, y, r->x1, r->x2);
1912 }
1913
1914 static void
1915 ps_line_vert (struct outp_driver *this, const struct rect *r,
1916               const struct color *c UNUSED, int style)
1917 {
1918   /* Must match output.h:OUTP_L_*. */
1919   static const int types[OUTP_L_COUNT] =
1920   {-1, vert, dbl_vert, spl_vert};
1921
1922   int x = (r->x1 + r->x2) / 2;
1923
1924   assert (this->driver_open && this->page_open);
1925   assert (style >= 0 && style < OUTP_L_COUNT);
1926   style = types[style];
1927   if (style != -1)
1928     line (this, style, x, r->y1, r->y2);
1929 }
1930
1931 #define L (style->l != OUTP_L_NONE)
1932 #define R (style->r != OUTP_L_NONE)
1933 #define T (style->t != OUTP_L_NONE)
1934 #define B (style->b != OUTP_L_NONE)
1935
1936 static void
1937 ps_line_intersection (struct outp_driver *this, const struct rect *r,
1938                       const struct color *c UNUSED,
1939                       const struct outp_styles *style)
1940 {
1941   struct ps_driver_ext *ext = this->ext;
1942
1943   int x = (r->x1 + r->x2) / 2;
1944   int y = (r->y1 + r->y2) / 2;
1945   int ofs = (ext->line_space + ext->line_width) / 2;
1946   int x1 = x - ofs, x2 = x + ofs;
1947   int y1 = y - ofs, y2 = y + ofs;
1948
1949   assert (this->driver_open && this->page_open);
1950   assert (!((style->l != style->r && style->l != OUTP_L_NONE
1951              && style->r != OUTP_L_NONE)
1952             || (style->t != style->b && style->t != OUTP_L_NONE
1953                 && style->b != OUTP_L_NONE)));
1954
1955   switch ((style->l | style->r) | ((style->t | style->b) << 8))
1956     {
1957     case (OUTP_L_SINGLE) | (OUTP_L_SINGLE << 8):
1958     case (OUTP_L_SINGLE) | (OUTP_L_NONE << 8):
1959     case (OUTP_L_NONE) | (OUTP_L_SINGLE << 8):
1960       if (L)
1961         line (this, horz, y, r->x1, x);
1962       if (R)
1963         line (this, horz, y, x, r->x2);
1964       if (T)
1965         line (this, vert, x, r->y1, y);
1966       if (B)
1967         line (this, vert, x, y, r->y2);
1968       break;
1969     case (OUTP_L_SINGLE) | (OUTP_L_DOUBLE << 8):
1970     case (OUTP_L_NONE) | (OUTP_L_DOUBLE << 8):
1971       if (L)
1972         line (this, horz, y, r->x1, x1);
1973       if (R)
1974         line (this, horz, y, x2, r->x2);
1975       if (T)
1976         line (this, dbl_vert, x, r->y1, y);
1977       if (B)
1978         line (this, dbl_vert, x, y, r->y2);
1979       if ((L && R) && !(T && B))
1980         line (this, horz, y, x1, x2);
1981       break;
1982     case (OUTP_L_DOUBLE) | (OUTP_L_SINGLE << 8):
1983     case (OUTP_L_DOUBLE) | (OUTP_L_NONE << 8):
1984       if (L)
1985         line (this, dbl_horz, y, r->x1, x);
1986       if (R)
1987         line (this, dbl_horz, y, x, r->x2);
1988       if (T)
1989         line (this, vert, x, r->y1, y);
1990       if (B)
1991         line (this, vert, x, y, r->y2);
1992       if ((T && B) && !(L && R))
1993         line (this, vert, x, y1, y2);
1994       break;
1995     case (OUTP_L_DOUBLE) | (OUTP_L_DOUBLE << 8):
1996       if (L)
1997         line (this, dbl_horz, y, r->x1, x);
1998       if (R)
1999         line (this, dbl_horz, y, x, r->x2);
2000       if (T)
2001         line (this, dbl_vert, x, r->y1, y);
2002       if (B)
2003         line (this, dbl_vert, x, y, r->y2);
2004       if (T && B && !L)
2005         line (this, vert, x1, y1, y2);
2006       if (T && B && !R)
2007         line (this, vert, x2, y1, y2);
2008       if (L && R && !T)
2009         line (this, horz, y1, x1, x2);
2010       if (L && R && !B)
2011         line (this, horz, y2, x1, x2);
2012       break;
2013     default:
2014       assert (0);
2015     }
2016 }
2017
2018 static void
2019 ps_box (struct outp_driver *this UNUSED, const struct rect *r UNUSED,
2020         const struct color *bord UNUSED, const struct color *fill UNUSED)
2021 {
2022   assert (this->driver_open && this->page_open);
2023 }
2024
2025 static void 
2026 ps_polyline_begin (struct outp_driver *this UNUSED,
2027                    const struct color *c UNUSED)
2028 {
2029   assert (this->driver_open && this->page_open);
2030 }
2031 static void 
2032 ps_polyline_point (struct outp_driver *this UNUSED, int x UNUSED, int y UNUSED)
2033 {
2034   assert (this->driver_open && this->page_open);
2035 }
2036 static void 
2037 ps_polyline_end (struct outp_driver *this UNUSED)
2038 {
2039   assert (this->driver_open && this->page_open);
2040 }
2041
2042 /* Returns the width of string S for THIS driver. */
2043 static int
2044 text_width (struct outp_driver *this, char *s)
2045 {
2046   struct outp_text text;
2047
2048   text.options = OUTP_T_JUST_LEFT;
2049   ls_init (&text.s, s, strlen (s));
2050   this->class->text_metrics (this, &text);
2051   return text.h;
2052 }
2053
2054 /* Write string S at location (X,Y) with width W for THIS driver. */
2055 static void
2056 out_text_plain (struct outp_driver *this, char *s, int x, int y, int w)
2057 {
2058   struct outp_text text;
2059
2060   text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT;
2061   ls_init (&text.s, s, strlen (s));
2062   text.h = w;
2063   text.v = this->font_height;
2064   text.x = x;
2065   text.y = y;
2066   this->class->text_draw (this, &text);
2067 }
2068
2069 /* Draw top of page headers for THIS driver. */
2070 static void
2071 draw_headers (struct outp_driver *this)
2072 {
2073   struct ps_driver_ext *ext = this->ext;
2074   
2075   struct font_entry *old_current = ext->current;
2076   char *old_family = xstrdup (ext->family); /* FIXME */
2077   int old_size = ext->size;
2078
2079   int fh = this->font_height;
2080   int y = -3 * fh;
2081
2082   fprintf (ext->file.file, "%d %d %d %d GB%s",
2083            0, YT (y), this->width, YT (y + 2 * fh + ext->line_gutter),
2084            ext->eol);
2085   this->class->text_set_font_family (this, "T");
2086
2087   y += ext->line_width + ext->line_gutter;
2088   
2089   {
2090     int rh_width;
2091     char buf[128];
2092
2093     sprintf (buf, _("%s - Page %d"), curdate, ext->page_number);
2094     rh_width = text_width (this, buf);
2095
2096     out_text_plain (this, buf, this->width - this->prop_em_width - rh_width,
2097                     y, rh_width);
2098
2099     if (outp_title && outp_subtitle)
2100       out_text_plain (this, outp_title, this->prop_em_width, y,
2101                       this->width - 3 * this->prop_em_width - rh_width);
2102
2103     y += fh;
2104   }
2105   
2106   {
2107     int rh_width;
2108     char buf[128];
2109     char *string = outp_subtitle ? outp_subtitle : outp_title;
2110
2111     sprintf (buf, "%s - %s", version, host_system);
2112     rh_width = text_width (this, buf);
2113     
2114     out_text_plain (this, buf, this->width - this->prop_em_width - rh_width,
2115                     y, rh_width);
2116
2117     if (string)
2118       out_text_plain (this, string, this->prop_em_width, y,
2119                       this->width - 3 * this->prop_em_width - rh_width);
2120
2121     y += fh;
2122   }
2123
2124   ext->current = old_current;
2125   free (ext->family);
2126   ext->family = old_family;
2127   ext->size = old_size;
2128 }
2129
2130 \f
2131 /* Text. */
2132
2133 static void
2134 ps_text_set_font_by_name (struct outp_driver *this, const char *dit)
2135 {
2136   struct ps_driver_ext *x = this->ext;
2137   struct font_entry *fe;
2138
2139   assert (this->driver_open && this->page_open);
2140   
2141   /* Short-circuit common fonts. */
2142   if (!strcmp (dit, "PROP"))
2143     {
2144       x->current = x->prop;
2145       x->size = x->font_size;
2146       return;
2147     }
2148   else if (!strcmp (dit, "FIXED"))
2149     {
2150       x->current = x->fixed;
2151       x->size = x->font_size;
2152       return;
2153     }
2154
2155   /* Find font_desc corresponding to Groff name dit. */
2156   fe = hsh_find (x->loaded, &dit);
2157   if (fe == NULL)
2158     fe = load_font (this, dit);
2159   x->current = fe;
2160 }
2161
2162 static void
2163 ps_text_set_font_by_position (struct outp_driver *this, int pos)
2164 {
2165   struct ps_driver_ext *x = this->ext;
2166   char *dit;
2167
2168   assert (this->driver_open && this->page_open);
2169
2170   /* Determine font name by suffixing position string to font family
2171      name. */
2172   {
2173     char *cp;
2174
2175     dit = local_alloc (strlen (x->family) + 3);
2176     cp = stpcpy (dit, x->family);
2177     switch (pos)
2178       {
2179       case OUTP_F_R:
2180         *cp++ = 'R';
2181         break;
2182       case OUTP_F_I:
2183         *cp++ = 'I';
2184         break;
2185       case OUTP_F_B:
2186         *cp++ = 'B';
2187         break;
2188       case OUTP_F_BI:
2189         *cp++ = 'B';
2190         *cp++ = 'I';
2191         break;
2192       default:
2193         assert(0);
2194       }
2195     *cp++ = 0;
2196   }
2197   
2198   /* Find font_desc corresponding to Groff name dit. */
2199   {
2200     struct font_entry *fe = hsh_find (x->loaded, &dit);
2201     if (fe == NULL)
2202       fe = load_font (this, dit);
2203     x->current = fe;
2204   }
2205
2206   local_free (dit);
2207 }
2208
2209 static void
2210 ps_text_set_font_family (struct outp_driver *this, const char *s)
2211 {
2212   struct ps_driver_ext *x = this->ext;
2213
2214   assert (this->driver_open && this->page_open);
2215   
2216   free(x->family);
2217   x->family = xstrdup (s);
2218 }
2219
2220 static const char *
2221 ps_text_get_font_name (struct outp_driver *this)
2222 {
2223   struct ps_driver_ext *x = this->ext;
2224
2225   assert (this->driver_open && this->page_open);
2226   return x->current->font->name;
2227 }
2228
2229 static const char *
2230 ps_text_get_font_family (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->family;
2236 }
2237
2238 static int
2239 ps_text_set_size (struct outp_driver *this, int size)
2240 {
2241   struct ps_driver_ext *x = this->ext;
2242
2243   assert (this->driver_open && this->page_open);
2244   x->size = PSUS / 72000 * size;
2245   return 1;
2246 }
2247
2248 static int
2249 ps_text_get_size (struct outp_driver *this, int *em_width)
2250 {
2251   struct ps_driver_ext *x = this->ext;
2252
2253   assert (this->driver_open && this->page_open);
2254   if (em_width)
2255     *em_width = (x->current->font->space_width * x->size) / 1000;
2256   return x->size / (PSUS / 72000);
2257 }
2258
2259 /* An output character. */
2260 struct output_char
2261   {
2262     struct font_entry *font;    /* Font of character. */
2263     int size;                   /* Size of character. */
2264     int x, y;                   /* Location of character. */
2265     unsigned char ch;           /* Character. */
2266     char separate;              /* Must be separate from previous char. */
2267   };
2268
2269 /* Hash table comparison function for ps_combo structs. */
2270 static int
2271 compare_ps_combo (const void *pa, const void *pb, void *foo UNUSED)
2272 {
2273   const struct ps_font_combo *a = pa;
2274   const struct ps_font_combo *b = pb;
2275
2276   return !((a->font == b->font) && (a->size == b->size));
2277 }
2278
2279 /* Hash table hash function for ps_combo structs. */
2280 static unsigned
2281 hash_ps_combo (const void *pa, void *foo UNUSED)
2282 {
2283   const struct ps_font_combo *a = pa;
2284   unsigned name_hash = hsh_hash_string (a->font->font->internal_name);
2285   return name_hash ^ hsh_hash_int (a->size);
2286 }
2287
2288 /* Hash table free function for ps_combo structs. */
2289 static void
2290 free_ps_combo (void *a, void *foo UNUSED)
2291 {
2292   free (a);
2293 }
2294
2295 /* Causes PostScript code to be output that switches to the font
2296    CP->FONT and font size CP->SIZE.  The first time a particular
2297    font/size combination is used on a particular page, this involves
2298    outputting PostScript code to load the font. */
2299 static void
2300 switch_font (struct outp_driver *this, const struct output_char *cp)
2301 {
2302   struct ps_driver_ext *ext = this->ext;
2303   struct ps_font_combo srch, **fc;
2304
2305   srch.font = cp->font;
2306   srch.size = cp->size;
2307
2308   fc = (struct ps_font_combo **) hsh_probe (ext->combos, &srch);
2309   if (*fc)
2310     {
2311       fprintf (ext->file.file, "F%x%s", (*fc)->index, ext->eol);
2312     }
2313   else
2314     {
2315       char *filename;
2316       struct ps_encoding *encoding;
2317       char buf[512], *bp;
2318
2319       *fc = xmalloc (sizeof **fc);
2320       (*fc)->font = cp->font;
2321       (*fc)->size = cp->size;
2322       (*fc)->index = ext->next_combo++;
2323
2324       filename = find_encoding_file (this, cp->font->font->encoding);
2325       if (filename)
2326         {
2327           encoding = get_encoding (this, filename);
2328           free (filename);
2329         }
2330       else
2331         {
2332           msg (IE, _("PostScript driver: Cannot find encoding `%s' for "
2333                "PostScript font `%s'."), cp->font->font->encoding,
2334                cp->font->font->internal_name);
2335           encoding = default_encoding (this);
2336         }
2337
2338       if (cp->font != ext->fixed && cp->font != ext->prop)
2339         {
2340           bp = stpcpy (buf, "%%IncludeResource: font ");
2341           bp = quote_ps_string (bp, cp->font->font->internal_name);
2342           bp = stpcpy (bp, ext->eol);
2343         }
2344       else
2345         bp = buf;
2346
2347       bp = spprintf (bp, "/F%x E%x %d", (*fc)->index, encoding->index,
2348                      cp->size);
2349       bp = quote_ps_name (bp, cp->font->font->internal_name);
2350       sprintf (bp, " SF%s", ext->eol);
2351       fputs (buf, ext->file.file);
2352     }
2353   ext->last_font = *fc;
2354 }
2355
2356 /* (write_text) Writes the accumulated line buffer to the output
2357    file. */
2358 #define output_line()                           \
2359         do                                      \
2360           {                                     \
2361             lp = stpcpy (lp, ext->eol);         \
2362             *lp = 0;                            \
2363             fputs (line, ext->file.file);       \
2364             lp = line;                          \
2365           }                                     \
2366         while (0)
2367
2368 /* (write_text) Adds the string representing number X to the line
2369    buffer, flushing the buffer to disk beforehand if necessary. */
2370 #define put_number(X)                           \
2371         do                                      \
2372           {                                     \
2373             int n = nsprintf (number, "%d", X); \
2374             if (n + lp > &line[75])             \
2375               output_line ();                   \
2376             lp = stpcpy (lp, number);           \
2377           }                                     \
2378         while (0)
2379
2380 /* Outputs PostScript code to THIS driver's output file to display the
2381    characters represented by the output_char's between CP and END,
2382    using the associated outp_text T to determine formatting.  WIDTH is
2383    the width of the output region; WIDTH_LEFT is the amount of the
2384    WIDTH that is not taken up by text (so it can be used to determine
2385    justification). */
2386 static void
2387 write_text (struct outp_driver *this,
2388             const struct output_char *cp, const struct output_char *end,
2389             struct outp_text *t, int width UNUSED, int width_left)
2390 {
2391   struct ps_driver_ext *ext = this->ext;
2392   int ofs;
2393
2394   int last_y;
2395
2396   char number[INT_DIGITS + 1];
2397   char line[80];
2398   char *lp;
2399
2400   switch (t->options & OUTP_T_JUST_MASK)
2401     {
2402     case OUTP_T_JUST_LEFT:
2403       ofs = 0;
2404       break;
2405     case OUTP_T_JUST_RIGHT:
2406       ofs = width_left;
2407       break;
2408     case OUTP_T_JUST_CENTER:
2409       ofs = width_left / 2;
2410       break;
2411     default:
2412       assert (0);
2413     }
2414
2415   lp = line;
2416   last_y = INT_MIN;
2417   while (cp < end)
2418     {
2419       int x = cp->x + ofs;
2420       int y = cp->y + (cp->font->font->ascent * cp->size / 1000);
2421
2422       if (ext->last_font == NULL
2423           || cp->font != ext->last_font->font
2424           || cp->size != ext->last_font->size)
2425         switch_font (this, cp);
2426
2427       *lp++ = '(';
2428       do
2429         {
2430           /* PORTME! */
2431           static unsigned char literal_chars[ODA_COUNT][32] =
2432           {
2433             {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff,
2434              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
2435              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2436              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2437             },
2438             {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff,
2439              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2440              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2441              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2442             },
2443             {0x7e, 0xd6, 0xff, 0xfb, 0xff, 0xfc, 0xff, 0xff,
2444              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2445              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2446              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2447             }
2448           };
2449
2450           if (TEST_BIT (literal_chars[ext->data], cp->ch))
2451             *lp++ = cp->ch;
2452           else
2453             switch (cp->ch)
2454               {
2455               case '(':
2456                 lp = stpcpy (lp, "\\(");
2457                 break;
2458               case ')':
2459                 lp = stpcpy (lp, "\\)");
2460                 break;
2461               default:
2462                 lp = spprintf (lp, "\\%03o", cp->ch);
2463                 break;
2464               }
2465           cp++;
2466         }
2467       while (cp < end && lp < &line[70] && cp->separate == 0);
2468       *lp++ = ')';
2469
2470       put_number (x);
2471
2472       if (y != last_y)
2473         {
2474           *lp++ = ' ';
2475           put_number (YT (y));
2476           *lp++ = ' ';
2477           *lp++ = 'S';
2478           last_y = y;
2479         }
2480       else
2481         {
2482           *lp++ = ' ';
2483           *lp++ = 'T';
2484         }
2485
2486       if (lp >= &line[70])
2487         output_line ();
2488     }
2489   if (lp != line)
2490     output_line ();
2491 }
2492
2493 #undef output_line
2494 #undef put_number
2495
2496 /* Displays the text in outp_text T, if DRAW is nonzero; or, merely
2497    determine the text metrics, if DRAW is zero. */
2498 static void
2499 text (struct outp_driver *this, struct outp_text *t, int draw)
2500 {
2501   struct ps_driver_ext *ext = this->ext;
2502
2503   /* Output. */
2504   struct output_char *buf;      /* Output buffer. */
2505   struct output_char *buf_end;  /* End of output buffer. */
2506   struct output_char *buf_loc;  /* Current location in output buffer. */
2507
2508   /* Saved state. */
2509   struct font_entry *old_current = ext->current;
2510   char *old_family = xstrdup (ext->family); /* FIXME */
2511   int old_size = ext->size;
2512
2513   /* Input string. */
2514   char *cp, *end;
2515
2516   /* Current location. */
2517   int x, y;
2518
2519   /* Keeping track of what's left over. */
2520   int width;                    /* Width available for characters. */
2521   int width_left, height_left;  /* Width, height left over. */
2522   int max_height;               /* Tallest character on this line so far. */
2523
2524   /* Previous character. */
2525   int prev_char;
2526
2527   /* Information about location of previous space. */
2528   char *space_char;             /* Character after space. */
2529   struct output_char *space_buf_loc; /* Buffer location after space. */
2530   int space_width_left;         /* Width of characters before space. */
2531
2532   /* Name of the current character. */
2533   const char *char_name;
2534   char local_char_name[2] = {0, 0};
2535
2536   local_char_name[0] = local_char_name[1] = 0;
2537
2538   buf = local_alloc (sizeof *buf * 128);
2539   buf_end = &buf[128];
2540   buf_loc = buf;
2541
2542   assert (!ls_null_p (&t->s));
2543   cp = ls_value (&t->s);
2544   end = ls_end (&t->s);
2545   if (draw)
2546     {
2547       x = t->x;
2548       y = t->y;
2549     }
2550   else
2551     x = y = 0;
2552   width = width_left = (t->options & OUTP_T_HORZ) ? t->h : INT_MAX;
2553   height_left = (t->options & OUTP_T_VERT) ? t->v : INT_MAX;
2554   max_height = 0;
2555   prev_char = -1;
2556   space_char = NULL;
2557   space_buf_loc = NULL;
2558   space_width_left = 0;
2559   
2560
2561   if (!width || !height_left)
2562     goto exit;
2563
2564   while (cp < end)
2565     {
2566       struct char_metrics *metric;
2567       int cur_char;
2568       int kern_amt;
2569       int char_width;
2570       int separate = 0;
2571
2572       /* Set char_name to the name of the character or ligature at
2573          *cp. */
2574       local_char_name[0] = *cp;
2575       char_name = local_char_name;
2576       if (ext->current->font->ligatures && *cp == 'f')
2577         {
2578           int lig = 0;
2579           char_name = NULL;
2580
2581           if (cp < end - 1)
2582             switch (cp[1])
2583               {
2584               case 'i':
2585                 lig = LIG_fi, char_name = "fi";
2586                 break;
2587               case 'l':
2588                 lig = LIG_fl, char_name = "fl";
2589                 break;
2590               case 'f':
2591                 if (cp < end - 2)
2592                   switch (cp[2])
2593                     {
2594                     case 'i':
2595                       lig = LIG_ffi, char_name = "ffi";
2596                       goto got_ligature;
2597                     case 'l':
2598                       lig = LIG_ffl, char_name = "ffl";
2599                       goto got_ligature;
2600                     }
2601                 lig = LIG_ff, char_name = "ff";
2602               got_ligature:
2603                 break;
2604               }
2605           if ((lig & ext->current->font->ligatures) == 0)
2606             {
2607               local_char_name[0] = *cp; /* 'f' */
2608               char_name = local_char_name;
2609             }
2610         }
2611       else if (*cp == '\n')
2612         {
2613           if (draw)
2614             {
2615               write_text (this, buf, buf_loc, t, width, width_left);
2616               buf_loc = buf;
2617               x = t->x;
2618               y += max_height;
2619             }
2620
2621           width_left = width;
2622           height_left -= max_height;
2623           max_height = 0;
2624           kern_amt = 0;
2625           separate = 1;
2626           cp++;
2627
2628           /* FIXME: when we're page buffering it will be necessary to
2629              set separate to 1. */
2630           continue;
2631         }
2632       cp += strlen (char_name);
2633
2634       /* Figure out what size this character is, and what kern
2635          adjustment we need. */
2636       cur_char = font_char_name_to_index (char_name);
2637       metric = font_get_char_metrics (ext->current->font, cur_char);
2638       if (!metric)
2639         {
2640           static struct char_metrics m;
2641           metric = &m;
2642           m.width = ext->current->font->space_width;
2643           m.code = *char_name;
2644         }
2645       kern_amt = font_get_kern_adjust (ext->current->font, prev_char,
2646                                        cur_char);
2647       if (kern_amt)
2648         {
2649           kern_amt = (kern_amt * ext->size / 1000);
2650           separate = 1;
2651         }
2652       char_width = metric->width * ext->size / 1000;
2653
2654       /* Record the current status if this is a space character. */
2655       if (cur_char == space_index && buf_loc > buf)
2656         {
2657           space_char = cp;
2658           space_buf_loc = buf_loc;
2659           space_width_left = width_left;
2660         }
2661
2662       /* Drop down to a new line if there's no room left on this
2663          line. */
2664       if (char_width + kern_amt > width_left)
2665         {
2666           /* Regress to previous space, if any. */
2667           if (space_char)
2668             {
2669               cp = space_char;
2670               width_left = space_width_left;
2671               buf_loc = space_buf_loc;
2672             }
2673
2674           if (draw)
2675             {
2676               write_text (this, buf, buf_loc, t, width, width_left);
2677               buf_loc = buf;
2678               x = t->x;
2679               y += max_height;
2680             }
2681
2682           width_left = width;
2683           height_left -= max_height;
2684           max_height = 0;
2685           kern_amt = 0;
2686
2687           if (space_char)
2688             {
2689               space_char = NULL;
2690               prev_char = -1;
2691               /* FIXME: when we're page buffering it will be
2692                  necessary to set separate to 1. */
2693               continue;
2694             }
2695           separate = 1;
2696         }
2697       if (ext->size > max_height)
2698         max_height = ext->size;
2699       if (max_height > height_left)
2700         goto exit;
2701
2702       /* Actually draw the character. */
2703       if (draw)
2704         {
2705           if (buf_loc >= buf_end)
2706             {
2707               int buf_len = buf_end - buf;
2708
2709               if (buf_len == 128)
2710                 {
2711                   struct output_char *new_buf;
2712
2713                   new_buf = xmalloc (sizeof *new_buf * 256);
2714                   memcpy (new_buf, buf, sizeof *new_buf * 128);
2715                   buf_loc = new_buf + 128;
2716                   buf_end = new_buf + 256;
2717                   local_free (buf);
2718                   buf = new_buf;
2719                 }
2720               else
2721                 {
2722                   buf = xrealloc (buf, sizeof *buf * buf_len * 2);
2723                   buf_loc = buf + buf_len;
2724                   buf_end = buf + buf_len * 2;
2725                 }
2726             }
2727
2728           x += kern_amt;
2729           buf_loc->font = ext->current;
2730           buf_loc->size = ext->size;
2731           buf_loc->x = x;
2732           buf_loc->y = y;
2733           buf_loc->ch = metric->code;
2734           buf_loc->separate = separate;
2735           buf_loc++;
2736           x += char_width;
2737         }
2738
2739       /* Prepare for next iteration. */
2740       width_left -= char_width + kern_amt;
2741       prev_char = cur_char;
2742     }
2743   height_left -= max_height;
2744   if (buf_loc > buf && draw)
2745     write_text (this, buf, buf_loc, t, width, width_left);
2746
2747 exit:
2748   if (!(t->options & OUTP_T_HORZ))
2749     t->h = INT_MAX - width_left;
2750   if (!(t->options & OUTP_T_VERT))
2751     t->v = INT_MAX - height_left;
2752   else
2753     t->v -= height_left;
2754   if (buf_end - buf == 128)
2755     local_free (buf);
2756   else
2757     free (buf);
2758   ext->current = old_current;
2759   free (ext->family);
2760   ext->family = old_family;
2761   ext->size = old_size;
2762 }
2763
2764 static void
2765 ps_text_metrics (struct outp_driver *this, struct outp_text *t)
2766 {
2767   assert (this->driver_open && this->page_open);
2768   text (this, t, 0);
2769 }
2770
2771 static void
2772 ps_text_draw (struct outp_driver *this, struct outp_text *t)
2773 {
2774   assert (this->driver_open && this->page_open);
2775   text (this, t, 1);
2776 }
2777 \f
2778 /* Font loader. */
2779
2780 /* Translate a filename to a font. */
2781 struct filename2font
2782   {
2783     char *filename;             /* Normalized filename. */
2784     struct font_desc *font;
2785   };
2786
2787 /* Table of `filename2font's. */
2788 static struct hsh_table *ps_fonts;
2789
2790 /* Hash table comparison function for filename2font structs. */
2791 static int
2792 compare_filename2font (const void *a, const void *b, void *param UNUSED)
2793 {
2794   return strcmp (((struct filename2font *) a)->filename,
2795                  ((struct filename2font *) b)->filename);
2796 }
2797
2798 /* Hash table hash function for filename2font structs. */
2799 static unsigned
2800 hash_filename2font (const void *f2f_, void *param UNUSED)
2801 {
2802   const struct filename2font *f2f = f2f_;
2803   return hsh_hash_string (f2f->filename);
2804 }
2805
2806 /* Initializes the global font list by creating the hash table for
2807    translation of filenames to font_desc structs. */
2808 static void
2809 init_fonts (void)
2810 {
2811   ps_fonts = hsh_create (31, compare_filename2font, hash_filename2font,
2812                          NULL, NULL);
2813 }
2814
2815 /* Loads the font having Groff name DIT into THIS driver instance.
2816    Specifically, adds it into the THIS driver's `loaded' hash
2817    table. */
2818 static struct font_entry *
2819 load_font (struct outp_driver *this, const char *dit)
2820 {
2821   struct ps_driver_ext *x = this->ext;
2822   char *filename1, *filename2;
2823   void **entry;
2824   struct font_entry *fe;
2825
2826   filename1 = find_ps_file (this, dit);
2827   if (!filename1)
2828     filename1 = xstrdup (dit);
2829   filename2 = fn_normalize (filename1);
2830   free (filename1);
2831
2832   entry = hsh_probe (ps_fonts, &filename2);
2833   if (*entry == NULL)
2834     {
2835       struct filename2font *f2f;
2836       struct font_desc *f = groff_read_font (filename2);
2837
2838       if (f == NULL)
2839         {
2840           if (x->fixed)
2841             f = x->fixed->font;
2842           else
2843             f = default_font ();
2844         }
2845       
2846       f2f = xmalloc (sizeof *f2f);
2847       f2f->filename = filename2;
2848       f2f->font = f;
2849       *entry = f2f;
2850     }
2851   else
2852     free (filename2);
2853
2854   fe = xmalloc (sizeof *fe);
2855   fe->dit = xstrdup (dit);
2856   fe->font = ((struct filename2font *) * entry)->font;
2857   *hsh_probe (x->loaded, &dit) = fe;
2858
2859   return fe;
2860 }
2861
2862 /* PostScript driver class. */
2863 struct outp_class postscript_class =
2864 {
2865   "postscript",
2866   MAGIC_PS,
2867   0,
2868
2869   ps_open_global,
2870   ps_close_global,
2871   ps_font_sizes,
2872
2873   ps_preopen_driver,
2874   ps_option,
2875   ps_postopen_driver,
2876   ps_close_driver,
2877
2878   ps_open_page,
2879   ps_close_page,
2880
2881   NULL,
2882
2883   ps_line_horz,
2884   ps_line_vert,
2885   ps_line_intersection,
2886
2887   ps_box,
2888   ps_polyline_begin,
2889   ps_polyline_point,
2890   ps_polyline_end,
2891
2892   ps_text_set_font_by_name,
2893   ps_text_set_font_by_position,
2894   ps_text_set_font_family,
2895   ps_text_get_font_name,
2896   ps_text_get_font_family,
2897   ps_text_set_size,
2898   ps_text_get_size,
2899   ps_text_metrics,
2900   ps_text_draw,
2901 };
2902
2903 /* EPSF driver class.  FIXME: Probably doesn't work right. */
2904 struct outp_class epsf_class =
2905 {
2906   "epsf",
2907   MAGIC_EPSF,
2908   0,
2909
2910   ps_open_global,
2911   ps_close_global,
2912   ps_font_sizes,
2913
2914   ps_preopen_driver,
2915   ps_option,
2916   ps_postopen_driver,
2917   ps_close_driver,
2918
2919   ps_open_page,
2920   ps_close_page,
2921
2922   NULL,
2923
2924   ps_line_horz,
2925   ps_line_vert,
2926   ps_line_intersection,
2927
2928   ps_box,
2929   ps_polyline_begin,
2930   ps_polyline_point,
2931   ps_polyline_end,
2932
2933   ps_text_set_font_by_name,
2934   ps_text_set_font_by_position,
2935   ps_text_set_font_family,
2936   ps_text_get_font_name,
2937   ps_text_get_font_family,
2938   ps_text_set_size,
2939   ps_text_get_size,
2940   ps_text_metrics,
2941   ps_text_draw,
2942 };
2943
2944 #endif /* NO_POSTSCRIPT */