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