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