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