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