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