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