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