output: Refactor implementation of charts.
[pspp-builds.git] / src / output / postscript.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <ctype.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <time.h>
25 #include <unistd.h>
26
27 #include <data/file-name.h>
28 #include <libpspp/assertion.h>
29 #include <libpspp/bit-vector.h>
30 #include <libpspp/compiler.h>
31 #include <libpspp/freaderror.h>
32 #include <libpspp/hash.h>
33 #include <libpspp/misc.h>
34 #include <libpspp/start-date.h>
35 #include <libpspp/version.h>
36 #include <output/afm.h>
37 #include <output/chart-provider.h>
38 #include <output/chart.h>
39 #include <output/manager.h>
40 #include <output/output.h>
41
42 #include "error.h"
43 #include "intprops.h"
44 #include "minmax.h"
45 #include "xalloc.h"
46
47 #include "gettext.h"
48 #define _(msgid) gettext (msgid)
49
50 /* PostScript driver options: (defaults listed first)
51
52    output-file="pspp.ps"
53
54    paper-size=letter (see "papersize" file)
55    orientation=portrait|landscape
56    headers=on|off
57
58    left-margin=0.5in
59    right-margin=0.5in
60    top-margin=0.5in
61    bottom-margin=0.5in
62
63    prop-font=Times-Roman
64    emph-font=Times-Italic
65    fixed-font=Courier
66    font-size=10000
67
68    line-gutter=1pt
69    line-spacing=1pt
70    line-width=0.5pt
71  */
72
73 /* The number of `psus' (PostScript driver UnitS) per inch. */
74 #define PSUS 72000
75
76 /* A PostScript font. */
77 struct font
78   {
79     struct afm *metrics;        /* Metrics. */
80     char *embed_fn;             /* Name of file to embed. */
81     char *encoding_fn;          /* Name of file with encoding. */
82   };
83
84 /* PostScript output driver extension record. */
85 struct ps_driver_ext
86   {
87     char *file_name;            /* Output file name. */
88     FILE *file;                 /* Output file. */
89
90     bool draw_headers;          /* Draw headers at top of page? */
91     int page_number;            /* Current page number. */
92
93     bool portrait;              /* Portrait mode? */
94     int paper_width;            /* Width of paper before dropping margins. */
95     int paper_length;           /* Length of paper before dropping margins. */
96     int left_margin;            /* Left margin in psus. */
97     int right_margin;           /* Right margin in psus. */
98     int top_margin;             /* Top margin in psus. */
99     int bottom_margin;          /* Bottom margin in psus. */
100
101     int line_gutter;            /* Space around lines. */
102     int line_space;             /* Space between lines. */
103     int line_width;             /* Width of lines. */
104
105     struct font *fonts[OUTP_FONT_CNT];
106     int last_font;              /* Index of last font set with setfont. */
107
108     int doc_num;                /* %%DocumentNumber counter. */
109   };
110
111 /* Transform logical y-ordinate Y into a page ordinate. */
112 #define YT(Y) (this->length - (Y))
113
114 static bool handle_option (void *this, const char *key,
115                            const struct string *val);
116 static void draw_headers (struct outp_driver *this);
117
118 static void write_ps_prologue (struct outp_driver *);
119
120 static char *quote_ps_name (const char *string);
121
122 static struct font *load_font (const char *string);
123 static void free_font (struct font *);
124 static void setup_font (struct outp_driver *this, struct font *, int index);
125 \f
126 /* Driver initialization. */
127
128 static bool
129 ps_open_driver (const char *name, int types, struct substring options)
130 {
131   struct outp_driver *this;
132   struct ps_driver_ext *x;
133   size_t i;
134
135   this = outp_allocate_driver (&postscript_class, name, types);
136   this->width = this->length = 0;
137   this->font_height = PSUS * 10 / 72;
138
139   this->ext = x = xmalloc (sizeof *x);
140   x->file_name = xstrdup ("pspp.ps");
141   x->file = NULL;
142   x->draw_headers = true;
143   x->page_number = 0;
144   x->portrait = true;
145   outp_get_paper_size ("", &x->paper_width, &x->paper_length);
146   x->left_margin = PSUS / 2;
147   x->right_margin = PSUS / 2;
148   x->top_margin = PSUS / 2;
149   x->bottom_margin = PSUS / 2;
150   x->line_gutter = PSUS / 72;
151   x->line_space = PSUS / 72;
152   x->line_width = PSUS / 144;
153   for (i = 0; i < OUTP_FONT_CNT; i++)
154     x->fonts[i] = NULL;
155   x->doc_num = 0;
156
157   outp_parse_options (this->name, options, handle_option, this);
158
159   x->file = fn_open (x->file_name, "w");
160   if (x->file == NULL)
161     {
162       error (0, errno, _("opening PostScript output file \"%s\""),
163              x->file_name);
164       goto error;
165     }
166
167   if (x->portrait)
168     {
169       this->width = x->paper_width;
170       this->length = x->paper_length;
171     }
172   else
173     {
174       this->width = x->paper_length;
175       this->length = x->paper_width;
176     }
177   this->width -= x->left_margin + x->right_margin;
178   this->length -= x->top_margin + x->bottom_margin;
179   if (x->draw_headers)
180     {
181       int header_length = 3 * this->font_height;
182       this->length -= header_length;
183       x->top_margin += header_length;
184     }
185
186   for (i = 0; i < OUTP_FONT_CNT; i++)
187     if (x->fonts[i] == NULL)
188       {
189         const char *default_fonts[OUTP_FONT_CNT];
190         default_fonts[OUTP_FIXED] = "Courier.afm";
191         default_fonts[OUTP_PROPORTIONAL] = "Times-Roman.afm";
192         default_fonts[OUTP_EMPHASIS] = "Times-Italic.afm";
193         x->fonts[i] = load_font (default_fonts[i]);
194         if (x->fonts[i] == NULL)
195           goto error;
196       }
197
198   if (this->length / this->font_height < 15)
199     {
200       error (0, 0, _("The defined PostScript page is not long "
201                      "enough to hold margins and headers, plus least 15 "
202                      "lines of the default fonts.  In fact, there's only "
203                      "room for %d lines of each font at the default size "
204                      "of %d.%03d points."),
205            this->length / this->font_height,
206            this->font_height / 1000, this->font_height % 1000);
207       goto error;
208     }
209
210   this->fixed_width =
211     afm_get_character (x->fonts[OUTP_FIXED]->metrics, '0')->width
212     * this->font_height / 1000;
213   this->prop_em_width =
214     afm_get_character (x->fonts[OUTP_PROPORTIONAL]->metrics, '0')->width
215     * this->font_height / 1000;
216
217   this->horiz_line_width[OUTP_L_NONE] = 0;
218   this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
219   this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
220                                            + 2 * x->line_width);
221   memcpy (this->vert_line_width, this->horiz_line_width,
222           sizeof this->vert_line_width);
223
224   write_ps_prologue (this);
225
226   outp_register_driver (this);
227   return true;
228
229  error:
230   this->class->close_driver (this);
231   outp_free_driver (this);
232   return false;
233 }
234
235 static bool
236 ps_close_driver (struct outp_driver *this)
237 {
238   struct ps_driver_ext *x = this->ext;
239   bool ok = true;
240   size_t i;
241
242   if (x->file != NULL)
243     {
244       fprintf (x->file,
245                "%%%%Trailer\n"
246                "%%%%Pages: %d\n"
247                "%%%%EOF\n",
248                x->page_number);
249
250       ok = fn_close (x->file_name, x->file) == 0;
251       if (!ok)
252         error (0, errno, _("closing PostScript output file \"%s\""),
253                x->file_name);
254     }
255
256   free (x->file_name);
257   for (i = 0; i < OUTP_FONT_CNT; i++)
258     free_font (x->fonts[i]);
259   free (x);
260
261   return ok;
262 }
263
264 /* Generic option types. */
265 enum
266 {
267   output_file_arg,
268   paper_size_arg,
269   orientation_arg,
270   line_style_arg,
271   boolean_arg,
272   pos_int_arg,
273   dimension_arg,
274   string_arg,
275   nonneg_int_arg
276 };
277
278 /* All the options that the PostScript driver supports. */
279 static const struct outp_option option_tab[] =
280 {
281   {"output-file",               output_file_arg,0},
282   {"paper-size",                paper_size_arg, 0},
283   {"orientation",               orientation_arg,0},
284
285   {"headers",                   boolean_arg,    1},
286
287   {"prop-font",                 string_arg,     OUTP_PROPORTIONAL},
288   {"emph-font",                 string_arg,     OUTP_EMPHASIS},
289   {"fixed-font",                string_arg,     OUTP_FIXED},
290
291   {"left-margin",               pos_int_arg,    0},
292   {"right-margin",              pos_int_arg,    1},
293   {"top-margin",                pos_int_arg,    2},
294   {"bottom-margin",             pos_int_arg,    3},
295   {"font-size",                 pos_int_arg,    4},
296
297   {"line-width",                dimension_arg,  0},
298   {"line-gutter",               dimension_arg,  1},
299   {"line-width",                dimension_arg,  2},
300   {NULL, 0, 0},
301 };
302
303 static bool
304 handle_option (void *this_, const char *key,
305                const struct string *val)
306 {
307   struct outp_driver *this = this_;
308   struct ps_driver_ext *x = this->ext;
309   int subcat;
310   char *value = ds_cstr (val);
311
312   switch (outp_match_keyword (key, option_tab, &subcat))
313     {
314     case -1:
315       error (0, 0,
316              _("unknown configuration parameter `%s' for PostScript device "
317                "driver"), key);
318       break;
319     case output_file_arg:
320       free (x->file_name);
321       x->file_name = xstrdup (value);
322       break;
323     case paper_size_arg:
324       outp_get_paper_size (value, &this->width, &this->length);
325       break;
326     case orientation_arg:
327       if (!strcmp (value, "portrait"))
328         x->portrait = true;
329       else if (!strcmp (value, "landscape"))
330         x->portrait = false;
331       else
332         error (0, 0, _("unknown orientation `%s' (valid orientations are "
333                        "`portrait' and `landscape')"), value);
334       break;
335     case boolean_arg:
336       if (!strcmp (value, "on") || !strcmp (value, "true")
337           || !strcmp (value, "yes") || atoi (value))
338         x->draw_headers = true;
339       else if (!strcmp (value, "off") || !strcmp (value, "false")
340                || !strcmp (value, "no") || !strcmp (value, "0"))
341         x->draw_headers = false;
342       else
343         {
344           error (0, 0, _("boolean value expected for %s"), key);
345           return false;
346         }
347       break;
348     case pos_int_arg:
349       {
350         char *tail;
351         int arg;
352
353         errno = 0;
354         arg = strtol (value, &tail, 0);
355         if (arg < 1 || errno == ERANGE || *tail)
356           {
357             error (0, 0, _("positive integer value required for `%s'"), key);
358             break;
359           }
360         if ((subcat == 4 || subcat == 5) && arg < 1000)
361           {
362             error (0, 0, _("default font size must be at least 1 point (value "
363                            "of 1000 for key `%s')"), key);
364             break;
365           }
366         switch (subcat)
367           {
368           case 0:
369             x->left_margin = arg;
370             break;
371           case 1:
372             x->right_margin = arg;
373             break;
374           case 2:
375             x->top_margin = arg;
376             break;
377           case 3:
378             x->bottom_margin = arg;
379             break;
380           case 4:
381             this->font_height = arg;
382             break;
383           default:
384             NOT_REACHED ();
385           }
386       }
387       break;
388     case dimension_arg:
389       {
390         int dimension = outp_evaluate_dimension (value);
391
392         if (dimension <= 0)
393           break;
394         switch (subcat)
395           {
396           case 0:
397             x->line_width = dimension;
398             break;
399           case 1:
400             x->line_gutter = dimension;
401             break;
402           case 2:
403             x->line_width = dimension;
404             break;
405           default:
406             NOT_REACHED ();
407           }
408       }
409       break;
410     case string_arg:
411       {
412         struct font *font = load_font (value);
413         if (font != NULL)
414           {
415             struct font **dst = &x->fonts[subcat];
416             if (*dst != NULL)
417               free_font (*dst);
418             *dst = font;
419           }
420       }
421       break;
422     default:
423       NOT_REACHED ();
424     }
425
426   return true;
427 }
428
429 /* Looks for a PostScript font file or config file in all the
430    appropriate places.  Returns the file name on success, NULL on
431    failure. */
432 static char *
433 find_ps_file (const char *name)
434 {
435   if (fn_is_absolute (name))
436     return xstrdup (name);
437   else
438     {
439       char *base_name = xasprintf ("psfonts/%s", name);
440       char *file_name = fn_search_path (base_name, config_path);
441       free (base_name);
442       return file_name;
443     }
444 }
445 \f
446 /* Basic file operations. */
447
448 /* Writes the PostScript prologue to file F. */
449 static void
450 write_ps_prologue (struct outp_driver *this)
451 {
452   struct ps_driver_ext *x = this->ext;
453   size_t embedded_cnt, preloaded_cnt;
454   size_t i;
455
456   fputs ("%!PS-Adobe-3.0\n", x->file);
457   fputs ("%%Pages: (atend)\n", x->file);
458
459   embedded_cnt = preloaded_cnt = 0;
460   for (i = 0; i < OUTP_FONT_CNT; i++)
461     {
462       bool embed = x->fonts[i]->embed_fn != NULL;
463       embedded_cnt += embed;
464       preloaded_cnt += !embed;
465     }
466   if (preloaded_cnt > 0)
467     {
468       fputs ("%%DocumentNeededResources: font", x->file);
469       for (i = 0; i < OUTP_FONT_CNT; i++)
470         {
471           struct font *f = x->fonts[i];
472           if (f->embed_fn == NULL)
473             fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
474         }
475       fputs ("\n", x->file);
476     }
477   if (embedded_cnt > 0)
478     {
479       fputs ("%%DocumentSuppliedResources: font", x->file);
480       for (i = 0; i < OUTP_FONT_CNT; i++)
481         {
482           struct font *f = x->fonts[i];
483           if (f->embed_fn != NULL)
484             fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
485         }
486       fputs ("\n", x->file);
487     }
488   fputs ("%%Copyright: This prologue is public domain.\n", x->file);
489   fprintf (x->file, "%%%%Creator: %s\n", version);
490   fprintf (x->file, "%%%%DocumentMedia: Plain %g %g 75 white ()\n",
491            x->paper_width / (PSUS / 72.0), x->paper_length / (PSUS / 72.0));
492   fprintf (x->file, "%%%%Orientation: %s\n",
493            x->portrait ? "Portrait" : "Landscape");
494   fputs ("%%EndComments\n", x->file);
495   fputs ("%%BeginDefaults\n", x->file);
496   fputs ("%%PageResources: font", x->file);
497   for (i = 0; i < OUTP_FONT_CNT; i++)
498     fprintf (x->file, " %s", afm_get_findfont_name (x->fonts[i]->metrics));
499   fputs ("\n", x->file);
500   fputs ("%%EndDefaults\n", x->file);
501   fputs ("%%BeginProlog\n", x->file);
502   fputs ("/ED{exch def}bind def\n", x->file);
503   fputs ("/L{moveto lineto stroke}bind def\n", x->file);
504   fputs ("/D{moveto lineto moveto lineto stroke}bind def\n", x->file);
505   fputs ("/S{show}bind def\n", x->file);
506   fputs ("/GS{glyphshow}def\n", x->file);
507   fputs ("/RF{\n", x->file);
508   fputs (" exch dup maxlength 1 add dict begin\n", x->file);
509   fputs (" {\n", x->file);
510   fputs ("  1 index/FID ne{def}{pop pop}ifelse\n", x->file);
511   fputs (" }forall\n", x->file);
512   fputs (" /Encoding ED\n", x->file);
513   fputs (" currentdict end\n", x->file);
514   fputs ("}bind def\n", x->file);
515   fputs ("/F{setfont}bind def\n", x->file);
516   fputs ("/EP{\n", x->file);
517   fputs (" pg restore\n", x->file);
518   fputs (" showpage\n", x->file);
519   fputs ("}bind def\n", x->file);
520   fputs ("/GB{\n", x->file);
521   fputs (" /y2 ED/x2 ED/y1 ED/x1 ED\n", x->file);
522   fputs (" x1 y1 moveto x2 y1 lineto x2 y2 lineto x1 y2 lineto closepath\n",
523          x->file);
524   fputs (" gsave 0.9 setgray fill grestore stroke\n", x->file);
525   fputs ("}bind def\n", x->file);
526   fputs ("/K{0 rmoveto}bind def\n", x->file);
527   fputs ("%%EndProlog\n", x->file);
528   fputs ("%%BeginSetup\n", x->file);
529   for (i = 0; i < OUTP_FONT_CNT; i++)
530     setup_font (this, x->fonts[i], i);
531   fputs ("%%EndSetup\n", x->file);
532 }
533
534 /* Returns STRING as a Postscript name, which is just '/'
535    followed by STRING unless characters need to be quoted.
536    The caller must free the string. */
537 static char *
538 quote_ps_name (const char *string)
539 {
540   const char *cp;
541
542   for (cp = string; *cp != '\0'; cp++)
543     {
544       unsigned char c = *cp;
545       if (!isalpha (c) && strchr ("^_|!$&:;.,-+", c) == NULL
546           && (cp == string || !isdigit (c)))
547         {
548           struct string out = DS_EMPTY_INITIALIZER;
549           ds_put_char (&out, '<');
550           for (cp = string; *cp != '\0'; cp++)
551             {
552               c = *cp;
553               ds_put_format (&out, "%02x", c);
554             }
555           ds_put_cstr (&out, ">cvn");
556           return ds_cstr (&out);
557         }
558     }
559   return xasprintf ("/%s", string);
560 }
561
562 static void
563 ps_open_page (struct outp_driver *this)
564 {
565   struct ps_driver_ext *x = this->ext;
566
567   /* Assure page independence. */
568   x->last_font = -1;
569
570   x->page_number++;
571
572   fprintf (x->file,
573            "%%%%Page: %d %d\n"
574            "%%%%BeginPageSetup\n"
575            "/pg save def 0.001 dup scale\n",
576            x->page_number, x->page_number);
577
578   if (!x->portrait)
579     fprintf (x->file,
580              "%d 0 translate 90 rotate\n",
581              x->paper_width);
582
583   if (x->bottom_margin != 0 || x->left_margin != 0)
584     fprintf (x->file,
585              "%d %d translate\n",
586              x->left_margin, x->bottom_margin);
587
588   fprintf (x->file,
589            "/LW %d def %d setlinewidth\n"
590            "%%%%EndPageSetup\n",
591            x->line_width, x->line_width);
592
593   if (x->draw_headers)
594     draw_headers (this);
595 }
596
597 static void
598 ps_close_page (struct outp_driver *this)
599 {
600   struct ps_driver_ext *x = this->ext;
601   fputs ("%%PageTrailer\n"
602          "EP\n",
603          x->file);
604 }
605
606 static void
607 ps_output_chart (struct outp_driver *this, const struct chart *chart)
608 {
609   struct ps_driver_ext *x = this->ext;
610   plPlotterParams *params;
611   int x_origin, y_origin;
612   char buf[BUFSIZ];
613   char *page_size;
614   plPlotter *lp;
615   FILE *file;
616   int size;
617
618   /* Create temporary file for chart. */
619   file = tmpfile ();
620   if (file == NULL)
621     {
622       error (0, errno, _("failed to create temporary file"));
623       return;
624     }
625
626   /* Create plotter for chart. */
627   size = this->width < this->length ? this->width : this->length;
628   x_origin = x->left_margin + (size - this->width) / 2;
629   y_origin = x->bottom_margin + (size - this->length) / 2;
630   page_size = xasprintf ("a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
631                          (double) size / PSUS, (double) size / PSUS,
632                          (double) x_origin / PSUS, (double) y_origin / PSUS);
633
634   params = pl_newplparams ();
635   pl_setplparam (params, "PAGESIZE", page_size);
636   free (page_size);
637   lp = pl_newpl_r ("ps", 0, file, stderr, params);
638   pl_deleteplparams (params);
639
640   if (lp == NULL)
641     {
642       fclose (file);
643       return;
644     }
645
646   /* Draw chart and free plotter. */
647   chart_draw (chart, lp);
648   pl_deletepl_r (lp);
649
650   /* Write prologue for chart. */
651   outp_eject_page (this);
652   fprintf (x->file,
653            "/sp save def\n"
654            "%d %d translate 1000 dup scale\n"
655            "userdict begin\n"
656            "/showpage { } def\n"
657            "0 setgray 0 setlinecap 1 setlinewidth\n"
658            "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
659            "%%%%BeginDocument: %d\n",
660            -x->left_margin, -x->bottom_margin,
661            x->doc_num++);
662
663   /* Copy chart into output file. */
664   rewind (file);
665   while (fwrite (buf, 1, fread (buf, 1, sizeof buf, file), x->file))
666     continue;
667   fclose (file);
668
669   /* Write epilogue for chart. */
670   fputs ("%%EndDocument\n"
671          "end\n"
672          "sp restore\n",
673          x->file);
674   outp_close_page (this);
675 }
676
677 static void
678 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
679 {
680   switch (s->type)
681     {
682     default:
683       NOT_REACHED ();
684     }
685 }
686 \f
687 /* Draws a line from (x0,y0) to (x1,y1). */
688 static void
689 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
690 {
691   struct ps_driver_ext *ext = this->ext;
692   fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
693 }
694
695 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
696    shortening it to X0...X1 if SHORTEN is true.
697    Draws a horizontal line X1...X3 at Y if RIGHT says so,
698    shortening it to X2...X3 if SHORTEN is true. */
699 static void
700 horz_line (struct outp_driver *this,
701            int x0, int x1, int x2, int x3, int y,
702            enum outp_line_style left, enum outp_line_style right,
703            bool shorten)
704 {
705   if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
706     dump_line (this, x0, y, x3, y);
707   else
708     {
709       if (left != OUTP_L_NONE)
710         dump_line (this, x0, y, shorten ? x1 : x2, y);
711       if (right != OUTP_L_NONE)
712         dump_line (this, shorten ? x2 : x1, y, x3, y);
713     }
714 }
715
716 /* Draws a vertical line Y0...Y2 at X if TOP says so,
717    shortening it to Y0...Y1 if SHORTEN is true.
718    Draws a vertical line Y1...Y3 at X if BOTTOM says so,
719    shortening it to Y2...Y3 if SHORTEN is true. */
720 static void
721 vert_line (struct outp_driver *this,
722            int y0, int y1, int y2, int y3, int x,
723            enum outp_line_style top, enum outp_line_style bottom,
724            bool shorten)
725 {
726   if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
727     dump_line (this, x, y0, x, y3);
728   else
729     {
730       if (top != OUTP_L_NONE)
731         dump_line (this, x, y0, x, shorten ? y1 : y2);
732       if (bottom != OUTP_L_NONE)
733         dump_line (this, x, shorten ? y2 : y1, x, y3);
734     }
735 }
736
737 /* Draws a generalized intersection of lines in the rectangle
738    (X0,Y0)-(X3,Y3).  The line coming from the top to the center
739    is of style TOP, from left to center of style LEFT, from
740    bottom to center of style BOTTOM, and from right to center of
741    style RIGHT. */
742 static void
743 ps_line (struct outp_driver *this,
744          int x0, int y0, int x3, int y3,
745          enum outp_line_style top, enum outp_line_style left,
746          enum outp_line_style bottom, enum outp_line_style right)
747 {
748   /* The algorithm here is somewhat subtle, to allow it to handle
749      all the kinds of intersections that we need.
750
751      Three additional ordinates are assigned along the x axis.  The
752      first is xc, midway between x0 and x3.  The others are x1 and
753      x2; for a single vertical line these are equal to xc, and for
754      a double vertical line they are the ordinates of the left and
755      right half of the double line.
756
757      yc, y1, and y2 are assigned similarly along the y axis.
758
759      The following diagram shows the coordinate system and output
760      for double top and bottom lines, single left line, and no
761      right line:
762
763                  x0       x1 xc  x2      x3
764                y0 ________________________
765                   |        #     #       |
766                   |        #     #       |
767                   |        #     #       |
768                   |        #     #       |
769                   |        #     #       |
770      y1 = y2 = yc |#########     #       |
771                   |        #     #       |
772                   |        #     #       |
773                   |        #     #       |
774                   |        #     #       |
775                y3 |________#_____#_______|
776   */
777   struct ps_driver_ext *ext = this->ext;
778
779   /* Offset from center of each line in a pair of double lines. */
780   int double_line_ofs = (ext->line_space + ext->line_width) / 2;
781
782   /* Are the lines along each axis single or double?
783      (It doesn't make sense to have different kinds of line on the
784      same axis, so we don't try to gracefully handle that case.) */
785   bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
786   bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
787
788   /* When horizontal lines are doubled,
789      the left-side line along y1 normally runs from x0 to x2,
790      and the right-side line along y1 from x3 to x1.
791      If the top-side line is also doubled, we shorten the y1 lines,
792      so that the left-side line runs only to x1,
793      and the right-side line only to x2.
794      Otherwise, the horizontal line at y = y1 below would cut off
795      the intersection, which looks ugly:
796                x0       x1     x2      x3
797              y0 ________________________
798                 |        #     #       |
799                 |        #     #       |
800                 |        #     #       |
801                 |        #     #       |
802              y1 |#########     ########|
803                 |                      |
804                 |                      |
805              y2 |######################|
806                 |                      |
807                 |                      |
808              y3 |______________________|
809      It is more of a judgment call when the horizontal line is
810      single.  We actually choose to cut off the line anyhow, as
811      shown in the first diagram above.
812   */
813   bool shorten_y1_lines = top == OUTP_L_DOUBLE;
814   bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
815   bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
816   int horz_line_ofs = double_vert ? double_line_ofs : 0;
817   int xc = (x0 + x3) / 2;
818   int x1 = xc - horz_line_ofs;
819   int x2 = xc + horz_line_ofs;
820
821   bool shorten_x1_lines = left == OUTP_L_DOUBLE;
822   bool shorten_x2_lines = right == OUTP_L_DOUBLE;
823   bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
824   int vert_line_ofs = double_horz ? double_line_ofs : 0;
825   int yc = (y0 + y3) / 2;
826   int y1 = yc - vert_line_ofs;
827   int y2 = yc + vert_line_ofs;
828
829   if (!double_horz)
830     horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
831   else
832     {
833       horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
834       horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
835     }
836
837   if (!double_vert)
838     vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
839   else
840     {
841       vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
842       vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
843     }
844 }
845
846 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
847    and with the given JUSTIFICATION for THIS driver. */
848 static int
849 draw_text (struct outp_driver *this,
850            const char *string, int x, int y, int max_width,
851            enum outp_justification justification)
852 {
853   struct outp_text text;
854   int width;
855
856   text.font = OUTP_PROPORTIONAL;
857   text.justification = justification;
858   text.string = ss_cstr (string);
859   text.h = max_width;
860   text.v = this->font_height;
861   text.x = x;
862   text.y = y;
863   this->class->text_metrics (this, &text, &width, NULL);
864   this->class->text_draw (this, &text);
865   return width;
866 }
867
868 /* Writes LEFT left-justified and RIGHT right-justified within
869    (X0...X1) at Y.  LEFT or RIGHT or both may be null. */
870 static void
871 draw_header_line (struct outp_driver *this,
872                   const char *left, const char *right,
873                   int x0, int x1, int y)
874 {
875   int right_width = 0;
876   if (right != NULL)
877     right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
878                    + this->prop_em_width);
879   if (left != NULL)
880     draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
881 }
882
883 /* Draw top of page headers for THIS driver. */
884 static void
885 draw_headers (struct outp_driver *this)
886 {
887   struct ps_driver_ext *ext = this->ext;
888   char *r1, *r2;
889   int x0, x1;
890   int y;
891
892   y = -3 * this->font_height;
893   x0 = this->prop_em_width;
894   x1 = this->width - this->prop_em_width;
895
896   /* Draw box. */
897   fprintf (ext->file, "%d %d %d %d GB\n",
898            0, YT (y),
899            this->width, YT (y + 2 * this->font_height + ext->line_gutter));
900   y += ext->line_width + ext->line_gutter;
901
902   r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
903   r2 = xasprintf ("%s - %s", version, host_system);
904
905   draw_header_line (this, outp_title, r1, x0, x1, y);
906   y += this->font_height;
907
908   draw_header_line (this, outp_subtitle, r2, x0, x1, y);
909
910   free (r1);
911   free (r2);
912 }
913 \f
914 /* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
915    given FONT.
916    The characters are justified according to JUSTIFICATION in a
917    field that has WIDTH_LEFT space remaining after the characters
918    themselves are accounted for.
919    Before character I is written, its x-position is adjusted by
920    KERNS[I]. */
921 static void
922 write_text (struct outp_driver *this,
923             int x0, int y0,
924             enum outp_font font,
925             enum outp_justification justification,
926             const struct afm_character **chars, int *kerns, size_t char_cnt,
927             int width_left)
928 {
929   struct ps_driver_ext *ext = this->ext;
930   struct afm *afm = ext->fonts[font]->metrics;
931   struct string out;
932   size_t i, j;
933
934   if (justification == OUTP_RIGHT)
935     x0 += width_left;
936   else if (justification == OUTP_CENTER)
937     x0 += width_left / 2;
938   y0 += afm_get_ascent (afm) * this->font_height / 1000;
939
940   fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
941
942   if (ext->last_font != font)
943     {
944       ext->last_font = font;
945       fprintf (ext->file, "F%d setfont\n", font);
946     }
947
948   ds_init_empty (&out);
949   for (i = 0; i < char_cnt; i = j)
950     {
951       for (j = i + 1; j < char_cnt; j++)
952         if (kerns[j] != 0)
953           break;
954
955       if (kerns[i] != 0)
956         fprintf (ext->file, "%d K", kerns[i]);
957       while (i < j)
958         {
959           size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
960           if (encoded > 0)
961             {
962               fprintf (ext->file, "%sS\n", ds_cstr (&out));
963               ds_clear (&out);
964               i += encoded;
965             }
966
967           if (i < j)
968             {
969               fprintf (ext->file, "/%s GS\n", chars[i]->name);
970               i++;
971             }
972         }
973     }
974   ds_destroy (&out);
975 }
976
977 /* State of a text formatting operation. */
978 struct text_state
979   {
980     /* Input. */
981     const struct outp_text *text;
982     bool draw;
983
984     /* Output. */
985     const struct afm_character **glyphs;
986     int *glyph_kerns;
987
988     /* State. */
989     size_t glyph_cnt;           /* Number of glyphs output. */
990     int width_left;             /* Width left over. */
991     int height_left;            /* Height left over. */
992
993     /* State as of last space. */
994     const char *space_char;     /* Just past last space. */
995     size_t space_glyph_cnt;     /* Number of glyphs as of last space. */
996     int space_width_left;       /* Width left over as of last space. */
997
998     /* Statistics. */
999     int max_width;             /* Widest line so far. */
1000   };
1001
1002 /* Adjusts S to complete a line of text,
1003    and draws the current line if appropriate. */
1004 static void
1005 finish_line (struct outp_driver *this, struct text_state *s)
1006 {
1007   int width;
1008
1009   if (s->draw)
1010     {
1011       write_text (this,
1012                   s->text->x, s->text->y + (s->text->v - s->height_left),
1013                   s->text->font,
1014                   s->text->justification,
1015                   s->glyphs, s->glyph_kerns, s->glyph_cnt,
1016                   s->width_left);
1017       s->glyph_cnt = 0;
1018     }
1019
1020   /* Update maximum width. */
1021   width = s->text->h - s->width_left;
1022   if (width > s->max_width)
1023     s->max_width = width;
1024
1025   /* Move to next line. */
1026   s->width_left = s->text->h;
1027   s->height_left -= this->font_height;
1028
1029   /* No spaces on this line yet. */
1030   s->space_char = NULL;
1031 }
1032
1033 /* Format TEXT on THIS driver.
1034    If DRAW is nonzero, draw the text.
1035    The width of the widest line is stored into *WIDTH, if WIDTH
1036    is nonnull.
1037    The total height of the text written is stored into *HEIGHT,
1038    if HEIGHT is nonnull. */
1039 static void
1040 text (struct outp_driver *this, const struct outp_text *text, bool draw,
1041       int *width, int *height)
1042 {
1043   struct ps_driver_ext *ext = this->ext;
1044   struct afm *afm = ext->fonts[text->font]->metrics;
1045   const char *cp;
1046   size_t glyph_cap;
1047   struct text_state s;
1048
1049   s.text = text;
1050   s.draw = draw;
1051
1052   s.glyphs = NULL;
1053   s.glyph_kerns = NULL;
1054   glyph_cap = 0;
1055
1056   s.glyph_cnt = 0;
1057   s.width_left = s.text->h;
1058   s.height_left = s.text->v;
1059
1060   s.space_char = 0;
1061
1062   s.max_width = 0;
1063
1064   cp = ss_data (s.text->string);
1065   while (s.height_left >= this->font_height && cp < ss_end (s.text->string))
1066     {
1067       const struct afm_character *cur;
1068       int char_width;
1069       int kern_adjust;
1070
1071       if (*cp == '\n')
1072         {
1073           finish_line (this, &s);
1074           cp++;
1075           continue;
1076         }
1077
1078       /* Get character and resolve ligatures. */
1079       cur = afm_get_character (afm, *cp);
1080       while (++cp < ss_end (s.text->string))
1081         {
1082           const struct afm_character *next = afm_get_character (afm, *cp);
1083           const struct afm_character *ligature = afm_get_ligature (cur, next);
1084           if (ligature == NULL)
1085             break;
1086           cur = ligature;
1087         }
1088       char_width = cur->width * this->font_height / 1000;
1089
1090       /* Get kern adjustment. */
1091       if (s.glyph_cnt > 0)
1092         kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
1093                        * this->font_height / 1000);
1094       else
1095         kern_adjust = 0;
1096
1097       /* Record the current status if this is a space character. */
1098       if (cur->code == ' ' && cp > ss_data (s.text->string))
1099         {
1100           s.space_char = cp;
1101           s.space_glyph_cnt = s.glyph_cnt;
1102           s.space_width_left = s.width_left;
1103         }
1104
1105       /* Enough room on this line? */
1106       if (char_width + kern_adjust > s.width_left)
1107         {
1108           if (s.space_char == NULL)
1109             {
1110               finish_line (this, &s);
1111               kern_adjust = 0;
1112             }
1113           else
1114             {
1115               cp = s.space_char;
1116               s.glyph_cnt = s.space_glyph_cnt;
1117               s.width_left = s.space_width_left;
1118               finish_line (this, &s);
1119               continue;
1120             }
1121         }
1122
1123       if (s.glyph_cnt >= glyph_cap)
1124         {
1125           glyph_cap = 2 * (glyph_cap + 8);
1126           s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
1127           s.glyph_kerns = xnrealloc (s.glyph_kerns,
1128                                      glyph_cap, sizeof *s.glyph_kerns);
1129         }
1130       s.glyphs[s.glyph_cnt] = cur;
1131       s.glyph_kerns[s.glyph_cnt] = kern_adjust;
1132       s.glyph_cnt++;
1133
1134       s.width_left -= char_width + kern_adjust;
1135     }
1136   if (s.height_left >= this->font_height && s.glyph_cnt > 0)
1137     finish_line (this, &s);
1138
1139   if (width != NULL)
1140     *width = s.max_width;
1141   if (height != NULL)
1142     *height = text->v - s.height_left;
1143   free (s.glyphs);
1144   free (s.glyph_kerns);
1145 }
1146
1147 static void
1148 ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
1149                  int *width, int *height)
1150 {
1151   text (this, t, false, width, height);
1152 }
1153
1154 static void
1155 ps_text_draw (struct outp_driver *this, const struct outp_text *t)
1156 {
1157   assert (this->page_open);
1158   text (this, t, true, NULL, NULL);
1159 }
1160 \f
1161 static void embed_font (struct outp_driver *this, struct font *font);
1162 static void reencode_font (struct outp_driver *this, struct font *font);
1163
1164 /* Loads and returns the font for STRING, which has the format
1165    "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
1166    PFA or PFB file's name, and ENC is the encoding file's name.
1167    PFA and ENC are optional.
1168    Returns a null pointer if unsuccessful. */
1169 static struct font *
1170 load_font (const char *string_)
1171 {
1172   char *string = xstrdup (string_);
1173   struct font *font;
1174   char *position = string;
1175   char *token;
1176   char *afm_file_name;
1177
1178   font = xmalloc (sizeof *font);
1179   font->metrics = NULL;
1180   font->embed_fn = NULL;
1181   font->encoding_fn = NULL;
1182
1183   token = strsep (&position, ",");
1184   if (token == NULL)
1185     {
1186       error (0, 0, _("\"%s\": bad font specification"), string);
1187       goto error;
1188     }
1189
1190   /* Read AFM file. */
1191   afm_file_name = find_ps_file (token);
1192   if (afm_file_name == NULL)
1193     {
1194       error (0, 0, _("could not find AFM file \"%s\""), token);
1195       goto error;
1196     }
1197   font->metrics = afm_open (afm_file_name);
1198   free (afm_file_name);
1199   if (font->metrics == NULL)
1200     goto error;
1201
1202   /* Find font file to embed. */
1203   token = strsep (&position, ",");
1204   if (token != NULL && *token != '\0')
1205     {
1206       font->embed_fn = find_ps_file (token);
1207       if (font->embed_fn == NULL)
1208         error (0, 0, _("could not find font \"%s\""), token);
1209     }
1210
1211   /* Find encoding. */
1212   token = strsep (&position, ",");
1213   if (token != NULL && *token == '\0')
1214     {
1215       font->encoding_fn = find_ps_file (token);
1216       if (font->encoding_fn == NULL)
1217         error (0, 0, _("could not find encoding \"%s\""), token);
1218     }
1219
1220   free (string);
1221   return font;
1222
1223  error:
1224   free (string);
1225   free_font (font);
1226   return NULL;
1227 }
1228
1229 /* Frees FONT. */
1230 static void
1231 free_font (struct font *font)
1232 {
1233   if (font != NULL)
1234     {
1235       afm_close (font->metrics);
1236       free (font->embed_fn);
1237       free (font->encoding_fn);
1238       free (font);
1239     }
1240 }
1241
1242 /* Emits PostScript code to embed FONT (if necessary), scale it
1243    to the proper size, re-encode it (if necessary), and store the
1244    resulting font as an object named F#, where INDEX is
1245    substituted for #. */
1246 static void
1247 setup_font (struct outp_driver *this, struct font *font, int index)
1248 {
1249   struct ps_driver_ext *x = this->ext;
1250   char *ps_name;
1251
1252   if (font->embed_fn != NULL)
1253     embed_font (this, font);
1254   else
1255     fprintf (x->file, "%%%%IncludeResource: font %s\n",
1256              afm_get_findfont_name (font->metrics));
1257
1258   ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
1259   fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
1260   free (ps_name);
1261
1262   if (font->encoding_fn != NULL)
1263     reencode_font (this, font);
1264
1265   fprintf (x->file, "/F%d ED\n", index);
1266 }
1267
1268 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1269    end-of-file or on error. */
1270 static void
1271 copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
1272 {
1273   while (copy_bytes > 0)
1274     {
1275       char buffer[BUFSIZ];
1276       unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
1277       size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
1278       size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
1279       if (write_bytes != chunk_bytes)
1280         break;
1281       copy_bytes -= chunk_bytes;
1282     }
1283 }
1284
1285 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1286    end-of-file or on error.  The bytes are translated into
1287    hexadecimal during copying and broken into lines with
1288    new-line characters. */
1289 static void
1290 copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
1291 {
1292   unsigned long i;
1293
1294   for (i = 0; i < copy_bytes; i++)
1295     {
1296       int c = getc (src);
1297       if (c == EOF)
1298         break;
1299       if (i > 0 && i % 36 == 0)
1300         putc ('\n', dst);
1301       fprintf (dst, "%02X", c);
1302     }
1303   putc ('\n', dst);
1304 }
1305
1306 /* Embeds the given FONT into THIS driver's output stream. */
1307 static void
1308 embed_font (struct outp_driver *this, struct font *font)
1309 {
1310   struct ps_driver_ext *x = this->ext;
1311   FILE *file;
1312   int c;
1313
1314   file = fopen (font->embed_fn, "rb");
1315   if (file == NULL)
1316     {
1317       error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
1318       return;
1319     }
1320
1321   fprintf (x->file, "%%%%BeginResource: font %s\n",
1322            afm_get_findfont_name (font->metrics));
1323
1324   c = getc (file);
1325   ungetc (c, file);
1326   if (c != 128)
1327     {
1328       /* PFA file.  Copy literally. */
1329       copy_bytes_literally (file, x->file, ULONG_MAX);
1330     }
1331   else
1332     {
1333       /* PFB file.  Translate as specified in Adobe Technical
1334          Note #5040. */
1335       while ((c = getc (file)) == 128)
1336         {
1337           int type;
1338           unsigned long length;
1339
1340           type = getc (file);
1341           if (type == 3)
1342             break;
1343
1344           length = getc (file);
1345           length |= (unsigned long) getc (file) << 8;
1346           length |= (unsigned long) getc (file) << 16;
1347           length |= (unsigned long) getc (file) << 24;
1348
1349           if (type == 1)
1350             copy_bytes_literally (file, x->file, length);
1351           else if (type == 2)
1352             copy_bytes_as_hex (file, x->file, length);
1353           else
1354             break;
1355         }
1356     }
1357   if (freaderror (file))
1358     error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
1359   fputs ("%%EndResource\n", x->file);
1360 }
1361
1362 /* Re-encodes FONT according to the specified encoding. */
1363 static void
1364 reencode_font (struct outp_driver *this, struct font *font)
1365 {
1366   struct ps_driver_ext *x = this->ext;
1367
1368   struct string line;
1369
1370   int line_number;
1371   FILE *file;
1372
1373   char *tab[256];
1374
1375   int i;
1376
1377   file = fopen (font->encoding_fn, "r");
1378   if (file == NULL)
1379     {
1380       error (errno, 0, _("cannot open font encoding file \"%s\""),
1381              font->encoding_fn);
1382       return;
1383     }
1384
1385   for (i = 0; i < 256; i++)
1386     tab[i] = NULL;
1387
1388   line_number = 0;
1389
1390   ds_init_empty (&line);
1391   while (ds_read_config_line (&line, &line_number, file))
1392     {
1393       char *pschar, *code;
1394       char *save_ptr, *tail;
1395       int code_val;
1396
1397       if (ds_is_empty (&line) == 0)
1398         continue;
1399
1400       pschar = strtok_r (ds_cstr (&line), " \t\r\n", &save_ptr);
1401       code = strtok_r (NULL, " \t\r\n", &save_ptr);
1402       if (pschar == NULL || code == NULL)
1403         continue;
1404
1405       code_val = strtol (code, &tail, 0);
1406       if (*tail)
1407         {
1408           error_at_line (0, 0, font->encoding_fn, line_number,
1409                          _("invalid numeric format"));
1410           continue;
1411         }
1412       if (code_val < 0 || code_val > 255)
1413         continue;
1414       if (tab[code_val] != 0)
1415         free (tab[code_val]);
1416       tab[code_val] = xstrdup (pschar);
1417     }
1418   ds_destroy (&line);
1419
1420   fputs ("[", x->file);
1421   for (i = 0; i < 256; i++)
1422     {
1423       char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
1424       fprintf (x->file, "%s\n", name);
1425       free (name);
1426       free (tab[i]);
1427     }
1428   fputs ("] RF\n", x->file);
1429
1430   if (freaderror (file) != 0)
1431     error (errno, 0, _("closing Postscript encoding \"%s\""),
1432            font->encoding_fn);
1433 }
1434
1435 /* PostScript driver class. */
1436 const struct outp_class postscript_class =
1437 {
1438   "postscript",
1439   0,
1440
1441   ps_open_driver,
1442   ps_close_driver,
1443
1444   ps_open_page,
1445   ps_close_page,
1446   NULL,
1447
1448   ps_output_chart,
1449
1450   ps_submit,
1451
1452   ps_line,
1453   ps_text_metrics,
1454   ps_text_draw,
1455 };