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