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