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