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