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