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