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