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