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