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