1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "output/cairo.h"
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"
46 #include <cairo/cairo-pdf.h>
47 #include <cairo/cairo-ps.h>
48 #include <cairo/cairo-svg.h>
49 #include <cairo/cairo.h>
51 #include <pango/pango-font.h>
52 #include <pango/pango-layout.h>
53 #include <pango/pango.h>
54 #include <pango/pangocairo.h>
57 #include "gl/intprops.h"
58 #include "gl/minmax.h"
59 #include "gl/xalloc.h"
62 #define _(msgid) gettext (msgid)
64 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
68 /* The unit used for internal measurements is inch/(72 * XR_POINT). */
69 #define XR_POINT PANGO_SCALE
71 /* Conversions to and from points. */
75 return x / (double) XR_POINT;
95 /* A font for use with Cairo. */
98 PangoFontDescription *desc;
102 /* An output item whose rendering is in progress. */
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
108 bool (*render) (struct xr_render_fsm *, struct xr_driver *);
110 /* Destroys the output item. */
111 void (*destroy) (struct xr_render_fsm *);
114 /* Cairo output driver. */
117 struct output_driver driver;
119 /* User parameters. */
120 struct xr_font fonts[XR_N_FONTS];
122 int width; /* Page width minus margins. */
123 int length; /* Page length minus margins and header. */
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). */
130 int line_gutter; /* Space around lines. */
131 int line_space; /* Space between lines. */
132 int line_width; /* Width of lines. */
134 int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
136 struct xr_color bg; /* Background color */
137 struct xr_color fg; /* Foreground color */
139 /* Internal state. */
140 struct render_params *params;
141 int char_width, char_height;
146 int page_number; /* Current page number. */
148 struct xr_render_fsm *fsm;
151 static const struct output_driver_class cairo_driver_class;
153 static void xr_driver_destroy_fsm (struct xr_driver *);
154 static void xr_driver_run_fsm (struct xr_driver *);
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 *,
160 static int xr_measure_cell_height (void *, const struct table_cell *,
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]);
166 static struct xr_render_fsm *xr_render_output_item (
167 struct xr_driver *, const struct output_item *);
169 /* Output driver basics. */
171 static struct xr_driver *
172 xr_driver_cast (struct output_driver *driver)
174 assert (driver->class == &cairo_driver_class);
175 return UP_CAST (driver, struct xr_driver, driver);
178 static struct driver_option *
179 opt (struct output_driver *d, struct string_map *options, const char *key,
180 const char *default_value)
182 return driver_option_get (d, options, key, default_value);
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"
191 parse_color (struct output_driver *d, struct string_map *options,
192 const char *key, const char *default_value,
193 struct xr_color *color)
195 int red, green, blue;
196 char *string = parse_string (opt (d, options, key, default_value));
198 if (3 != sscanf (string, "#%04x%04x%04x", &red, &green, &blue))
200 /* If the parsed option string fails, then try the default value */
201 if ( 3 != sscanf (default_value, "#%04x%04x%04x", &red, &green, &blue))
203 /* ... and if that fails set everything to zero */
204 red = green = blue = 0;
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;
216 static PangoFontDescription *
217 parse_font (struct output_driver *d, struct string_map *options,
218 const char *key, const char *default_value,
221 PangoFontDescription *desc;
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);
229 msg (MW, _("`%s': bad font specification"), string);
231 /* Fall back to DEFAULT_VALUE, which had better be a valid font
233 desc = pango_font_description_from_string (default_value);
234 assert (desc != NULL);
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);
249 apply_options (struct xr_driver *xr, struct string_map *o)
251 struct output_driver *d = &xr->driver;
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;
258 int min_break[TABLE_N_AXES];
260 /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
261 const double scale = XR_POINT / 1000.;
265 for (i = 0; i < XR_N_FONTS; i++)
267 struct xr_font *font = &xr->fonts[i];
269 if (font->desc != NULL)
270 pango_font_description_free (font->desc);
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",
276 xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font (d, o, "prop-font",
278 xr->fonts[XR_FONT_EMPHASIS].desc = parse_font (d, o, "emph-font",
279 "serif italic", font_size);
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;
286 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg);
287 parse_color (d, o, "foreground-color", "#000000000000", &xr->fg);
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"));
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;
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;
310 static struct xr_driver *
311 xr_allocate (const char *name, int device_type, struct string_map *o)
313 struct xr_driver *xr = xzalloc (sizeof *xr);
314 struct output_driver *d = &xr->driver;
316 output_driver_init (d, &cairo_driver_class, name, device_type);
318 apply_options (xr, o);
324 pango_to_xr (int pango)
326 return (XR_POINT != PANGO_SCALE
327 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
334 return (XR_POINT != PANGO_SCALE
335 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
340 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
346 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
350 for (i = 0; i < XR_N_FONTS; i++)
352 struct xr_font *font = &xr->fonts[i];
353 int char_width, char_height;
355 font->layout = pango_cairo_create_layout (cairo);
356 pango_layout_set_font_description (font->layout, font->desc);
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));
364 if (xr->params == NULL)
366 int single_width, double_width;
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;
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++)
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;
388 for (i = 0; i < TABLE_N_AXES; i++)
389 xr->params->min_break[i] = xr->min_break[i];
392 cairo_set_source_rgb (xr->cairo, xr->fg.red, xr->fg.green, xr->fg.blue);
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)
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;
407 xr = xr_allocate (file_name, device_type, o);
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);
420 status = cairo_surface_status (surface);
421 if (status != CAIRO_STATUS_SUCCESS)
423 msg (ME, _("error opening output file `%s': %s"),
424 file_name, cairo_status_to_string (status));
425 cairo_surface_destroy (surface);
429 xr->cairo = cairo_create (surface);
430 cairo_surface_destroy (surface);
432 if (!xr_set_cairo (xr, xr->cairo))
435 cairo_save (xr->cairo);
436 xr_driver_next_page (xr, xr->cairo);
438 if (xr->width / xr->char_width < MIN_WIDTH)
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."),
444 xr->width / xr->char_width);
448 if (xr->length / xr->char_height < MIN_LENGTH)
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."),
454 xr->length / xr->char_height);
461 output_driver_destroy (&xr->driver);
465 static struct output_driver *
466 xr_pdf_create (const char *file_name, enum settings_output_devices device_type,
467 struct string_map *o)
469 return xr_create (file_name, device_type, o, XR_PDF);
472 static struct output_driver *
473 xr_ps_create (const char *file_name, enum settings_output_devices device_type,
474 struct string_map *o)
476 return xr_create (file_name, device_type, o, XR_PS);
479 static struct output_driver *
480 xr_svg_create (const char *file_name, enum settings_output_devices device_type,
481 struct string_map *o)
483 return xr_create (file_name, device_type, o, XR_SVG);
487 xr_destroy (struct output_driver *driver)
489 struct xr_driver *xr = xr_driver_cast (driver);
492 xr_driver_destroy_fsm (xr);
494 if (xr->cairo != NULL)
496 cairo_status_t status;
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);
507 free (xr->command_name);
508 for (i = 0; i < XR_N_FONTS; i++)
510 struct xr_font *font = &xr->fonts[i];
512 if (font->desc != NULL)
513 pango_font_description_free (font->desc);
514 if (font->layout != NULL)
515 g_object_unref (font->layout);
523 xr_flush (struct output_driver *driver)
525 struct xr_driver *xr = xr_driver_cast (driver);
527 cairo_surface_flush (cairo_get_target (xr->cairo));
531 xr_init_caption_cell (const char *caption, struct table_cell *cell)
533 cell->contents = caption;
534 cell->options = TAB_LEFT;
535 cell->destructor = NULL;
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)
542 const char *caption = table_item_get_caption (item);
546 /* XXX doesn't do well with very large captions */
547 int min_width, max_width;
548 struct table_cell cell;
550 xr_init_caption_cell (caption, &cell);
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);
557 *caption_heightp = 0;
559 return render_page_create (xr->params, table_item_get_table (item));
563 xr_submit (struct output_driver *driver, const struct output_item *output_item)
565 struct xr_driver *xr = xr_driver_cast (driver);
567 output_driver_track_current_command (output_item, &xr->command_name);
569 xr_driver_output_item (xr, output_item);
570 while (xr_driver_need_new_page (xr))
572 cairo_restore (xr->cairo);
573 cairo_show_page (xr->cairo);
574 cairo_save (xr->cairo);
575 xr_driver_next_page (xr, xr->cairo);
579 /* Functions for rendering a series of output items to a series of Cairo
580 contexts, with pagination.
582 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
583 its underlying implementation.
585 See the big comment in cairo.h for intended usage. */
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). */
591 xr_driver_next_page (struct xr_driver *xr, cairo_t *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);
599 cairo_restore (cairo);
601 cairo_translate (cairo,
602 xr_to_pt (xr->left_margin),
603 xr_to_pt (xr->top_margin));
609 xr_driver_run_fsm (xr);
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()
616 xr_driver_output_item (struct xr_driver *xr,
617 const struct output_item *output_item)
619 assert (xr->fsm == NULL);
620 xr->fsm = xr_render_output_item (xr, output_item);
621 xr_driver_run_fsm (xr);
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,
628 xr_driver_need_new_page (const struct xr_driver *xr)
630 return xr->fsm != NULL;
633 /* Returns true if the current page doesn't have any content yet. */
635 xr_driver_is_page_blank (const struct xr_driver *xr)
641 xr_driver_destroy_fsm (struct xr_driver *xr)
645 xr->fsm->destroy (xr->fsm);
651 xr_driver_run_fsm (struct xr_driver *xr)
653 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
654 xr_driver_destroy_fsm (xr);
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);
663 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
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);
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. */
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,
680 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
681 dump_line (xr, x0, y, x3, y);
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);
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. */
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,
700 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
701 dump_line (xr, x, y0, x, y3);
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);
712 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
713 enum render_line_style styles[TABLE_N_AXES][2])
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];
724 /* The algorithm here is somewhat subtle, to allow it to handle
725 all the kinds of intersections that we need.
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.
733 yc, y1, and y2 are assigned similarly along the y axis.
735 The following diagram shows the coordinate system and output
736 for double top and bottom lines, single left line, and no
740 y0 ________________________
746 y1 = y2 = yc |######### # |
751 y3 |________#_____#_______|
753 struct xr_driver *xr = xr_;
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;
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;
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:
773 y0 ________________________
778 y1 |######### ########|
781 y2 |######################|
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.
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;
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;
806 horz_line (xr, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
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);
814 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
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);
823 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
824 int *min_width, int *max_width)
826 struct xr_driver *xr = xr_;
827 int bb[TABLE_N_AXES][2];
828 int clip[TABLE_N_AXES][2];
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);
839 xr_layout_cell (xr, cell, bb, clip, min_width, &h);
843 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
845 struct xr_driver *xr = xr_;
846 int bb[TABLE_N_AXES][2];
847 int clip[TABLE_N_AXES][2];
854 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
855 xr_layout_cell (xr, cell, bb, clip, &w, &h);
860 xr_draw_cell (void *xr_, const struct table_cell *cell,
861 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
863 struct xr_driver *xr = xr_;
866 xr_layout_cell (xr, cell, bb, clip, &w, &h);
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)
874 struct xr_font *font;
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]);
881 pango_layout_set_text (font->layout, cell->contents, -1);
883 pango_layout_set_alignment (
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 (
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);
893 if (clip[H][0] != clip[H][1])
895 cairo_save (xr->cairo);
897 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
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);
904 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
905 cairo_clip (xr->cairo);
908 cairo_translate (xr->cairo,
910 xr_to_pt (bb[V][0] + xr->y));
911 pango_cairo_show_layout (xr->cairo, font->layout);
912 cairo_restore (xr->cairo);
915 pango_layout_get_size (font->layout, &w, &h);
916 *width = pango_to_xr (w);
917 *height = pango_to_xr (h);
921 xr_draw_title (struct xr_driver *xr, const char *title,
922 int title_width, int title_height)
924 struct table_cell cell;
925 int bb[TABLE_N_AXES][2];
927 xr_init_caption_cell (title, &cell);
929 bb[H][1] = title_width;
931 bb[V][1] = title_height;
932 xr_draw_cell (xr, &cell, bb, bb);
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 };
942 static const struct output_driver_class cairo_driver_class =
950 /* GUI rendering helpers. */
954 struct output_item *item;
957 struct render_page *page;
958 struct xr_driver *xr;
963 #define CHART_WIDTH 500
964 #define CHART_HEIGHT 375
969 xr_driver_create (cairo_t *cairo, struct string_map *options)
971 struct xr_driver *xr = xr_allocate ("cairo", 0, options);
972 if (!xr_set_cairo (xr, cairo))
974 output_driver_destroy (&xr->driver);
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. */
983 xr_driver_destroy (struct xr_driver *xr)
988 output_driver_destroy (&xr->driver);
992 static struct xr_rendering *
993 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
995 struct table_item *table_item;
996 struct xr_rendering *r;
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);
1006 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1008 if (is_table_item (xr->item))
1009 apply_options (xr->xr, o);
1012 struct xr_rendering *
1013 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1016 struct xr_rendering *r = NULL;
1018 if (is_text_item (item))
1019 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1021 else if (is_message_item (item))
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);
1029 else if (is_table_item (item))
1031 r = xzalloc (sizeof *r);
1032 r->item = output_item_ref (item);
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);
1038 else if (is_chart_item (item))
1040 r = xzalloc (sizeof *r);
1041 r->item = output_item_ref (item);
1048 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
1050 if (is_table_item (r->item))
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;
1064 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1065 double x, double y, double width, double height);
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. */
1070 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1071 int x, int y, int w, int h)
1073 if (is_table_item (r->item))
1075 struct xr_driver *xr = r->xr;
1077 xr_set_cairo (xr, cr);
1079 if (r->title_height > 0)
1082 xr_draw_title (xr, table_item_get_caption (to_table_item (r->item)),
1083 r->title_width, r->title_height);
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);
1092 xr_draw_chart (to_chart_item (r->item), cr,
1093 0, 0, CHART_WIDTH, CHART_HEIGHT);
1097 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1098 double x, double y, double width, double height)
1100 struct xrchart_geometry geom;
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);
1122 xrchart_geometry_free (cr, &geom);
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
1134 const int width = 640;
1135 const int length = 480;
1137 cairo_surface_t *surface;
1138 cairo_status_t status;
1139 const char *number_pos;
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);
1148 file_name = xstrdup (file_name_template);
1150 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1151 cr = cairo_create (surface);
1153 cairo_set_source_rgb (cr, bg->red, bg->green, bg->blue);
1156 cairo_set_source_rgb (cr, fg->red, fg->green, fg->blue);
1158 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
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));
1166 cairo_surface_destroy (surface);
1171 struct xr_table_state
1173 struct xr_render_fsm fsm;
1174 struct table_item *table_item;
1175 struct render_break x_break;
1176 struct render_break y_break;
1181 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1183 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1187 struct render_page *y_slice;
1190 while (!render_break_has_next (&ts->y_break))
1192 struct render_page *x_slice;
1194 render_break_destroy (&ts->y_break);
1195 if (!render_break_has_next (&ts->x_break))
1198 x_slice = render_break_next (&ts->x_break, xr->width);
1199 render_break_init (&ts->y_break, x_slice, V);
1202 space = xr->length - xr->y;
1203 if (render_break_next_size (&ts->y_break) > space)
1209 y_slice = render_break_next (&ts->y_break, space);
1210 if (ts->caption_height)
1213 xr_draw_title (xr, table_item_get_caption (ts->table_item),
1214 xr->width, ts->caption_height);
1216 xr->y += ts->caption_height;
1217 ts->caption_height = 0;
1221 render_page_draw (y_slice);
1222 xr->y += render_page_get_size (y_slice, V);
1223 render_page_unref (y_slice);
1228 xr_table_destroy (struct xr_render_fsm *fsm)
1230 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1232 table_item_unref (ts->table_item);
1233 render_break_destroy (&ts->x_break);
1234 render_break_destroy (&ts->y_break);
1238 static struct xr_render_fsm *
1239 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1241 struct xr_table_state *ts;
1242 struct render_page *page;
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);
1251 xr->y += xr->char_height;
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;
1257 render_break_init (&ts->x_break, page, H);
1258 render_break_init_empty (&ts->y_break);
1263 struct xr_chart_state
1265 struct xr_render_fsm fsm;
1266 struct chart_item *chart_item;
1270 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1272 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
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));
1286 xr_chart_destroy (struct xr_render_fsm *fsm)
1288 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1290 chart_item_unref (cs->chart_item);
1294 static struct xr_render_fsm *
1295 xr_render_chart (const struct chart_item *chart_item)
1297 struct xr_chart_state *cs;
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);
1308 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1314 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1316 /* Nothing to do. */
1319 static struct xr_render_fsm *
1320 xr_render_eject (void)
1322 static struct xr_render_fsm eject_renderer =
1328 return &eject_renderer;
1331 static struct xr_render_fsm *
1332 xr_create_text_renderer (struct xr_driver *xr, const char *text)
1334 struct table_item *table_item;
1335 struct xr_render_fsm *fsm;
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);
1344 static struct xr_render_fsm *
1345 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1347 enum text_item_type type = text_item_get_type (text_item);
1348 const char *text = text_item_get_text (text_item);
1352 case TEXT_ITEM_TITLE:
1354 xr->title = xstrdup (text);
1357 case TEXT_ITEM_SUBTITLE:
1358 free (xr->subtitle);
1359 xr->subtitle = xstrdup (text);
1362 case TEXT_ITEM_COMMAND_CLOSE:
1365 case TEXT_ITEM_BLANK_LINE:
1367 xr->y += xr->char_height;
1370 case TEXT_ITEM_EJECT_PAGE:
1372 return xr_render_eject ();
1376 return xr_create_text_renderer (xr, text);
1382 static struct xr_render_fsm *
1383 xr_render_message (struct xr_driver *xr,
1384 const struct message_item *message_item)
1386 const struct msg *msg = message_item_get_msg (message_item);
1387 struct xr_render_fsm *fsm;
1390 s = msg_to_string (msg, xr->command_name);
1391 fsm = xr_create_text_renderer (xr, s);
1397 static struct xr_render_fsm *
1398 xr_render_output_item (struct xr_driver *xr,
1399 const struct output_item *output_item)
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));