Fix spacing.
[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 *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 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
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   for (fe = hsh_first (x->loaded, &iter); fe != NULL;
1633        fe = hsh_next (x->loaded, &iter)) 
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   int type;
1832
1833   for (type = 0; type < n_line_types; type++)
1834     {
1835       struct line_form *line;
1836
1837       if (x->lines[type] == NULL) 
1838         continue;
1839
1840       for (line = hsh_first (x->lines[type], &iter); line != NULL;
1841            line = hsh_next (x->lines[type], &iter)) 
1842         {
1843           int i;
1844           int lo = INT_MIN, hi;
1845
1846           qsort (line->dep, line->ndep, sizeof *line->dep, int_2_compare);
1847           lo = line->dep[0][0];
1848           hi = line->dep[0][1];
1849           for (i = 1; i < line->ndep; i++)
1850             if (line->dep[i][0] <= hi + 1)
1851               {
1852                 int min_hi = line->dep[i][1];
1853                 if (min_hi > hi)
1854                   hi = min_hi;
1855               }
1856             else
1857               {
1858                 dump_fancy_line (this, type, line->ind, lo, hi);
1859                 lo = line->dep[i][0];
1860                 hi = line->dep[i][1];
1861               }
1862           dump_fancy_line (this, type, line->ind, lo, hi);
1863         }
1864
1865       hsh_destroy (x->lines[type]);
1866       x->lines[type] = NULL;
1867     }
1868 }
1869
1870 /* (Same args as dump_fancy_line()).  Either dumps the line directly
1871    to the output file, or adds it to the cache, depending on the
1872    user-selected line optimization mode. */
1873 static void
1874 line (struct outp_driver *this, int type, int ind, int dep1, int dep2)
1875 {
1876   struct ps_driver_ext *ext = this->ext;
1877   struct line_form **f;
1878
1879   assert (dep2 >= dep1);
1880   if (ext->line_opt == 0)
1881     {
1882       dump_fancy_line (this, type, ind, dep1, dep2);
1883       return;
1884     }
1885
1886   if (ext->lines[type] == NULL)
1887     ext->lines[type] = hsh_create (31, compare_line, hash_line,
1888                                    free_line, NULL);
1889   f = (struct line_form **) hsh_probe (ext->lines[type],
1890                                        (struct line_form *) & ind);
1891   if (*f == NULL)
1892     {
1893       *f = xmalloc (sizeof **f + sizeof (int[15][2]));
1894       (*f)->ind = ind;
1895       (*f)->mdep = 16;
1896       (*f)->ndep = 1;
1897       (*f)->dep[0][0] = dep1;
1898       (*f)->dep[0][1] = dep2;
1899       return;
1900     }
1901   if ((*f)->ndep >= (*f)->mdep)
1902     {
1903       (*f)->mdep += 16;
1904       *f = xrealloc (*f, (sizeof **f + sizeof (int[2]) * ((*f)->mdep - 1)));
1905     }
1906   (*f)->dep[(*f)->ndep][0] = dep1;
1907   (*f)->dep[(*f)->ndep][1] = dep2;
1908   (*f)->ndep++;
1909 }
1910
1911 void
1912 ps_line_horz (struct outp_driver *this, const struct rect *r,
1913               const struct color *c unused, int style)
1914 {
1915   /* Must match output.h:OUTP_L_*. */
1916   static const int types[OUTP_L_COUNT] =
1917   {-1, horz, dbl_horz, spl_horz};
1918
1919   int y = (r->y1 + r->y2) / 2;
1920
1921   assert (this->driver_open && this->page_open);
1922   assert (style >= 0 && style < OUTP_L_COUNT);
1923   style = types[style];
1924   if (style != -1)
1925     line (this, style, y, r->x1, r->x2);
1926 }
1927
1928 void
1929 ps_line_vert (struct outp_driver *this, const struct rect *r,
1930               const struct color *c unused, int style)
1931 {
1932   /* Must match output.h:OUTP_L_*. */
1933   static const int types[OUTP_L_COUNT] =
1934   {-1, vert, dbl_vert, spl_vert};
1935
1936   int x = (r->x1 + r->x2) / 2;
1937
1938   assert (this->driver_open && this->page_open);
1939   assert (style >= 0 && style < OUTP_L_COUNT);
1940   style = types[style];
1941   if (style != -1)
1942     line (this, style, x, r->y1, r->y2);
1943 }
1944
1945 #define L (style->l != OUTP_L_NONE)
1946 #define R (style->r != OUTP_L_NONE)
1947 #define T (style->t != OUTP_L_NONE)
1948 #define B (style->b != OUTP_L_NONE)
1949
1950 void
1951 ps_line_intersection (struct outp_driver *this, const struct rect *r,
1952                       const struct color *c unused,
1953                       const struct outp_styles *style)
1954 {
1955   struct ps_driver_ext *ext = this->ext;
1956
1957   int x = (r->x1 + r->x2) / 2;
1958   int y = (r->y1 + r->y2) / 2;
1959   int ofs = (ext->line_space + ext->line_width) / 2;
1960   int x1 = x - ofs, x2 = x + ofs;
1961   int y1 = y - ofs, y2 = y + ofs;
1962
1963   assert (this->driver_open && this->page_open);
1964   assert (!((style->l != style->r && style->l != OUTP_L_NONE
1965              && style->r != OUTP_L_NONE)
1966             || (style->t != style->b && style->t != OUTP_L_NONE
1967                 && style->b != OUTP_L_NONE)));
1968
1969   switch ((style->l | style->r) | ((style->t | style->b) << 8))
1970     {
1971     case (OUTP_L_SINGLE) | (OUTP_L_SINGLE << 8):
1972     case (OUTP_L_SINGLE) | (OUTP_L_NONE << 8):
1973     case (OUTP_L_NONE) | (OUTP_L_SINGLE << 8):
1974       if (L)
1975         line (this, horz, y, r->x1, x);
1976       if (R)
1977         line (this, horz, y, x, r->x2);
1978       if (T)
1979         line (this, vert, x, r->y1, y);
1980       if (B)
1981         line (this, vert, x, y, r->y2);
1982       break;
1983     case (OUTP_L_SINGLE) | (OUTP_L_DOUBLE << 8):
1984     case (OUTP_L_NONE) | (OUTP_L_DOUBLE << 8):
1985       if (L)
1986         line (this, horz, y, r->x1, x1);
1987       if (R)
1988         line (this, horz, y, x2, r->x2);
1989       if (T)
1990         line (this, dbl_vert, x, r->y1, y);
1991       if (B)
1992         line (this, dbl_vert, x, y, r->y2);
1993       if ((L && R) && !(T && B))
1994         line (this, horz, y, x1, x2);
1995       break;
1996     case (OUTP_L_DOUBLE) | (OUTP_L_SINGLE << 8):
1997     case (OUTP_L_DOUBLE) | (OUTP_L_NONE << 8):
1998       if (L)
1999         line (this, dbl_horz, y, r->x1, x);
2000       if (R)
2001         line (this, dbl_horz, y, x, r->x2);
2002       if (T)
2003         line (this, vert, x, r->y1, y);
2004       if (B)
2005         line (this, vert, x, y, r->y2);
2006       if ((T && B) && !(L && R))
2007         line (this, vert, x, y1, y2);
2008       break;
2009     case (OUTP_L_DOUBLE) | (OUTP_L_DOUBLE << 8):
2010       if (L)
2011         line (this, dbl_horz, y, r->x1, x);
2012       if (R)
2013         line (this, dbl_horz, y, x, r->x2);
2014       if (T)
2015         line (this, dbl_vert, x, r->y1, y);
2016       if (B)
2017         line (this, dbl_vert, x, y, r->y2);
2018       if (T && B && !L)
2019         line (this, vert, x1, y1, y2);
2020       if (T && B && !R)
2021         line (this, vert, x2, y1, y2);
2022       if (L && R && !T)
2023         line (this, horz, y1, x1, x2);
2024       if (L && R && !B)
2025         line (this, horz, y2, x1, x2);
2026       break;
2027     default:
2028       assert (0);
2029     }
2030 }
2031
2032 void
2033 ps_line_width (struct outp_driver *this, int *width, int *height)
2034 {
2035   struct ps_driver_ext *x = this->ext;
2036
2037   assert (this->driver_open && this->page_open);
2038   width[0] = height[0] = 0;
2039   width[1] = height[1] = 2 * x->line_gutter + x->line_width;
2040   width[2] = height[2] = (2 * x->line_gutter + 2 * x->line_width
2041                           + x->line_space);
2042   width[3] = height[3] = 2 * x->line_gutter + x->line_width;
2043 }
2044
2045 void
2046 ps_box (struct outp_driver *this unused, const struct rect *r unused,
2047         const struct color *bord unused, const struct color *fill unused)
2048 {
2049   assert (this->driver_open && this->page_open);
2050 }
2051
2052 void 
2053 ps_polyline_begin (struct outp_driver *this unused,
2054                    const struct color *c unused)
2055 {
2056   assert (this->driver_open && this->page_open);
2057 }
2058 void 
2059 ps_polyline_point (struct outp_driver *this unused, int x unused, int y unused)
2060 {
2061   assert (this->driver_open && this->page_open);
2062 }
2063 void 
2064 ps_polyline_end (struct outp_driver *this unused)
2065 {
2066   assert (this->driver_open && this->page_open);
2067 }
2068
2069 /* Returns the width of string S for THIS driver. */
2070 static int
2071 text_width (struct outp_driver *this, char *s)
2072 {
2073   struct outp_text text;
2074
2075   text.options = OUTP_T_JUST_LEFT;
2076   ls_init (&text.s, s, strlen (s));
2077   this->class->text_metrics (this, &text);
2078   return text.h;
2079 }
2080
2081 /* Write string S at location (X,Y) with width W for THIS driver. */
2082 static void
2083 out_text_plain (struct outp_driver *this, char *s, int x, int y, int w)
2084 {
2085   struct outp_text text;
2086
2087   text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT;
2088   ls_init (&text.s, s, strlen (s));
2089   text.h = w;
2090   text.v = this->font_height;
2091   text.x = x;
2092   text.y = y;
2093   this->class->text_draw (this, &text);
2094 }
2095
2096 /* Draw top of page headers for THIS driver. */
2097 static void
2098 draw_headers (struct outp_driver *this)
2099 {
2100   struct ps_driver_ext *ext = this->ext;
2101   
2102   struct font_entry *old_current = ext->current;
2103   char *old_family = xstrdup (ext->family); /* FIXME */
2104   int old_size = ext->size;
2105
2106   int fh = this->font_height;
2107   int y = -3 * fh;
2108
2109   fprintf (ext->file.file, "%d %d %d %d GB%s",
2110            0, YT (y), this->width, YT (y + 2 * fh + ext->line_gutter),
2111            ext->eol);
2112   this->class->text_set_font_family (this, "T");
2113
2114   y += ext->line_width + ext->line_gutter;
2115   
2116   {
2117     int rh_width;
2118     char buf[128];
2119
2120     sprintf (buf, _("%s - Page %d"), curdate, ext->page_number);
2121     rh_width = text_width (this, buf);
2122
2123     out_text_plain (this, buf, this->width - this->prop_em_width - rh_width,
2124                     y, rh_width);
2125
2126     if (outp_title && outp_subtitle)
2127       out_text_plain (this, outp_title, this->prop_em_width, y,
2128                       this->width - 3 * this->prop_em_width - rh_width);
2129
2130     y += fh;
2131   }
2132   
2133   {
2134     int rh_width;
2135     char buf[128];
2136     char *string = outp_subtitle ? outp_subtitle : outp_title;
2137
2138     sprintf (buf, "%s - %s", version, host_system);
2139     rh_width = text_width (this, buf);
2140     
2141     out_text_plain (this, buf, this->width - this->prop_em_width - rh_width,
2142                     y, rh_width);
2143
2144     if (string)
2145       out_text_plain (this, string, this->prop_em_width, y,
2146                       this->width - 3 * this->prop_em_width - rh_width);
2147
2148     y += fh;
2149   }
2150
2151   ext->current = old_current;
2152   free (ext->family);
2153   ext->family = old_family;
2154   ext->size = old_size;
2155 }
2156
2157 \f
2158 /* Text. */
2159
2160 void
2161 ps_text_set_font_by_name (struct outp_driver *this, const char *dit)
2162 {
2163   struct ps_driver_ext *x = this->ext;
2164   struct font_entry *fe;
2165
2166   assert (this->driver_open && this->page_open);
2167   
2168   /* Short-circuit common fonts. */
2169   if (!strcmp (dit, "PROP"))
2170     {
2171       x->current = x->prop;
2172       x->size = x->font_size;
2173       return;
2174     }
2175   else if (!strcmp (dit, "FIXED"))
2176     {
2177       x->current = x->fixed;
2178       x->size = x->font_size;
2179       return;
2180     }
2181
2182   /* Find font_desc corresponding to Groff name dit. */
2183   fe = hsh_find (x->loaded, &dit);
2184   if (fe == NULL)
2185     fe = load_font (this, dit);
2186   x->current = fe;
2187 }
2188
2189 void
2190 ps_text_set_font_by_position (struct outp_driver *this, int pos)
2191 {
2192   struct ps_driver_ext *x = this->ext;
2193   char *dit;
2194
2195   assert (this->driver_open && this->page_open);
2196
2197   /* Determine font name by suffixing position string to font family
2198      name. */
2199   {
2200     char *cp;
2201
2202     dit = local_alloc (strlen (x->family) + 3);
2203     cp = stpcpy (dit, x->family);
2204     switch (pos)
2205       {
2206       case OUTP_F_R:
2207         *cp++ = 'R';
2208         break;
2209       case OUTP_F_I:
2210         *cp++ = 'I';
2211         break;
2212       case OUTP_F_B:
2213         *cp++ = 'B';
2214         break;
2215       case OUTP_F_BI:
2216         *cp++ = 'B';
2217         *cp++ = 'I';
2218         break;
2219       default:
2220         assert(0);
2221       }
2222     *cp++ = 0;
2223   }
2224   
2225   /* Find font_desc corresponding to Groff name dit. */
2226   {
2227     struct font_entry *fe = hsh_find (x->loaded, &dit);
2228     if (fe == NULL)
2229       fe = load_font (this, dit);
2230     x->current = fe;
2231   }
2232
2233   local_free (dit);
2234 }
2235
2236 void
2237 ps_text_set_font_family (struct outp_driver *this, const char *s)
2238 {
2239   struct ps_driver_ext *x = this->ext;
2240
2241   assert (this->driver_open && this->page_open);
2242   
2243   free(x->family);
2244   x->family = xstrdup (s);
2245 }
2246
2247 const char *
2248 ps_text_get_font_name (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->current->font->name;
2254 }
2255
2256 const char *
2257 ps_text_get_font_family (struct outp_driver *this)
2258 {
2259   struct ps_driver_ext *x = this->ext;
2260   
2261   assert (this->driver_open && this->page_open);
2262   return x->family;
2263 }
2264
2265 int
2266 ps_text_set_size (struct outp_driver *this, int size)
2267 {
2268   struct ps_driver_ext *x = this->ext;
2269
2270   assert (this->driver_open && this->page_open);
2271   x->size = PSUS / 72000 * size;
2272   return 1;
2273 }
2274
2275 int
2276 ps_text_get_size (struct outp_driver *this, int *em_width)
2277 {
2278   struct ps_driver_ext *x = this->ext;
2279
2280   assert (this->driver_open && this->page_open);
2281   if (em_width)
2282     *em_width = (x->current->font->space_width * x->size) / 1000;
2283   return x->size / (PSUS / 72000);
2284 }
2285
2286 /* An output character. */
2287 struct output_char
2288   {
2289     struct font_entry *font;    /* Font of character. */
2290     int size;                   /* Size of character. */
2291     int x, y;                   /* Location of character. */
2292     unsigned char ch;           /* Character. */
2293     char separate;              /* Must be separate from previous char. */
2294   };
2295
2296 /* Hash table comparison function for ps_combo structs. */
2297 static int
2298 compare_ps_combo (const void *pa, const void *pb, void *foo unused)
2299 {
2300   const struct ps_font_combo *a = pa;
2301   const struct ps_font_combo *b = pb;
2302
2303   return !((a->font == b->font) && (a->size == b->size));
2304 }
2305
2306 /* Hash table hash function for ps_combo structs. */
2307 static unsigned
2308 hash_ps_combo (const void *pa, void *foo unused)
2309 {
2310   const struct ps_font_combo *a = pa;
2311   unsigned name_hash = hsh_hash_string (a->font->font->internal_name);
2312   return name_hash ^ hsh_hash_int (a->size);
2313 }
2314
2315 /* Hash table free function for ps_combo structs. */
2316 static void
2317 free_ps_combo (void *a, void *foo unused)
2318 {
2319   free (a);
2320 }
2321
2322 /* Causes PostScript code to be output that switches to the font
2323    CP->FONT and font size CP->SIZE.  The first time a particular
2324    font/size combination is used on a particular page, this involves
2325    outputting PostScript code to load the font. */
2326 static void
2327 switch_font (struct outp_driver *this, const struct output_char *cp)
2328 {
2329   struct ps_driver_ext *ext = this->ext;
2330   struct ps_font_combo srch, **fc;
2331
2332   srch.font = cp->font;
2333   srch.size = cp->size;
2334
2335   fc = (struct ps_font_combo **) hsh_probe (ext->combos, &srch);
2336   if (*fc)
2337     {
2338       fprintf (ext->file.file, "F%x%s", (*fc)->index, ext->eol);
2339     }
2340   else
2341     {
2342       char *filename;
2343       struct ps_encoding *encoding;
2344       char buf[512], *bp;
2345
2346       *fc = xmalloc (sizeof **fc);
2347       (*fc)->font = cp->font;
2348       (*fc)->size = cp->size;
2349       (*fc)->index = ext->next_combo++;
2350
2351       filename = find_encoding_file (this, cp->font->font->encoding);
2352       if (filename)
2353         {
2354           encoding = get_encoding (this, filename);
2355           free (filename);
2356         }
2357       else
2358         {
2359           msg (IE, _("PostScript driver: Cannot find encoding `%s' for "
2360                "PostScript font `%s'."), cp->font->font->encoding,
2361                cp->font->font->internal_name);
2362           encoding = default_encoding (this);
2363         }
2364
2365       if (cp->font != ext->fixed && cp->font != ext->prop)
2366         {
2367           bp = stpcpy (buf, "%%IncludeResource: font ");
2368           bp = quote_ps_string (bp, cp->font->font->internal_name);
2369           bp = stpcpy (bp, ext->eol);
2370         }
2371       else
2372         bp = buf;
2373
2374       bp = spprintf (bp, "/F%x E%x %d", (*fc)->index, encoding->index,
2375                      cp->size);
2376       bp = quote_ps_name (bp, cp->font->font->internal_name);
2377       sprintf (bp, " SF%s", ext->eol);
2378       fputs (buf, ext->file.file);
2379     }
2380   ext->last_font = *fc;
2381 }
2382
2383 /* (write_text) Writes the accumulated line buffer to the output
2384    file. */
2385 #define output_line()                           \
2386         do                                      \
2387           {                                     \
2388             lp = stpcpy (lp, ext->eol);         \
2389             *lp = 0;                            \
2390             fputs (line, ext->file.file);       \
2391             lp = line;                          \
2392           }                                     \
2393         while (0)
2394
2395 /* (write_text) Adds the string representing number X to the line
2396    buffer, flushing the buffer to disk beforehand if necessary. */
2397 #define put_number(X)                           \
2398         do                                      \
2399           {                                     \
2400             int n = nsprintf (number, "%d", X); \
2401             if (n + lp > &line[75])             \
2402               output_line ();                   \
2403             lp = stpcpy (lp, number);           \
2404           }                                     \
2405         while (0)
2406
2407 /* Outputs PostScript code to THIS driver's output file to display the
2408    characters represented by the output_char's between CP and END,
2409    using the associated outp_text T to determine formatting.  WIDTH is
2410    the width of the output region; WIDTH_LEFT is the amount of the
2411    WIDTH that is not taken up by text (so it can be used to determine
2412    justification). */
2413 static void
2414 write_text (struct outp_driver *this,
2415             const struct output_char *cp, const struct output_char *end,
2416             struct outp_text *t, int width unused, int width_left)
2417 {
2418   struct ps_driver_ext *ext = this->ext;
2419   int ofs;
2420
2421   int last_y;
2422
2423   char number[INT_DIGITS + 1];
2424   char line[80];
2425   char *lp;
2426
2427   switch (t->options & OUTP_T_JUST_MASK)
2428     {
2429     case OUTP_T_JUST_LEFT:
2430       ofs = 0;
2431       break;
2432     case OUTP_T_JUST_RIGHT:
2433       ofs = width_left;
2434       break;
2435     case OUTP_T_JUST_CENTER:
2436       ofs = width_left / 2;
2437       break;
2438     }
2439
2440   lp = line;
2441   last_y = INT_MIN;
2442   while (cp < end)
2443     {
2444       int x = cp->x + ofs;
2445       int y = cp->y + (cp->font->font->ascent * cp->size / 1000);
2446
2447       if (ext->last_font == NULL
2448           || cp->font != ext->last_font->font
2449           || cp->size != ext->last_font->size)
2450         switch_font (this, cp);
2451
2452       *lp++ = '(';
2453       do
2454         {
2455           /* PORTME! */
2456           static unsigned char literal_chars[ODA_COUNT][32] =
2457           {
2458             {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff,
2459              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
2460              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2461              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2462             },
2463             {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff,
2464              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2465              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2466              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2467             },
2468             {0x7e, 0xd6, 0xff, 0xfb, 0xff, 0xfc, 0xff, 0xff,
2469              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2470              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2471              0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2472             }
2473           };
2474
2475           if (TEST_BIT (literal_chars[ext->data], cp->ch))
2476             *lp++ = cp->ch;
2477           else
2478             switch (cp->ch)
2479               {
2480               case '(':
2481                 lp = stpcpy (lp, "\\(");
2482                 break;
2483               case ')':
2484                 lp = stpcpy (lp, "\\)");
2485                 break;
2486               default:
2487                 lp = spprintf (lp, "\\%03o", cp->ch);
2488                 break;
2489               }
2490           cp++;
2491         }
2492       while (cp < end && lp < &line[70] && cp->separate == 0);
2493       *lp++ = ')';
2494
2495       put_number (x);
2496
2497       if (y != last_y)
2498         {
2499           *lp++ = ' ';
2500           put_number (YT (y));
2501           *lp++ = ' ';
2502           *lp++ = 'S';
2503           last_y = y;
2504         }
2505       else
2506         {
2507           *lp++ = ' ';
2508           *lp++ = 'T';
2509         }
2510
2511       if (lp >= &line[70])
2512         output_line ();
2513     }
2514   if (lp != line)
2515     output_line ();
2516 }
2517
2518 #undef output_line
2519 #undef put_number
2520
2521 /* Displays the text in outp_text T, if DRAW is nonzero; or, merely
2522    determine the text metrics, if DRAW is zero. */
2523 static void
2524 text (struct outp_driver *this, struct outp_text *t, int draw)
2525 {
2526   struct ps_driver_ext *ext = this->ext;
2527
2528   /* Output. */
2529   struct output_char *buf;      /* Output buffer. */
2530   struct output_char *buf_end;  /* End of output buffer. */
2531   struct output_char *buf_loc;  /* Current location in output buffer. */
2532
2533   /* Saved state. */
2534   struct font_entry *old_current = ext->current;
2535   char *old_family = xstrdup (ext->family); /* FIXME */
2536   int old_size = ext->size;
2537
2538   /* Input string. */
2539   char *cp, *end;
2540
2541   /* Current location. */
2542   int x, y;
2543
2544   /* Keeping track of what's left over. */
2545   int width;                    /* Width available for characters. */
2546   int width_left, height_left;  /* Width, height left over. */
2547   int max_height;               /* Tallest character on this line so far. */
2548
2549   /* Previous character. */
2550   int prev_char;
2551
2552   /* Information about location of previous space. */
2553   char *space_char;             /* Character after space. */
2554   struct output_char *space_buf_loc; /* Buffer location after space. */
2555   int space_width_left;         /* Width of characters before space. */
2556
2557   /* Name of the current character. */
2558   const char *char_name;
2559   char local_char_name[2] = {0, 0};
2560
2561   local_char_name[0] = local_char_name[1] = 0;
2562
2563   buf = local_alloc (sizeof *buf * 128);
2564   buf_end = &buf[128];
2565   buf_loc = buf;
2566
2567   assert (!ls_null_p (&t->s));
2568   cp = ls_value (&t->s);
2569   end = ls_end (&t->s);
2570   if (draw)
2571     {
2572       x = t->x;
2573       y = t->y;
2574     }
2575   width = width_left = (t->options & OUTP_T_HORZ) ? t->h : INT_MAX;
2576   height_left = (t->options & OUTP_T_VERT) ? t->v : INT_MAX;
2577   max_height = 0;
2578   prev_char = -1;
2579   space_char = NULL;
2580
2581   if (!width || !height_left)
2582     goto exit;
2583
2584   while (cp < end)
2585     {
2586       struct char_metrics *metric;
2587       int cur_char;
2588       int kern_amt;
2589       int char_width;
2590       int separate = 0;
2591
2592       /* Set char_name to the name of the character or ligature at
2593          *cp. */
2594       if (ext->current->font->ligatures && *cp == 'f')
2595         {
2596           int lig = 0;
2597
2598           if (cp < end - 1)
2599             switch (cp[1])
2600               {
2601               case 'i':
2602                 lig = LIG_fi, char_name = "fi";
2603                 break;
2604               case 'l':
2605                 lig = LIG_fl, char_name = "fl";
2606                 break;
2607               case 'f':
2608                 if (cp < end - 2)
2609                   switch (cp[2])
2610                     {
2611                     case 'i':
2612                       lig = LIG_ffi, char_name = "ffi";
2613                       goto got_ligature;
2614                     case 'l':
2615                       lig = LIG_ffl, char_name = "ffl";
2616                       goto got_ligature;
2617                     }
2618                 lig = LIG_ff, char_name = "ff";
2619               got_ligature:
2620                 break;
2621               }
2622           if ((lig & ext->current->font->ligatures) == 0)
2623             {
2624               local_char_name[0] = *cp++;       /* 'f' */
2625               char_name = local_char_name;
2626             }
2627           else
2628             cp += strlen (char_name);
2629         }
2630       else if (*cp == '\n')
2631         {
2632           if (draw)
2633             {
2634               write_text (this, buf, buf_loc, t, width, width_left);
2635               buf_loc = buf;
2636               x = t->x;
2637               y += max_height;
2638             }
2639
2640           width_left = width;
2641           height_left -= max_height;
2642           max_height = 0;
2643           kern_amt = 0;
2644           separate = 1;
2645           cp++;
2646
2647           /* FIXME: when we're page buffering it will be necessary to
2648              set separate to 1. */
2649           continue;
2650         }
2651       else
2652         {
2653           local_char_name[0] = *cp++;
2654           char_name = local_char_name;
2655         }
2656
2657       /* Figure out what size this character is, and what kern
2658          adjustment we need. */
2659       cur_char = font_char_name_to_index (char_name);
2660       metric = font_get_char_metrics (ext->current->font, cur_char);
2661       if (!metric)
2662         {
2663           static struct char_metrics m;
2664           metric = &m;
2665           m.width = ext->current->font->space_width;
2666           m.code = *char_name;
2667         }
2668       kern_amt = font_get_kern_adjust (ext->current->font, prev_char,
2669                                        cur_char);
2670       if (kern_amt)
2671         {
2672           kern_amt = (kern_amt * ext->size / 1000);
2673           separate = 1;
2674         }
2675       char_width = metric->width * ext->size / 1000;
2676
2677       /* Record the current status if this is a space character. */
2678       if (cur_char == space_index && buf_loc > buf)
2679         {
2680           space_char = cp;
2681           space_buf_loc = buf_loc;
2682           space_width_left = width_left;
2683         }
2684
2685       /* Drop down to a new line if there's no room left on this
2686          line. */
2687       if (char_width + kern_amt > width_left)
2688         {
2689           /* Regress to previous space, if any. */
2690           if (space_char)
2691             {
2692               cp = space_char;
2693               width_left = space_width_left;
2694               buf_loc = space_buf_loc;
2695             }
2696
2697           if (draw)
2698             {
2699               write_text (this, buf, buf_loc, t, width, width_left);
2700               buf_loc = buf;
2701               x = t->x;
2702               y += max_height;
2703             }
2704
2705           width_left = width;
2706           height_left -= max_height;
2707           max_height = 0;
2708           kern_amt = 0;
2709
2710           if (space_char)
2711             {
2712               space_char = NULL;
2713               prev_char = -1;
2714               /* FIXME: when we're page buffering it will be
2715                  necessary to set separate to 1. */
2716               continue;
2717             }
2718           separate = 1;
2719         }
2720       if (ext->size > max_height)
2721         max_height = ext->size;
2722       if (max_height > height_left)
2723         goto exit;
2724
2725       /* Actually draw the character. */
2726       if (draw)
2727         {
2728           if (buf_loc >= buf_end)
2729             {
2730               int buf_len = buf_end - buf;
2731
2732               if (buf_len == 128)
2733                 {
2734                   struct output_char *new_buf;
2735
2736                   new_buf = xmalloc (sizeof *new_buf * 256);
2737                   memcpy (new_buf, buf, sizeof *new_buf * 128);
2738                   buf_loc = new_buf + 128;
2739                   buf_end = new_buf + 256;
2740                   local_free (buf);
2741                   buf = new_buf;
2742                 }
2743               else
2744                 {
2745                   buf = xrealloc (buf, sizeof *buf * buf_len * 2);
2746                   buf_loc = buf + buf_len;
2747                   buf_end = buf + buf_len * 2;
2748                 }
2749             }
2750
2751           x += kern_amt;
2752           buf_loc->font = ext->current;
2753           buf_loc->size = ext->size;
2754           buf_loc->x = x;
2755           buf_loc->y = y;
2756           buf_loc->ch = metric->code;
2757           buf_loc->separate = separate;
2758           buf_loc++;
2759           x += char_width;
2760         }
2761
2762       /* Prepare for next iteration. */
2763       width_left -= char_width + kern_amt;
2764       prev_char = cur_char;
2765     }
2766   height_left -= max_height;
2767   if (buf_loc > buf && draw)
2768     write_text (this, buf, buf_loc, t, width, width_left);
2769
2770 exit:
2771   if (!(t->options & OUTP_T_HORZ))
2772     t->h = INT_MAX - width_left;
2773   if (!(t->options & OUTP_T_VERT))
2774     t->v = INT_MAX - height_left;
2775   else
2776     t->v -= height_left;
2777   if (buf_end - buf == 128)
2778     local_free (buf);
2779   else
2780     free (buf);
2781   ext->current = old_current;
2782   free (ext->family);
2783   ext->family = old_family;
2784   ext->size = old_size;
2785 }
2786
2787 void
2788 ps_text_metrics (struct outp_driver *this, struct outp_text *t)
2789 {
2790   assert (this->driver_open && this->page_open);
2791   text (this, t, 0);
2792 }
2793
2794 void
2795 ps_text_draw (struct outp_driver *this, struct outp_text *t)
2796 {
2797   assert (this->driver_open && this->page_open);
2798   text (this, t, 1);
2799 }
2800 \f
2801 /* Font loader. */
2802
2803 /* Translate a filename to a font. */
2804 struct filename2font
2805   {
2806     char *filename;             /* Normalized filename. */
2807     struct font_desc *font;
2808   };
2809
2810 /* Table of `filename2font's. */
2811 static struct hsh_table *ps_fonts;
2812
2813 /* Hash table comparison function for filename2font structs. */
2814 static int
2815 compare_filename2font (const void *a, const void *b, void *param unused)
2816 {
2817   return strcmp (((struct filename2font *) a)->filename,
2818                  ((struct filename2font *) b)->filename);
2819 }
2820
2821 /* Hash table hash function for filename2font structs. */
2822 static unsigned
2823 hash_filename2font (const void *f2f_, void *param unused)
2824 {
2825   const struct filename2font *f2f = f2f_;
2826   return hsh_hash_string (f2f->filename);
2827 }
2828
2829 /* Initializes the global font list by creating the hash table for
2830    translation of filenames to font_desc structs. */
2831 static void
2832 init_fonts (void)
2833 {
2834   ps_fonts = hsh_create (31, compare_filename2font, hash_filename2font,
2835                          NULL, NULL);
2836 }
2837
2838 /* Loads the font having Groff name DIT into THIS driver instance.
2839    Specifically, adds it into the THIS driver's `loaded' hash
2840    table. */
2841 static struct font_entry *
2842 load_font (struct outp_driver *this, const char *dit)
2843 {
2844   struct ps_driver_ext *x = this->ext;
2845   char *filename1, *filename2;
2846   void **entry;
2847   struct font_entry *fe;
2848
2849   filename1 = find_ps_file (this, dit);
2850   if (!filename1)
2851     filename1 = xstrdup (dit);
2852   filename2 = fn_normalize (filename1);
2853   free (filename1);
2854
2855   entry = hsh_probe (ps_fonts, &filename2);
2856   if (*entry == NULL)
2857     {
2858       struct filename2font *f2f;
2859       struct font_desc *f = groff_read_font (filename2);
2860
2861       if (f == NULL)
2862         {
2863           if (x->fixed)
2864             f = x->fixed->font;
2865           else
2866             f = default_font ();
2867         }
2868       
2869       f2f = xmalloc (sizeof *f2f);
2870       f2f->filename = filename2;
2871       f2f->font = f;
2872       *entry = f2f;
2873     }
2874   else
2875     free (filename2);
2876
2877   fe = xmalloc (sizeof *fe);
2878   fe->dit = xstrdup (dit);
2879   fe->font = ((struct filename2font *) * entry)->font;
2880   *hsh_probe (x->loaded, &dit) = fe;
2881
2882   return fe;
2883 }
2884
2885 /* PostScript driver class. */
2886 struct outp_class postscript_class =
2887 {
2888   "postscript",
2889   MAGIC_PS,
2890   0,
2891
2892   ps_open_global,
2893   ps_close_global,
2894   ps_font_sizes,
2895
2896   ps_preopen_driver,
2897   ps_option,
2898   ps_postopen_driver,
2899   ps_close_driver,
2900
2901   ps_open_page,
2902   ps_close_page,
2903
2904   NULL,
2905
2906   ps_line_horz,
2907   ps_line_vert,
2908   ps_line_intersection,
2909
2910   ps_box,
2911   ps_polyline_begin,
2912   ps_polyline_point,
2913   ps_polyline_end,
2914
2915   ps_text_set_font_by_name,
2916   ps_text_set_font_by_position,
2917   ps_text_set_font_family,
2918   ps_text_get_font_name,
2919   ps_text_get_font_family,
2920   ps_text_set_size,
2921   ps_text_get_size,
2922   ps_text_metrics,
2923   ps_text_draw,
2924 };
2925
2926 /* EPSF driver class.  FIXME: Probably doesn't work right. */
2927 struct outp_class epsf_class =
2928 {
2929   "epsf",
2930   MAGIC_EPSF,
2931   0,
2932
2933   ps_open_global,
2934   ps_close_global,
2935   ps_font_sizes,
2936
2937   ps_preopen_driver,
2938   ps_option,
2939   ps_postopen_driver,
2940   ps_close_driver,
2941
2942   ps_open_page,
2943   ps_close_page,
2944
2945   NULL,
2946
2947   ps_line_horz,
2948   ps_line_vert,
2949   ps_line_intersection,
2950
2951   ps_box,
2952   ps_polyline_begin,
2953   ps_polyline_point,
2954   ps_polyline_end,
2955
2956   ps_text_set_font_by_name,
2957   ps_text_set_font_by_position,
2958   ps_text_set_font_family,
2959   ps_text_get_font_name,
2960   ps_text_get_font_family,
2961   ps_text_set_size,
2962   ps_text_get_size,
2963   ps_text_metrics,
2964   ps_text_draw,
2965 };
2966
2967 #endif /* NO_POSTSCRIPT */