Set the output driver parameters dynamically from the output window style.
[pspp-builds.git] / src / output / cairo.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2010, 2011 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 "output/cairo.h"
20
21 #include "libpspp/assertion.h"
22 #include "libpspp/cast.h"
23 #include "libpspp/message.h"
24 #include "libpspp/start-date.h"
25 #include "libpspp/str.h"
26 #include "libpspp/string-map.h"
27 #include "libpspp/version.h"
28 #include "output/cairo-chart.h"
29 #include "output/chart-item-provider.h"
30 #include "output/charts/boxplot.h"
31 #include "output/charts/np-plot.h"
32 #include "output/charts/piechart.h"
33 #include "output/charts/plot-hist.h"
34 #include "output/charts/roc-chart.h"
35 #include "output/charts/scree.h"
36 #include "output/driver-provider.h"
37 #include "output/message-item.h"
38 #include "output/options.h"
39 #include "output/render.h"
40 #include "output/tab.h"
41 #include "output/table-item.h"
42 #include "output/table.h"
43 #include "output/text-item.h"
44
45 #include <cairo/cairo-pdf.h>
46 #include <cairo/cairo-ps.h>
47 #include <cairo/cairo-svg.h>
48 #include <cairo/cairo.h>
49 #include <pango/pango-font.h>
50 #include <pango/pango-layout.h>
51 #include <pango/pango.h>
52 #include <pango/pangocairo.h>
53 #include <stdlib.h>
54
55 #include "gl/error.h"
56 #include "gl/intprops.h"
57 #include "gl/minmax.h"
58 #include "gl/xalloc.h"
59
60 #include "gettext.h"
61 #define _(msgid) gettext (msgid)
62
63 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
64 #define H TABLE_HORZ
65 #define V TABLE_VERT
66
67 /* Measurements as we present to the rest of PSPP. */
68 #define XR_POINT PANGO_SCALE
69 #define XR_INCH (XR_POINT * 72)
70
71 /* Conversions to and from points. */
72 static double
73 xr_to_pt (int x)
74 {
75   return x / (double) XR_POINT;
76 }
77
78 /* Output types. */
79 enum xr_output_type
80   {
81     XR_PDF,
82     XR_PS,
83     XR_SVG
84   };
85
86 /* Cairo fonts. */
87 enum xr_font_type
88   {
89     XR_FONT_PROPORTIONAL,
90     XR_FONT_EMPHASIS,
91     XR_FONT_FIXED,
92     XR_N_FONTS
93   };
94
95 /* A font for use with Cairo. */
96 struct xr_font
97   {
98     PangoFontDescription *desc;
99     PangoLayout *layout;
100   };
101
102 /* An output item whose rendering is in progress. */
103 struct xr_render_fsm
104   {
105     /* Renders as much of itself as it can on the current page.  Returns true
106        if rendering is complete, false if the output item needs another
107        page. */
108     bool (*render) (struct xr_render_fsm *, struct xr_driver *);
109
110     /* Destroys the output item. */
111     void (*destroy) (struct xr_render_fsm *);
112   };
113
114 /* Cairo output driver. */
115 struct xr_driver
116   {
117     struct output_driver driver;
118
119     /* User parameters. */
120     struct xr_font fonts[XR_N_FONTS];
121
122     int width;                  /* Page width minus margins. */
123     int length;                 /* Page length minus margins and header. */
124
125     int left_margin;            /* Left margin in XR units. */
126     int right_margin;           /* Right margin in XR units. */
127     int top_margin;             /* Top margin in XR units. */
128     int bottom_margin;          /* Bottom margin in XR units. */
129
130     int line_gutter;            /* Space around lines. */
131     int line_space;             /* Space between lines. */
132     int line_width;             /* Width of lines. */
133
134     double bg_red, bg_green, bg_blue; /* Background color */
135     double fg_red, fg_green, fg_blue; /* Foreground color */
136
137     /* Internal state. */
138     struct render_params *params;
139     int char_width, char_height;
140     char *command_name;
141     char *title;
142     char *subtitle;
143     cairo_t *cairo;
144     int page_number;            /* Current page number. */
145     int y;
146     struct xr_render_fsm *fsm;
147   };
148
149 static const struct output_driver_class cairo_driver_class;
150
151 static void xr_driver_destroy_fsm (struct xr_driver *);
152 static void xr_driver_run_fsm (struct xr_driver *);
153
154 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
155                           enum render_line_style styles[TABLE_N_AXES][2]);
156 static void xr_measure_cell_width (void *, const struct table_cell *,
157                                    int *min, int *max);
158 static int xr_measure_cell_height (void *, const struct table_cell *,
159                                    int width);
160 static void xr_draw_cell (void *, const struct table_cell *,
161                           int bb[TABLE_N_AXES][2],
162                           int clip[TABLE_N_AXES][2]);
163
164 static struct xr_render_fsm *xr_render_output_item (
165   struct xr_driver *, const struct output_item *);
166 \f
167 /* Output driver basics. */
168
169 static struct xr_driver *
170 xr_driver_cast (struct output_driver *driver)
171 {
172   assert (driver->class == &cairo_driver_class);
173   return UP_CAST (driver, struct xr_driver, driver);
174 }
175
176 static struct driver_option *
177 opt (struct output_driver *d, struct string_map *options, const char *key,
178      const char *default_value)
179 {
180   return driver_option_get (d, options, key, default_value);
181 }
182
183 /* Parse color information specified by KEY into {RED,GREEN,BLUE}.
184    Currently, the input string must be of the form "#RRRRGGGGBBBB"
185    Future implementations might allow things like "yellow" and
186    "sky-blue-ultra-brown"
187 */
188 static void
189 parse_color (struct output_driver *d, struct string_map *options,
190               const char *key, const char *default_value,
191               double *dred, double *dgreen, double *dblue)
192 {
193   int red, green, blue;
194   char *string = parse_string (opt (d, options, key, default_value));
195
196   if (3 != sscanf (string, "#%04x%04x%04x", &red, &green, &blue))
197     {
198       /* If the parsed option string fails, then try the default value */
199       if ( 3 != sscanf (default_value, "#%04x%04x%04x", &red, &green, &blue))
200         {
201           /* ... and if that fails set everything to zero */
202           red = green = blue = 0;
203         }
204     }
205
206   /* Convert 16 bit ints to float */
207   *dred = red / (double) 0xFFFF;
208   *dgreen = green / (double) 0xFFFF;
209   *dblue = blue / (double) 0xFFFF;
210 }
211
212 static PangoFontDescription *
213 parse_font (struct output_driver *d, struct string_map *options,
214             const char *key, const char *default_value,
215             int default_points)
216 {
217   PangoFontDescription *desc;
218   char *string;
219
220   /* Parse KEY as a font description. */
221   string = parse_string (opt (d, options, key, default_value));
222   desc = pango_font_description_from_string (string);
223   if (desc == NULL)
224     {
225       error (0, 0, _("`%s': bad font specification"), string);
226
227       /* Fall back to DEFAULT_VALUE, which had better be a valid font
228          description. */
229       desc = pango_font_description_from_string (default_value);
230       assert (desc != NULL);
231     }
232   free (string);
233
234   /* If the font description didn't include an explicit font size, then set it
235      to DEFAULT_POINTS. */
236   if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
237     pango_font_description_set_size (desc,
238                                      default_points / 1000.0 * PANGO_SCALE);
239
240   return desc;
241 }
242
243
244 static void
245 apply_options (struct xr_driver *xr, struct string_map *o)
246 {
247   struct output_driver *d = &xr->driver;
248
249   int paper_width, paper_length;
250
251   int font_points = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
252   xr->fonts[XR_FONT_FIXED].desc = parse_font (d, o, "fixed-font", "monospace",
253                                               font_points);
254   xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font (d, o, "prop-font",
255                                                      "serif", font_points);
256   xr->fonts[XR_FONT_EMPHASIS].desc = parse_font (d, o, "emph-font",
257                                                  "serif italic", font_points);
258
259   xr->line_gutter = XR_POINT;
260   xr->line_space = XR_POINT;
261   xr->line_width = XR_POINT / 2;
262   xr->page_number = 0;
263
264   parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg_red, &xr->bg_green, &xr->bg_blue);
265   parse_color (d, o, "foreground-color", "#000000000000", &xr->fg_red, &xr->fg_green, &xr->fg_blue);
266
267   parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
268   xr->left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
269   xr->right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
270   xr->top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
271   xr->bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
272
273   xr->width = paper_width - xr->left_margin - xr->right_margin;
274   xr->length = paper_length - xr->top_margin - xr->bottom_margin;
275 }
276
277 static struct xr_driver *
278 xr_allocate (const char *name, int device_type, struct string_map *o)
279 {
280   struct xr_driver *xr = xzalloc (sizeof *xr);
281   struct output_driver *d = &xr->driver;
282
283   output_driver_init (d, &cairo_driver_class, name, device_type);
284
285   apply_options (xr, o);
286
287   return xr;
288 }
289
290 static bool
291 xr_is_72dpi (cairo_t *cr)
292 {
293   cairo_surface_type_t type;
294   cairo_surface_t *surface;
295
296   surface = cairo_get_target (cr);
297   type = cairo_surface_get_type (surface);
298   return type == CAIRO_SURFACE_TYPE_PDF || type == CAIRO_SURFACE_TYPE_PS;
299 }
300
301 static bool
302 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
303 {
304   PangoContext *context;
305   PangoFontMap *map;
306   int i;
307
308   xr->cairo = cairo;
309
310   cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
311
312   map = pango_cairo_font_map_get_default ();
313   context = pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (map));
314   if (xr_is_72dpi (cairo))
315     {
316       /* Pango seems to always scale fonts according to the DPI specified
317          in the font map, even if the surface has a real DPI.  The default
318          DPI is 96, so on a 72 DPI device fonts end up being 96/72 = 133%
319          of their desired size.  We deal with this by fixing the resolution
320          here.  Presumably there is a better solution, but what? */
321       pango_cairo_context_set_resolution (context, 72.0);
322     }
323
324   xr->char_width = 0;
325   xr->char_height = 0;
326   for (i = 0; i < XR_N_FONTS; i++)
327     {
328       struct xr_font *font = &xr->fonts[i];
329       int char_width, char_height;
330
331       font->layout = pango_layout_new (context);
332       pango_layout_set_font_description (font->layout, font->desc);
333
334       pango_layout_set_text (font->layout, "0", 1);
335       pango_layout_get_size (font->layout, &char_width, &char_height);
336       xr->char_width = MAX (xr->char_width, char_width);
337       xr->char_height = MAX (xr->char_height, char_height);
338     }
339
340   g_object_unref (G_OBJECT (context));
341
342   if (xr->params == NULL)
343     {
344       int single_width, double_width;
345
346       xr->params = xmalloc (sizeof *xr->params);
347       xr->params->draw_line = xr_draw_line;
348       xr->params->measure_cell_width = xr_measure_cell_width;
349       xr->params->measure_cell_height = xr_measure_cell_height;
350       xr->params->draw_cell = xr_draw_cell;
351       xr->params->aux = xr;
352       xr->params->size[H] = xr->width;
353       xr->params->size[V] = xr->length;
354       xr->params->font_size[H] = xr->char_width;
355       xr->params->font_size[V] = xr->char_height;
356
357       single_width = 2 * xr->line_gutter + xr->line_width;
358       double_width = 2 * xr->line_gutter + xr->line_space + 2 * xr->line_width;
359       for (i = 0; i < TABLE_N_AXES; i++)
360         {
361           xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
362           xr->params->line_widths[i][RENDER_LINE_SINGLE] = single_width;
363           xr->params->line_widths[i][RENDER_LINE_DOUBLE] = double_width;
364         }
365     }
366
367   cairo_set_source_rgb (xr->cairo, xr->fg_red, xr->fg_green, xr->fg_blue);
368
369   return true;
370 }
371
372 static struct output_driver *
373 xr_create (const char *file_name, enum settings_output_devices device_type,
374            struct string_map *o, enum xr_output_type file_type)
375 {
376   enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
377   struct output_driver *d;
378   struct xr_driver *xr;
379   cairo_surface_t *surface;
380   cairo_status_t status;
381   double width_pt, length_pt;
382
383   xr = xr_allocate (file_name, device_type, o);
384   d = &xr->driver;
385
386   width_pt = (xr->width + xr->left_margin + xr->right_margin) / 1000.0;
387   length_pt = (xr->length + xr->top_margin + xr->bottom_margin) / 1000.0;
388   if (file_type == XR_PDF)
389     surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
390   else if (file_type == XR_PS)
391     surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
392   else if (file_type == XR_SVG)
393     surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
394   else
395     NOT_REACHED ();
396
397   status = cairo_surface_status (surface);
398   if (status != CAIRO_STATUS_SUCCESS)
399     {
400       error (0, 0, _("error opening output file `%s': %s"),
401              file_name, cairo_status_to_string (status));
402       cairo_surface_destroy (surface);
403       goto error;
404     }
405
406   xr->cairo = cairo_create (surface);
407   cairo_surface_destroy (surface);
408
409   if (!xr_set_cairo (xr, xr->cairo))
410     goto error;
411
412   cairo_save (xr->cairo);
413   xr_driver_next_page (xr, xr->cairo);
414
415   if (xr->width / xr->char_width < MIN_WIDTH)
416     {
417       error (0, 0, _("The defined page is not wide enough to hold at least %d "
418                      "characters in the default font.  In fact, there's only "
419                      "room for %d characters."),
420              MIN_WIDTH,
421              xr->width / xr->char_width);
422       goto error;
423     }
424
425   if (xr->length / xr->char_height < MIN_LENGTH)
426     {
427       error (0, 0, _("The defined page is not long enough to hold at least %d "
428                      "lines in the default font.  In fact, there's only "
429                      "room for %d lines."),
430              MIN_LENGTH,
431              xr->length / xr->char_height);
432       goto error;
433     }
434
435   return &xr->driver;
436
437  error:
438   output_driver_destroy (&xr->driver);
439   return NULL;
440 }
441
442 static struct output_driver *
443 xr_pdf_create (const char *file_name, enum settings_output_devices device_type,
444                struct string_map *o)
445 {
446   return xr_create (file_name, device_type, o, XR_PDF);
447 }
448
449 static struct output_driver *
450 xr_ps_create (const char *file_name, enum settings_output_devices device_type,
451                struct string_map *o)
452 {
453   return xr_create (file_name, device_type, o, XR_PS);
454 }
455
456 static struct output_driver *
457 xr_svg_create (const char *file_name, enum settings_output_devices device_type,
458                struct string_map *o)
459 {
460   return xr_create (file_name, device_type, o, XR_SVG);
461 }
462
463 static void
464 xr_destroy (struct output_driver *driver)
465 {
466   struct xr_driver *xr = xr_driver_cast (driver);
467   size_t i;
468
469   xr_driver_destroy_fsm (xr);
470
471   if (xr->cairo != NULL)
472     {
473       cairo_status_t status;
474
475       cairo_surface_finish (cairo_get_target (xr->cairo));
476       status = cairo_status (xr->cairo);
477       if (status != CAIRO_STATUS_SUCCESS)
478         error (0, 0, _("error drawing output for %s driver: %s"),
479                output_driver_get_name (driver),
480                cairo_status_to_string (status));
481       cairo_destroy (xr->cairo);
482     }
483
484   free (xr->command_name);
485   for (i = 0; i < XR_N_FONTS; i++)
486     {
487       struct xr_font *font = &xr->fonts[i];
488
489       if (font->desc != NULL)
490         pango_font_description_free (font->desc);
491       if (font->layout != NULL)
492         g_object_unref (font->layout);
493     }
494
495   free (xr->params);
496   free (xr);
497 }
498
499 static void
500 xr_flush (struct output_driver *driver)
501 {
502   struct xr_driver *xr = xr_driver_cast (driver);
503
504   cairo_surface_flush (cairo_get_target (xr->cairo));
505 }
506
507 static void
508 xr_init_caption_cell (const char *caption, struct table_cell *cell)
509 {
510   cell->contents = caption;
511   cell->options = TAB_LEFT;
512   cell->destructor = NULL;
513 }
514
515 static struct render_page *
516 xr_render_table_item (struct xr_driver *xr, const struct table_item *item,
517                       int *caption_widthp, int *caption_heightp)
518 {
519   const char *caption = table_item_get_caption (item);
520
521   if (caption != NULL)
522     {
523       /* XXX doesn't do well with very large captions */
524       int min_width, max_width;
525       struct table_cell cell;
526
527       xr_init_caption_cell (caption, &cell);
528
529       xr_measure_cell_width (xr, &cell, &min_width, &max_width);
530       *caption_widthp = MIN (max_width, xr->width);
531       *caption_heightp = xr_measure_cell_height (xr, &cell, *caption_widthp);
532     }
533   else
534     *caption_heightp = 0;
535
536   return render_page_create (xr->params, table_item_get_table (item));
537 }
538
539 static void
540 xr_submit (struct output_driver *driver, const struct output_item *output_item)
541 {
542   struct xr_driver *xr = xr_driver_cast (driver);
543
544   xr_driver_output_item (xr, output_item);
545   while (xr_driver_need_new_page (xr))
546     {
547       cairo_restore (xr->cairo);
548       cairo_show_page (xr->cairo);
549       cairo_save (xr->cairo);
550       xr_driver_next_page (xr, xr->cairo);
551     }
552 }
553 \f
554 /* Functions for rendering a series of output items to a series of Cairo
555    contexts, with pagination.
556
557    Used by PSPPIRE for printing, and by the basic Cairo output driver above as
558    its underlying implementation.
559
560    See the big comment in cairo.h for intended usage. */
561
562 /* Gives new page CAIRO to XR for output.  CAIRO may be null to skip actually
563    rendering the page (which might be useful to find out how many pages an
564    output document has without actually rendering it). */
565 void
566 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
567 {
568   if (cairo != NULL)
569     {
570       cairo_save (cairo);
571       cairo_set_source_rgb (cairo, xr->bg_red, xr->bg_green, xr->bg_blue);
572       cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
573       cairo_fill (cairo);
574       cairo_restore (cairo);
575
576       cairo_translate (cairo,
577                        xr_to_pt (xr->left_margin),
578                        xr_to_pt (xr->top_margin));
579     }
580
581   xr->page_number++;
582   xr->cairo = cairo;
583   xr->y = 0;
584   xr_driver_run_fsm (xr);
585 }
586
587 /* Start rendering OUTPUT_ITEM to XR.  Only valid if XR is not in the middle of
588    rendering a previous output item, that is, only if xr_driver_need_new_page()
589    returns false. */
590 void
591 xr_driver_output_item (struct xr_driver *xr,
592                        const struct output_item *output_item)
593 {
594   assert (xr->fsm == NULL);
595   xr->fsm = xr_render_output_item (xr, output_item);
596   xr_driver_run_fsm (xr);
597 }
598
599 /* Returns true if XR is in the middle of rendering an output item and needs a
600    new page to be appended using xr_driver_next_page() to make progress,
601    otherwise false. */
602 bool
603 xr_driver_need_new_page (const struct xr_driver *xr)
604 {
605   return xr->fsm != NULL;
606 }
607
608 /* Returns true if the current page doesn't have any content yet. */
609 bool
610 xr_driver_is_page_blank (const struct xr_driver *xr)
611 {
612   return xr->y == 0;
613 }
614
615 static void
616 xr_driver_destroy_fsm (struct xr_driver *xr)
617 {
618   if (xr->fsm != NULL)
619     {
620       xr->fsm->destroy (xr->fsm);
621       xr->fsm = NULL;
622     }
623 }
624
625 static void
626 xr_driver_run_fsm (struct xr_driver *xr)
627 {
628   if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
629     xr_driver_destroy_fsm (xr);
630 }
631 \f
632 static void
633 xr_layout_cell (struct xr_driver *, const struct table_cell *,
634                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
635                 PangoWrapMode, int *width, int *height);
636
637 static void
638 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
639 {
640   cairo_new_path (xr->cairo);
641   cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
642   cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
643   cairo_stroke (xr->cairo);
644 }
645
646 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
647    shortening it to X0...X1 if SHORTEN is true.
648    Draws a horizontal line X1...X3 at Y if RIGHT says so,
649    shortening it to X2...X3 if SHORTEN is true. */
650 static void
651 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
652            enum render_line_style left, enum render_line_style right,
653            bool shorten)
654 {
655   if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
656     dump_line (xr, x0, y, x3, y);
657   else
658     {
659       if (left != RENDER_LINE_NONE)
660         dump_line (xr, x0, y, shorten ? x1 : x2, y);
661       if (right != RENDER_LINE_NONE)
662         dump_line (xr, shorten ? x2 : x1, y, x3, y);
663     }
664 }
665
666 /* Draws a vertical line Y0...Y2 at X if TOP says so,
667    shortening it to Y0...Y1 if SHORTEN is true.
668    Draws a vertical line Y1...Y3 at X if BOTTOM says so,
669    shortening it to Y2...Y3 if SHORTEN is true. */
670 static void
671 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
672            enum render_line_style top, enum render_line_style bottom,
673            bool shorten)
674 {
675   if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
676     dump_line (xr, x, y0, x, y3);
677   else
678     {
679       if (top != RENDER_LINE_NONE)
680         dump_line (xr, x, y0, x, shorten ? y1 : y2);
681       if (bottom != RENDER_LINE_NONE)
682         dump_line (xr, x, shorten ? y2 : y1, x, y3);
683     }
684 }
685
686 static void
687 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
688               enum render_line_style styles[TABLE_N_AXES][2])
689 {
690   const int x0 = bb[H][0];
691   const int y0 = bb[V][0];
692   const int x3 = bb[H][1];
693   const int y3 = bb[V][1];
694   const int top = styles[H][0];
695   const int left = styles[V][0];
696   const int bottom = styles[H][1];
697   const int right = styles[V][1];
698
699   /* The algorithm here is somewhat subtle, to allow it to handle
700      all the kinds of intersections that we need.
701
702      Three additional ordinates are assigned along the x axis.  The
703      first is xc, midway between x0 and x3.  The others are x1 and
704      x2; for a single vertical line these are equal to xc, and for
705      a double vertical line they are the ordinates of the left and
706      right half of the double line.
707
708      yc, y1, and y2 are assigned similarly along the y axis.
709
710      The following diagram shows the coordinate system and output
711      for double top and bottom lines, single left line, and no
712      right line:
713
714                  x0       x1 xc  x2      x3
715                y0 ________________________
716                   |        #     #       |
717                   |        #     #       |
718                   |        #     #       |
719                   |        #     #       |
720                   |        #     #       |
721      y1 = y2 = yc |#########     #       |
722                   |        #     #       |
723                   |        #     #       |
724                   |        #     #       |
725                   |        #     #       |
726                y3 |________#_____#_______|
727   */
728   struct xr_driver *xr = xr_;
729
730   /* Offset from center of each line in a pair of double lines. */
731   int double_line_ofs = (xr->line_space + xr->line_width) / 2;
732
733   /* Are the lines along each axis single or double?
734      (It doesn't make sense to have different kinds of line on the
735      same axis, so we don't try to gracefully handle that case.) */
736   bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
737   bool double_horz = left == RENDER_LINE_DOUBLE || right == RENDER_LINE_DOUBLE;
738
739   /* When horizontal lines are doubled,
740      the left-side line along y1 normally runs from x0 to x2,
741      and the right-side line along y1 from x3 to x1.
742      If the top-side line is also doubled, we shorten the y1 lines,
743      so that the left-side line runs only to x1,
744      and the right-side line only to x2.
745      Otherwise, the horizontal line at y = y1 below would cut off
746      the intersection, which looks ugly:
747                x0       x1     x2      x3
748              y0 ________________________
749                 |        #     #       |
750                 |        #     #       |
751                 |        #     #       |
752                 |        #     #       |
753              y1 |#########     ########|
754                 |                      |
755                 |                      |
756              y2 |######################|
757                 |                      |
758                 |                      |
759              y3 |______________________|
760      It is more of a judgment call when the horizontal line is
761      single.  We actually choose to cut off the line anyhow, as
762      shown in the first diagram above.
763   */
764   bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
765   bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
766   bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
767   int horz_line_ofs = double_vert ? double_line_ofs : 0;
768   int xc = (x0 + x3) / 2;
769   int x1 = xc - horz_line_ofs;
770   int x2 = xc + horz_line_ofs;
771
772   bool shorten_x1_lines = left == RENDER_LINE_DOUBLE;
773   bool shorten_x2_lines = right == RENDER_LINE_DOUBLE;
774   bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
775   int vert_line_ofs = double_horz ? double_line_ofs : 0;
776   int yc = (y0 + y3) / 2;
777   int y1 = yc - vert_line_ofs;
778   int y2 = yc + vert_line_ofs;
779
780   if (!double_horz)
781     horz_line (xr, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
782   else
783     {
784       horz_line (xr, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
785       horz_line (xr, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
786     }
787
788   if (!double_vert)
789     vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
790   else
791     {
792       vert_line (xr, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
793       vert_line (xr, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
794     }
795 }
796
797 static void
798 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
799                        int *min_width, int *max_width)
800 {
801   struct xr_driver *xr = xr_;
802   int bb[TABLE_N_AXES][2];
803   int clip[TABLE_N_AXES][2];
804   int h;
805
806   bb[H][0] = 0;
807   bb[H][1] = INT_MAX;
808   bb[V][0] = 0;
809   bb[V][1] = INT_MAX;
810   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
811   xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, max_width, &h);
812
813   bb[H][1] = 1;
814   xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, min_width, &h);
815 }
816
817 static int
818 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
819 {
820   struct xr_driver *xr = xr_;
821   int bb[TABLE_N_AXES][2];
822   int clip[TABLE_N_AXES][2];
823   int w, h;
824
825   bb[H][0] = 0;
826   bb[H][1] = width;
827   bb[V][0] = 0;
828   bb[V][1] = INT_MAX;
829   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
830   xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
831   return h;
832 }
833
834 static void
835 xr_draw_cell (void *xr_, const struct table_cell *cell,
836               int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
837 {
838   struct xr_driver *xr = xr_;
839   int w, h;
840
841   xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
842 }
843 \f
844 static void
845 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
846                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
847                 PangoWrapMode wrap, int *width, int *height)
848 {
849   struct xr_font *font;
850
851   font = (cell->options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
852           : cell->options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
853           : &xr->fonts[XR_FONT_PROPORTIONAL]);
854
855   pango_layout_set_text (font->layout, cell->contents, -1);
856
857   pango_layout_set_alignment (
858     font->layout,
859     ((cell->options & TAB_ALIGNMENT) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
860      : (cell->options & TAB_ALIGNMENT) == TAB_LEFT ? PANGO_ALIGN_LEFT
861      : PANGO_ALIGN_CENTER));
862   pango_layout_set_width (font->layout,
863                           bb[H][1] == INT_MAX ? -1 : bb[H][1] - bb[H][0]);
864   pango_layout_set_wrap (font->layout, wrap);
865
866   if (clip[H][0] != clip[H][1])
867     {
868       cairo_save (xr->cairo);
869
870       if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
871         {
872           double x0 = xr_to_pt (clip[H][0]);
873           double y0 = xr_to_pt (clip[V][0] + xr->y);
874           double x1 = xr_to_pt (clip[H][1]);
875           double y1 = xr_to_pt (clip[V][1] + xr->y);
876
877           cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
878           cairo_clip (xr->cairo);
879         }
880
881       cairo_translate (xr->cairo,
882                        xr_to_pt (bb[H][0]),
883                        xr_to_pt (bb[V][0] + xr->y));
884       pango_cairo_show_layout (xr->cairo, font->layout);
885       cairo_restore (xr->cairo);
886     }
887
888   if (width != NULL || height != NULL)
889     {
890       int w, h;
891
892       pango_layout_get_size (font->layout, &w, &h);
893       if (width != NULL)
894         *width = w;
895       if (height != NULL)
896         *height = h;
897     }
898 }
899
900 static void
901 xr_draw_title (struct xr_driver *xr, const char *title,
902                int title_width, int title_height)
903 {
904   struct table_cell cell;
905   int bb[TABLE_N_AXES][2];
906
907   xr_init_caption_cell (title, &cell);
908   bb[H][0] = 0;
909   bb[H][1] = title_width;
910   bb[V][0] = 0;
911   bb[V][1] = title_height;
912   xr_draw_cell (xr, &cell, bb, bb);
913 }
914 \f
915 struct output_driver_factory pdf_driver_factory = { "pdf", xr_pdf_create };
916 struct output_driver_factory ps_driver_factory = { "ps", xr_ps_create };
917 struct output_driver_factory svg_driver_factory = { "svg", xr_svg_create };
918
919 static const struct output_driver_class cairo_driver_class =
920 {
921   "cairo",
922   xr_destroy,
923   xr_submit,
924   xr_flush,
925 };
926 \f
927 /* GUI rendering helpers. */
928
929 struct xr_rendering
930   {
931     struct output_item *item;
932
933     /* Table items. */
934     struct render_page *page;
935     struct xr_driver *xr;
936     int title_width;
937     int title_height;
938   };
939
940 #define CHART_WIDTH 500
941 #define CHART_HEIGHT 375
942
943
944
945 struct xr_driver *
946 xr_driver_create (cairo_t *cairo, struct string_map *options)
947 {
948   struct xr_driver *xr = xr_allocate ("cairo", 0, options);
949   if (!xr_set_cairo (xr, cairo))
950     {
951       output_driver_destroy (&xr->driver);
952       return NULL;
953     }
954   return xr;
955 }
956
957 /* Destroy XR, which should have been created with xr_driver_create().  Any
958    cairo_t added to XR is not destroyed, because it is owned by the client. */
959 void
960 xr_driver_destroy (struct xr_driver *xr)
961 {
962   if (xr != NULL)
963     {
964       xr->cairo = NULL;
965       output_driver_destroy (&xr->driver);
966     }
967 }
968
969 static struct xr_rendering *
970 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
971 {
972   struct table_item *table_item;
973   struct xr_rendering *r;
974
975   table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
976   r = xr_rendering_create (xr, &table_item->output_item, cr);
977   table_item_unref (table_item);
978
979   return r;
980 }
981
982 void 
983 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
984 {
985   apply_options (xr->xr, o);
986 }
987
988 struct xr_rendering *
989 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
990                      cairo_t *cr)
991 {
992   struct xr_rendering *r = NULL;
993
994   if (is_text_item (item))
995     r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
996                                   cr);
997   else if (is_message_item (item))
998     {
999       const struct message_item *message_item = to_message_item (item);
1000       const struct msg *msg = message_item_get_msg (message_item);
1001       char *s = msg_to_string (msg, NULL);
1002       r = xr_rendering_create_text (xr, s, cr);
1003       free (s);
1004     }
1005   else if (is_table_item (item))
1006     {
1007       r = xzalloc (sizeof *r);
1008       r->item = output_item_ref (item);
1009       r->xr = xr;
1010       xr_set_cairo (xr, cr);
1011       r->page = xr_render_table_item (xr, to_table_item (item),
1012                                       &r->title_width, &r->title_height);
1013     }
1014   else if (is_chart_item (item))
1015     {
1016       r = xzalloc (sizeof *r);
1017       r->item = output_item_ref (item);
1018     }
1019
1020   return r;
1021 }
1022
1023 void
1024 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
1025 {
1026   if (is_table_item (r->item))
1027     {
1028       int w0 = render_page_get_size (r->page, H);
1029       int w1 = r->title_width;
1030       *w = MAX (w0, w1) / 1024;
1031       *h = (render_page_get_size (r->page, V) + r->title_height) / 1024;
1032     }
1033   else
1034     {
1035       *w = CHART_WIDTH;
1036       *h = CHART_HEIGHT;
1037     }
1038 }
1039
1040 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1041                     double x, double y, double width, double height);
1042
1043 /* Draws onto CR at least the region of R that is enclosed in (X,Y)-(X+W,Y+H),
1044    and possibly some additional parts. */
1045 void
1046 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1047                    int x, int y, int w, int h)
1048 {
1049   if (is_table_item (r->item))
1050     {
1051       struct xr_driver *xr = r->xr;
1052
1053       xr_set_cairo (xr, cr);
1054
1055       if (r->title_height > 0)
1056         {
1057           xr->y = 0;
1058           xr_draw_title (xr, table_item_get_caption (to_table_item (r->item)),
1059                          r->title_width, r->title_height);
1060         }
1061
1062       xr->y = r->title_height;
1063       render_page_draw_region (r->page, x * 1024, (y * 1024) - r->title_height,
1064                                w * 1024, h * 1024);
1065     }
1066   else
1067     xr_draw_chart (to_chart_item (r->item), cr,
1068                    0, 0, CHART_WIDTH, CHART_HEIGHT);
1069 }
1070
1071 static void
1072 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1073                double x, double y, double width, double height)
1074 {
1075   struct xrchart_geometry geom;
1076
1077   cairo_save (cr);
1078   cairo_translate (cr, x, y + height);
1079   cairo_scale (cr, 1.0, -1.0);
1080   xrchart_geometry_init (cr, &geom, width, height);
1081   if (is_boxplot (chart_item))
1082     xrchart_draw_boxplot (chart_item, cr, &geom);
1083   else if (is_histogram_chart (chart_item))
1084     xrchart_draw_histogram (chart_item, cr, &geom);
1085   else if (is_np_plot_chart (chart_item))
1086     xrchart_draw_np_plot (chart_item, cr, &geom);
1087   else if (is_piechart (chart_item))
1088     xrchart_draw_piechart (chart_item, cr, &geom);
1089   else if (is_roc_chart (chart_item))
1090     xrchart_draw_roc (chart_item, cr, &geom);
1091   else if (is_scree (chart_item))
1092     xrchart_draw_scree (chart_item, cr, &geom);
1093   else
1094     NOT_REACHED ();
1095   xrchart_geometry_free (cr, &geom);
1096
1097   cairo_restore (cr);
1098 }
1099
1100 char *
1101 xr_draw_png_chart (const struct chart_item *item,
1102                    const char *file_name_template, int number)
1103 {
1104   const int width = 640;
1105   const int length = 480;
1106
1107   cairo_surface_t *surface;
1108   cairo_status_t status;
1109   const char *number_pos;
1110   char *file_name;
1111   cairo_t *cr;
1112
1113   number_pos = strchr (file_name_template, '#');
1114   if (number_pos != NULL)
1115     file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1116                            file_name_template, number, number_pos + 1);
1117   else
1118     file_name = xstrdup (file_name_template);
1119
1120   surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1121   cr = cairo_create (surface);
1122
1123   cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
1124
1125   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1126
1127   status = cairo_surface_write_to_png (surface, file_name);
1128   if (status != CAIRO_STATUS_SUCCESS)
1129     error (0, 0, _("error writing output file `%s': %s"),
1130            file_name, cairo_status_to_string (status));
1131
1132   cairo_destroy (cr);
1133   cairo_surface_destroy (surface);
1134
1135   return file_name;
1136 }
1137 \f
1138 struct xr_table_state
1139   {
1140     struct xr_render_fsm fsm;
1141     struct table_item *table_item;
1142     struct render_break x_break;
1143     struct render_break y_break;
1144     int caption_height;
1145   };
1146
1147 static bool
1148 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1149 {
1150   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1151
1152   for (;;)
1153     {
1154       struct render_page *y_slice;
1155       int space;
1156
1157       while (!render_break_has_next (&ts->y_break))
1158         {
1159           struct render_page *x_slice;
1160
1161           render_break_destroy (&ts->y_break);
1162           if (!render_break_has_next (&ts->x_break))
1163             return false;
1164
1165           x_slice = render_break_next (&ts->x_break, xr->width);
1166           render_break_init (&ts->y_break, x_slice, V);
1167         }
1168
1169       space = xr->length - xr->y;
1170       if (render_break_next_size (&ts->y_break) > space)
1171         {
1172           assert (xr->y > 0);
1173           return true;
1174         }
1175
1176       y_slice = render_break_next (&ts->y_break, space);
1177       if (ts->caption_height)
1178         {
1179           if (xr->cairo)
1180             xr_draw_title (xr, table_item_get_caption (ts->table_item),
1181                            xr->width, ts->caption_height);
1182
1183           xr->y += ts->caption_height;
1184           ts->caption_height = 0;
1185         }
1186
1187       if (xr->cairo)
1188         render_page_draw (y_slice);
1189       xr->y += render_page_get_size (y_slice, V);
1190       render_page_unref (y_slice);
1191     }
1192 }
1193
1194 static void
1195 xr_table_destroy (struct xr_render_fsm *fsm)
1196 {
1197   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1198
1199   table_item_unref (ts->table_item);
1200   render_break_destroy (&ts->x_break);
1201   render_break_destroy (&ts->y_break);
1202   free (ts);
1203 }
1204
1205 static struct xr_render_fsm *
1206 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1207 {
1208   struct xr_table_state *ts;
1209   struct render_page *page;
1210   int caption_width;
1211
1212   ts = xmalloc (sizeof *ts);
1213   ts->fsm.render = xr_table_render;
1214   ts->fsm.destroy = xr_table_destroy;
1215   ts->table_item = table_item_ref (table_item);
1216
1217   if (xr->y > 0)
1218     xr->y += xr->char_height;
1219
1220   page = xr_render_table_item (xr, table_item,
1221                                &caption_width, &ts->caption_height);
1222   xr->params->size[V] = xr->length - ts->caption_height;
1223
1224   render_break_init (&ts->x_break, page, H);
1225   render_break_init_empty (&ts->y_break);
1226
1227   return &ts->fsm;
1228 }
1229 \f
1230 struct xr_chart_state
1231   {
1232     struct xr_render_fsm fsm;
1233     struct chart_item *chart_item;
1234   };
1235
1236 static bool
1237 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1238 {
1239   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1240
1241   if (xr->y > 0)
1242     return true;
1243
1244   if (xr->cairo != NULL)
1245     xr_draw_chart (cs->chart_item, xr->cairo, 0.0, 0.0,
1246                    xr_to_pt (xr->width), xr_to_pt (xr->length));
1247   xr->y = xr->length;
1248
1249   return false;
1250 }
1251
1252 static void
1253 xr_chart_destroy (struct xr_render_fsm *fsm)
1254 {
1255   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1256
1257   chart_item_unref (cs->chart_item);
1258   free (cs);
1259 }
1260
1261 static struct xr_render_fsm *
1262 xr_render_chart (const struct chart_item *chart_item)
1263 {
1264   struct xr_chart_state *cs;
1265
1266   cs = xmalloc (sizeof *cs);
1267   cs->fsm.render = xr_chart_render;
1268   cs->fsm.destroy = xr_chart_destroy;
1269   cs->chart_item = chart_item_ref (chart_item);
1270
1271   return &cs->fsm;
1272 }
1273 \f
1274 static bool
1275 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1276 {
1277   return xr->y > 0;
1278 }
1279
1280 static void
1281 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1282 {
1283   /* Nothing to do. */
1284 }
1285
1286 static struct xr_render_fsm *
1287 xr_render_eject (void)
1288 {
1289   static struct xr_render_fsm eject_renderer =
1290     {
1291       xr_eject_render,
1292       xr_eject_destroy
1293     };
1294
1295   return &eject_renderer;
1296 }
1297 \f
1298 static struct xr_render_fsm *
1299 xr_create_text_renderer (struct xr_driver *xr, const char *text)
1300 {
1301   struct table_item *table_item;
1302   struct xr_render_fsm *fsm;
1303
1304   table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
1305   fsm = xr_render_table (xr, table_item);
1306   table_item_unref (table_item);
1307
1308   return fsm;
1309 }
1310
1311 static struct xr_render_fsm *
1312 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1313 {
1314   enum text_item_type type = text_item_get_type (text_item);
1315   const char *text = text_item_get_text (text_item);
1316
1317   switch (type)
1318     {
1319     case TEXT_ITEM_TITLE:
1320       free (xr->title);
1321       xr->title = xstrdup (text);
1322       break;
1323
1324     case TEXT_ITEM_SUBTITLE:
1325       free (xr->subtitle);
1326       xr->subtitle = xstrdup (text);
1327       break;
1328
1329     case TEXT_ITEM_COMMAND_CLOSE:
1330       break;
1331
1332     case TEXT_ITEM_BLANK_LINE:
1333       if (xr->y > 0)
1334         xr->y += xr->char_height;
1335       break;
1336
1337     case TEXT_ITEM_EJECT_PAGE:
1338       if (xr->y > 0)
1339         return xr_render_eject ();
1340       break;
1341
1342     default:
1343       return xr_create_text_renderer (xr, text);
1344     }
1345
1346   return NULL;
1347 }
1348
1349 static struct xr_render_fsm *
1350 xr_render_message (struct xr_driver *xr,
1351                    const struct message_item *message_item)
1352 {
1353   const struct msg *msg = message_item_get_msg (message_item);
1354   struct xr_render_fsm *fsm;
1355   char *s;
1356
1357   s = msg_to_string (msg, xr->command_name);
1358   fsm = xr_create_text_renderer (xr, s);
1359   free (s);
1360
1361   return fsm;
1362 }
1363
1364 static struct xr_render_fsm *
1365 xr_render_output_item (struct xr_driver *xr,
1366                        const struct output_item *output_item)
1367 {
1368   if (is_table_item (output_item))
1369     return xr_render_table (xr, to_table_item (output_item));
1370   else if (is_chart_item (output_item))
1371     return xr_render_chart (to_chart_item (output_item));
1372   else if (is_text_item (output_item))
1373     return xr_render_text (xr, to_text_item (output_item));
1374   else if (is_message_item (output_item))
1375     return xr_render_message (xr, to_message_item (output_item));
1376   else
1377     return NULL;
1378 }