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;
152 static const struct output_driver_class cairo_driver_class;
154 static void xr_driver_destroy_fsm (struct xr_driver *);
155 static void xr_driver_run_fsm (struct xr_driver *);
157 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
158 enum render_line_style styles[TABLE_N_AXES][2]);
159 static void xr_measure_cell_width (void *, const struct table_cell *,
161 static int xr_measure_cell_height (void *, const struct table_cell *,
163 static void xr_draw_cell (void *, const struct table_cell *,
164 int bb[TABLE_N_AXES][2],
165 int clip[TABLE_N_AXES][2]);
166 static int xr_adjust_break (void *, const struct table_cell *,
167 int width, int height);
169 static struct xr_render_fsm *xr_render_output_item (
170 struct xr_driver *, const struct output_item *);
172 /* Output driver basics. */
174 static struct xr_driver *
175 xr_driver_cast (struct output_driver *driver)
177 assert (driver->class == &cairo_driver_class);
178 return UP_CAST (driver, struct xr_driver, driver);
181 static struct driver_option *
182 opt (struct output_driver *d, struct string_map *options, const char *key,
183 const char *default_value)
185 return driver_option_get (d, options, key, default_value);
188 /* Parse color information specified by KEY into {RED,GREEN,BLUE}.
189 Currently, the input string must be of the form "#RRRRGGGGBBBB"
190 Future implementations might allow things like "yellow" and
191 "sky-blue-ultra-brown"
194 parse_color (struct output_driver *d, struct string_map *options,
195 const char *key, const char *default_value,
196 struct xr_color *color)
198 int red, green, blue;
199 char *string = parse_string (opt (d, options, key, default_value));
201 if (3 != sscanf (string, "#%04x%04x%04x", &red, &green, &blue))
203 /* If the parsed option string fails, then try the default value */
204 if ( 3 != sscanf (default_value, "#%04x%04x%04x", &red, &green, &blue))
206 /* ... and if that fails set everything to zero */
207 red = green = blue = 0;
213 /* Convert 16 bit ints to float */
214 color->red = red / (double) 0xFFFF;
215 color->green = green / (double) 0xFFFF;
216 color->blue = blue / (double) 0xFFFF;
219 static PangoFontDescription *
220 parse_font (struct output_driver *d, struct string_map *options,
221 const char *key, const char *default_value,
224 PangoFontDescription *desc;
227 /* Parse KEY as a font description. */
228 string = parse_string (opt (d, options, key, default_value));
229 desc = pango_font_description_from_string (string);
232 msg (MW, _("`%s': bad font specification"), string);
234 /* Fall back to DEFAULT_VALUE, which had better be a valid font
236 desc = pango_font_description_from_string (default_value);
237 assert (desc != NULL);
241 /* If the font description didn't include an explicit font size, then set it
242 to DEFAULT_SIZE, which is in inch/72000 units. */
243 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
244 pango_font_description_set_size (desc,
245 (default_size / 1000.0) * PANGO_SCALE);
252 apply_options (struct xr_driver *xr, struct string_map *o)
254 struct output_driver *d = &xr->driver;
256 /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
257 int left_margin, right_margin;
258 int top_margin, bottom_margin;
259 int paper_width, paper_length;
261 int min_break[TABLE_N_AXES];
263 /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
264 const double scale = XR_POINT / 1000.;
268 for (i = 0; i < XR_N_FONTS; i++)
270 struct xr_font *font = &xr->fonts[i];
272 if (font->desc != NULL)
273 pango_font_description_free (font->desc);
276 font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
277 xr->fonts[XR_FONT_FIXED].desc = parse_font (d, o, "fixed-font", "monospace",
279 xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font (d, o, "prop-font",
281 xr->fonts[XR_FONT_EMPHASIS].desc = parse_font (d, o, "emph-font",
282 "serif italic", font_size);
284 xr->line_gutter = parse_dimension (opt (d, o, "gutter", "3pt")) * scale;
285 xr->line_space = XR_POINT;
286 xr->line_width = XR_POINT / 2;
289 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg);
290 parse_color (d, o, "foreground-color", "#000000000000", &xr->fg);
292 /* Get dimensions. */
293 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
294 left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
295 right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
296 top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
297 bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
299 min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
300 min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
302 /* Convert to inch/(XR_POINT * 72). */
303 xr->left_margin = left_margin * scale;
304 xr->right_margin = right_margin * scale;
305 xr->top_margin = top_margin * scale;
306 xr->bottom_margin = bottom_margin * scale;
307 xr->width = (paper_width - left_margin - right_margin) * scale;
308 xr->length = (paper_length - top_margin - bottom_margin) * scale;
309 xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
310 xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
313 static struct xr_driver *
314 xr_allocate (const char *name, int device_type, struct string_map *o)
316 struct xr_driver *xr = xzalloc (sizeof *xr);
317 struct output_driver *d = &xr->driver;
319 output_driver_init (d, &cairo_driver_class, name, device_type);
321 apply_options (xr, o);
327 pango_to_xr (int pango)
329 return (XR_POINT != PANGO_SCALE
330 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
337 return (XR_POINT != PANGO_SCALE
338 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
343 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
349 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
353 for (i = 0; i < XR_N_FONTS; i++)
355 struct xr_font *font = &xr->fonts[i];
356 int char_width, char_height;
358 font->layout = pango_cairo_create_layout (cairo);
359 pango_layout_set_font_description (font->layout, font->desc);
361 pango_layout_set_text (font->layout, "0", 1);
362 pango_layout_get_size (font->layout, &char_width, &char_height);
363 xr->char_width = MAX (xr->char_width, pango_to_xr (char_width));
364 xr->char_height = MAX (xr->char_height, pango_to_xr (char_height));
367 if (xr->params == NULL)
369 int single_width, double_width;
371 xr->params = xmalloc (sizeof *xr->params);
372 xr->params->draw_line = xr_draw_line;
373 xr->params->measure_cell_width = xr_measure_cell_width;
374 xr->params->measure_cell_height = xr_measure_cell_height;
375 xr->params->adjust_break = xr_adjust_break;
376 xr->params->draw_cell = xr_draw_cell;
377 xr->params->aux = xr;
378 xr->params->size[H] = xr->width;
379 xr->params->size[V] = xr->length;
380 xr->params->font_size[H] = xr->char_width;
381 xr->params->font_size[V] = xr->char_height;
383 single_width = 2 * xr->line_gutter + xr->line_width;
384 double_width = 2 * xr->line_gutter + xr->line_space + 2 * xr->line_width;
385 for (i = 0; i < TABLE_N_AXES; i++)
387 xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
388 xr->params->line_widths[i][RENDER_LINE_SINGLE] = single_width;
389 xr->params->line_widths[i][RENDER_LINE_DOUBLE] = double_width;
392 for (i = 0; i < TABLE_N_AXES; i++)
393 xr->params->min_break[i] = xr->min_break[i];
396 cairo_set_source_rgb (xr->cairo, xr->fg.red, xr->fg.green, xr->fg.blue);
401 static struct output_driver *
402 xr_create (const char *file_name, enum settings_output_devices device_type,
403 struct string_map *o, enum xr_output_type file_type)
405 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
406 struct xr_driver *xr;
407 cairo_surface_t *surface;
408 cairo_status_t status;
409 double width_pt, length_pt;
411 xr = xr_allocate (file_name, device_type, o);
413 width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
414 length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
415 if (file_type == XR_PDF)
416 surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
417 else if (file_type == XR_PS)
418 surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
419 else if (file_type == XR_SVG)
420 surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
424 status = cairo_surface_status (surface);
425 if (status != CAIRO_STATUS_SUCCESS)
427 msg (ME, _("error opening output file `%s': %s"),
428 file_name, cairo_status_to_string (status));
429 cairo_surface_destroy (surface);
433 xr->cairo = cairo_create (surface);
434 cairo_surface_destroy (surface);
436 if (!xr_set_cairo (xr, xr->cairo))
439 cairo_save (xr->cairo);
440 xr_driver_next_page (xr, xr->cairo);
442 if (xr->width / xr->char_width < MIN_WIDTH)
444 msg (ME, _("The defined page is not wide enough to hold at least %d "
445 "characters in the default font. In fact, there's only "
446 "room for %d characters."),
448 xr->width / xr->char_width);
452 if (xr->length / xr->char_height < MIN_LENGTH)
454 msg (ME, _("The defined page is not long enough to hold at least %d "
455 "lines in the default font. In fact, there's only "
456 "room for %d lines."),
458 xr->length / xr->char_height);
465 output_driver_destroy (&xr->driver);
469 static struct output_driver *
470 xr_pdf_create (const char *file_name, enum settings_output_devices device_type,
471 struct string_map *o)
473 return xr_create (file_name, device_type, o, XR_PDF);
476 static struct output_driver *
477 xr_ps_create (const char *file_name, enum settings_output_devices device_type,
478 struct string_map *o)
480 return xr_create (file_name, device_type, o, XR_PS);
483 static struct output_driver *
484 xr_svg_create (const char *file_name, enum settings_output_devices device_type,
485 struct string_map *o)
487 return xr_create (file_name, device_type, o, XR_SVG);
491 xr_destroy (struct output_driver *driver)
493 struct xr_driver *xr = xr_driver_cast (driver);
496 xr_driver_destroy_fsm (xr);
498 if (xr->cairo != NULL)
500 cairo_status_t status;
502 cairo_surface_finish (cairo_get_target (xr->cairo));
503 status = cairo_status (xr->cairo);
504 if (status != CAIRO_STATUS_SUCCESS)
505 msg (ME, _("error drawing output for %s driver: %s"),
506 output_driver_get_name (driver),
507 cairo_status_to_string (status));
508 cairo_destroy (xr->cairo);
511 free (xr->command_name);
512 for (i = 0; i < XR_N_FONTS; i++)
514 struct xr_font *font = &xr->fonts[i];
516 if (font->desc != NULL)
517 pango_font_description_free (font->desc);
518 if (font->layout != NULL)
519 g_object_unref (font->layout);
527 xr_flush (struct output_driver *driver)
529 struct xr_driver *xr = xr_driver_cast (driver);
531 cairo_surface_flush (cairo_get_target (xr->cairo));
535 xr_init_caption_cell (const char *caption, struct table_cell *cell,
536 struct cell_contents *contents)
538 contents->options = TAB_LEFT;
539 contents->text = CONST_CAST (char *, caption);
540 contents->table = NULL;
541 cell->contents = contents;
542 cell->n_contents = 1;
543 cell->destructor = NULL;
546 static struct render_page *
547 xr_render_table_item (struct xr_driver *xr, const struct table_item *item,
548 int *caption_widthp, int *caption_heightp)
550 const char *caption = table_item_get_caption (item);
554 /* XXX doesn't do well with very large captions */
555 struct cell_contents contents;
556 int min_width, max_width;
557 struct table_cell cell;
559 xr_init_caption_cell (caption, &cell, &contents);
561 xr_measure_cell_width (xr, &cell, &min_width, &max_width);
562 *caption_widthp = MIN (max_width, xr->width);
563 *caption_heightp = xr_measure_cell_height (xr, &cell, *caption_widthp);
566 *caption_heightp = 0;
568 return render_page_create (xr->params, table_item_get_table (item));
572 xr_submit (struct output_driver *driver, const struct output_item *output_item)
574 struct xr_driver *xr = xr_driver_cast (driver);
576 output_driver_track_current_command (output_item, &xr->command_name);
578 xr_driver_output_item (xr, output_item);
579 while (xr_driver_need_new_page (xr))
581 cairo_restore (xr->cairo);
582 cairo_show_page (xr->cairo);
583 cairo_save (xr->cairo);
584 xr_driver_next_page (xr, xr->cairo);
588 /* Functions for rendering a series of output items to a series of Cairo
589 contexts, with pagination.
591 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
592 its underlying implementation.
594 See the big comment in cairo.h for intended usage. */
596 /* Gives new page CAIRO to XR for output. CAIRO may be null to skip actually
597 rendering the page (which might be useful to find out how many pages an
598 output document has without actually rendering it). */
600 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
605 cairo_set_source_rgb (cairo, xr->bg.red, xr->bg.green, xr->bg.blue);
606 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
608 cairo_restore (cairo);
610 cairo_translate (cairo,
611 xr_to_pt (xr->left_margin),
612 xr_to_pt (xr->top_margin));
618 xr_driver_run_fsm (xr);
621 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
622 rendering a previous output item, that is, only if xr_driver_need_new_page()
625 xr_driver_output_item (struct xr_driver *xr,
626 const struct output_item *output_item)
628 assert (xr->fsm == NULL);
629 xr->fsm = xr_render_output_item (xr, output_item);
630 xr_driver_run_fsm (xr);
633 /* Returns true if XR is in the middle of rendering an output item and needs a
634 new page to be appended using xr_driver_next_page() to make progress,
637 xr_driver_need_new_page (const struct xr_driver *xr)
639 return xr->fsm != NULL;
642 /* Returns true if the current page doesn't have any content yet. */
644 xr_driver_is_page_blank (const struct xr_driver *xr)
650 xr_driver_destroy_fsm (struct xr_driver *xr)
654 xr->fsm->destroy (xr->fsm);
660 xr_driver_run_fsm (struct xr_driver *xr)
662 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
663 xr_driver_destroy_fsm (xr);
667 xr_layout_cell (struct xr_driver *, const struct table_cell *,
668 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
669 int *width, int *height, int *brk);
672 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
674 cairo_new_path (xr->cairo);
675 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
676 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
677 cairo_stroke (xr->cairo);
681 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
683 cairo_new_path (xr->cairo);
684 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
685 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y0 + xr->y));
686 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
687 cairo_line_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y1 + xr->y));
688 cairo_close_path (xr->cairo);
689 cairo_stroke (xr->cairo);
692 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
693 shortening it to X0...X1 if SHORTEN is true.
694 Draws a horizontal line X1...X3 at Y if RIGHT says so,
695 shortening it to X2...X3 if SHORTEN is true. */
697 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
698 enum render_line_style left, enum render_line_style right,
701 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
702 dump_line (xr, x0, y, x3, y);
705 if (left != RENDER_LINE_NONE)
706 dump_line (xr, x0, y, shorten ? x1 : x2, y);
707 if (right != RENDER_LINE_NONE)
708 dump_line (xr, shorten ? x2 : x1, y, x3, y);
712 /* Draws a vertical line Y0...Y2 at X if TOP says so,
713 shortening it to Y0...Y1 if SHORTEN is true.
714 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
715 shortening it to Y2...Y3 if SHORTEN is true. */
717 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
718 enum render_line_style top, enum render_line_style bottom,
721 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
722 dump_line (xr, x, y0, x, y3);
725 if (top != RENDER_LINE_NONE)
726 dump_line (xr, x, y0, x, shorten ? y1 : y2);
727 if (bottom != RENDER_LINE_NONE)
728 dump_line (xr, x, shorten ? y2 : y1, x, y3);
733 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
734 enum render_line_style styles[TABLE_N_AXES][2])
736 const int x0 = bb[H][0];
737 const int y0 = bb[V][0];
738 const int x3 = bb[H][1];
739 const int y3 = bb[V][1];
740 const int top = styles[H][0];
741 const int left = styles[V][0];
742 const int bottom = styles[H][1];
743 const int right = styles[V][1];
745 /* The algorithm here is somewhat subtle, to allow it to handle
746 all the kinds of intersections that we need.
748 Three additional ordinates are assigned along the x axis. The
749 first is xc, midway between x0 and x3. The others are x1 and
750 x2; for a single vertical line these are equal to xc, and for
751 a double vertical line they are the ordinates of the left and
752 right half of the double line.
754 yc, y1, and y2 are assigned similarly along the y axis.
756 The following diagram shows the coordinate system and output
757 for double top and bottom lines, single left line, and no
761 y0 ________________________
767 y1 = y2 = yc |######### # |
772 y3 |________#_____#_______|
774 struct xr_driver *xr = xr_;
776 /* Offset from center of each line in a pair of double lines. */
777 int double_line_ofs = (xr->line_space + xr->line_width) / 2;
779 /* Are the lines along each axis single or double?
780 (It doesn't make sense to have different kinds of line on the
781 same axis, so we don't try to gracefully handle that case.) */
782 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
783 bool double_horz = left == RENDER_LINE_DOUBLE || right == RENDER_LINE_DOUBLE;
785 /* When horizontal lines are doubled,
786 the left-side line along y1 normally runs from x0 to x2,
787 and the right-side line along y1 from x3 to x1.
788 If the top-side line is also doubled, we shorten the y1 lines,
789 so that the left-side line runs only to x1,
790 and the right-side line only to x2.
791 Otherwise, the horizontal line at y = y1 below would cut off
792 the intersection, which looks ugly:
794 y0 ________________________
799 y1 |######### ########|
802 y2 |######################|
805 y3 |______________________|
806 It is more of a judgment call when the horizontal line is
807 single. We actually choose to cut off the line anyhow, as
808 shown in the first diagram above.
810 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
811 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
812 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
813 int horz_line_ofs = double_vert ? double_line_ofs : 0;
814 int xc = (x0 + x3) / 2;
815 int x1 = xc - horz_line_ofs;
816 int x2 = xc + horz_line_ofs;
818 bool shorten_x1_lines = left == RENDER_LINE_DOUBLE;
819 bool shorten_x2_lines = right == RENDER_LINE_DOUBLE;
820 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
821 int vert_line_ofs = double_horz ? double_line_ofs : 0;
822 int yc = (y0 + y3) / 2;
823 int y1 = yc - vert_line_ofs;
824 int y2 = yc + vert_line_ofs;
827 horz_line (xr, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
830 horz_line (xr, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
831 horz_line (xr, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
835 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
838 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
839 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
844 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
845 int *min_width, int *max_width)
847 struct xr_driver *xr = xr_;
848 int bb[TABLE_N_AXES][2];
849 int clip[TABLE_N_AXES][2];
856 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
857 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
860 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
864 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
866 struct xr_driver *xr = xr_;
867 int bb[TABLE_N_AXES][2];
868 int clip[TABLE_N_AXES][2];
875 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
876 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
881 xr_draw_cell (void *xr_, const struct table_cell *cell,
882 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
884 struct xr_driver *xr = xr_;
887 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
891 xr_adjust_break (void *xr_, const struct table_cell *cell,
892 int width, int height)
894 struct xr_driver *xr = xr_;
895 int bb[TABLE_N_AXES][2];
896 int clip[TABLE_N_AXES][2];
899 if (xr_measure_cell_height (xr_, cell, width) < height)
906 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
907 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
912 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
914 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
916 double x0 = xr_to_pt (clip[H][0] + xr->x);
917 double y0 = xr_to_pt (clip[V][0] + xr->y);
918 double x1 = xr_to_pt (clip[H][1] + xr->x);
919 double y1 = xr_to_pt (clip[V][1] + xr->y);
921 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
922 cairo_clip (xr->cairo);
927 xr_layout_cell_text (struct xr_driver *xr,
928 const struct cell_contents *contents,
929 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
930 int y, int *widthp, int *brk)
932 unsigned int options = contents->options;
933 struct xr_font *font;
936 font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
937 : options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
938 : &xr->fonts[XR_FONT_PROPORTIONAL]);
940 pango_layout_set_text (font->layout, contents->text, -1);
942 pango_layout_set_alignment (
944 ((options & TAB_ALIGNMENT) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
945 : (options & TAB_ALIGNMENT) == TAB_LEFT ? PANGO_ALIGN_LEFT
946 : PANGO_ALIGN_CENTER));
947 pango_layout_set_width (
949 bb[H][1] == INT_MAX ? -1 : xr_to_pango (bb[H][1] - bb[H][0]));
950 pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
952 if (clip[H][0] != clip[H][1])
954 cairo_save (xr->cairo);
956 cairo_translate (xr->cairo,
957 xr_to_pt (bb[H][0] + xr->x),
958 xr_to_pt (y + xr->y));
959 pango_cairo_show_layout (xr->cairo, font->layout);
961 /* If enabled, this draws a blue rectangle around the extents of each
962 line of text, which can be rather useful for debugging layout
966 PangoLayoutIter *iter;
967 iter = pango_layout_get_iter (font->layout);
970 PangoRectangle extents;
972 pango_layout_iter_get_line_extents (iter, &extents, NULL);
973 cairo_save (xr->cairo);
974 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
976 pango_to_xr (extents.x) - xr->x,
977 pango_to_xr (extents.y) - xr->y,
978 pango_to_xr (extents.x + extents.width) - xr->x,
979 pango_to_xr (extents.y + extents.height) - xr->y);
980 cairo_restore (xr->cairo);
982 while (pango_layout_iter_next_line (iter));
983 pango_layout_iter_free (iter);
986 cairo_restore (xr->cairo);
989 pango_layout_get_size (font->layout, &w, &h);
994 if (y + h >= bb[V][1])
996 PangoLayoutIter *iter;
999 /* Choose a breakpoint between lines instead of in the middle of one. */
1000 iter = pango_layout_get_iter (font->layout);
1003 PangoRectangle extents;
1007 pango_layout_iter_get_line_extents (iter, NULL, &extents);
1008 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1009 extents.x = pango_to_xr (extents.x);
1010 extents.y = pango_to_xr (y0);
1011 extents.width = pango_to_xr (extents.width);
1012 extents.height = pango_to_xr (y1 - y0);
1013 bottom = y + extents.y + extents.height;
1014 if (bottom < bb[V][1])
1016 if (brk && clip[H][0] != clip[H][1])
1023 while (pango_layout_iter_next_line (iter));
1025 /* If enabled, draws a green line across the chosen breakpoint, which can
1026 be useful for debugging issues with breaking. */
1029 if (best && !xr->nest)
1031 cairo_save (xr->cairo);
1032 cairo_set_source_rgb (xr->cairo, 0, 1, 0);
1033 dump_line (xr, -xr->left_margin, best, xr->width + xr->right_margin, best);
1034 cairo_restore (xr->cairo);
1042 xr_layout_cell_subtable (struct xr_driver *xr,
1043 const struct cell_contents *contents,
1044 int bb[TABLE_N_AXES][2],
1045 int clip[TABLE_N_AXES][2], int *widthp, int *brk)
1047 const struct table *table = contents->table;
1048 int single_width, double_width;
1049 struct render_params params;
1050 struct render_page *page;
1051 int r[TABLE_N_AXES][2];
1055 params.draw_line = xr_draw_line;
1056 params.measure_cell_width = xr_measure_cell_width;
1057 params.measure_cell_height = xr_measure_cell_height;
1058 params.adjust_break = NULL;
1059 params.draw_cell = xr_draw_cell;
1061 params.size[H] = bb[H][1] - bb[H][0];
1062 params.size[V] = bb[V][1] - bb[V][0];
1063 params.font_size[H] = xr->char_width;
1064 params.font_size[V] = xr->char_height;
1066 single_width = 2 * xr->line_gutter + xr->line_width;
1067 double_width = 2 * xr->line_gutter + xr->line_space + 2 * xr->line_width;
1068 for (i = 0; i < TABLE_N_AXES; i++)
1070 params.line_widths[i][RENDER_LINE_NONE] = 0;
1071 params.line_widths[i][RENDER_LINE_SINGLE] = single_width;
1072 params.line_widths[i][RENDER_LINE_DOUBLE] = double_width;
1076 page = render_page_create (¶ms, table);
1077 width = render_page_get_size (page, H);
1078 height = render_page_get_size (page, V);
1079 if (bb[V][0] + height >= bb[V][1])
1080 *brk = bb[V][0] + render_page_get_best_breakpoint (page, bb[V][1] - bb[V][0]);
1082 /* r = intersect(bb, clip) - bb. */
1083 for (i = 0; i < TABLE_N_AXES; i++)
1085 r[i][0] = MAX (bb[i][0], clip[i][0]) - bb[i][0];
1086 r[i][1] = MIN (bb[i][1], clip[i][1]) - bb[i][0];
1089 if (r[H][0] < r[H][1] && r[V][0] < r[V][1])
1091 unsigned int alignment = contents->options & TAB_ALIGNMENT;
1094 cairo_save (xr->cairo);
1097 if (alignment == TAB_RIGHT)
1098 xr->x += params.size[H] - width;
1099 else if (alignment == TAB_CENTER)
1100 xr->x += (params.size[H] - width) / 2;
1102 render_page_draw_region (page, r[H][0], r[V][0],
1103 r[H][1] - r[H][0], r[V][1] - r[V][0]);
1106 cairo_restore (xr->cairo);
1108 render_page_unref (page);
1111 if (width > *widthp)
1113 return bb[V][0] + height;
1117 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1118 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1119 int *width, int *height, int *brk)
1121 int bb[TABLE_N_AXES][2];
1129 memcpy (bb, bb_, sizeof bb);
1131 /* If enabled, draws a blue rectangle around the cell extents, which can be
1132 useful for debugging layout. */
1135 if (clip[H][0] != clip[H][1])
1137 int offset = (xr->nest) * XR_POINT;
1139 cairo_save (xr->cairo);
1140 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1142 bb[H][0] + offset, bb[V][0] + offset,
1143 bb[H][1] - offset, bb[V][1] - offset);
1144 cairo_restore (xr->cairo);
1148 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
1150 const struct cell_contents *contents = &cell->contents[i];
1156 bb[V][0] += xr->char_height / 2;
1157 if (bb[V][0] >= bb[V][1])
1164 bb[V][0] = xr_layout_cell_text (xr, contents, bb, clip,
1165 bb[V][0], width, brk);
1167 bb[V][0] = xr_layout_cell_subtable (xr, contents, bb, clip, width, brk);
1169 *height = bb[V][0] - bb_[V][0];
1173 xr_draw_title (struct xr_driver *xr, const char *title,
1174 int title_width, int title_height)
1176 struct cell_contents contents;
1177 struct table_cell cell;
1178 int bb[TABLE_N_AXES][2];
1180 xr_init_caption_cell (title, &cell, &contents);
1182 bb[H][1] = title_width;
1184 bb[V][1] = title_height;
1185 xr_draw_cell (xr, &cell, bb, bb);
1188 struct output_driver_factory pdf_driver_factory =
1189 { "pdf", "pspp.pdf", xr_pdf_create };
1190 struct output_driver_factory ps_driver_factory =
1191 { "ps", "pspp.ps", xr_ps_create };
1192 struct output_driver_factory svg_driver_factory =
1193 { "svg", "pspp.svg", xr_svg_create };
1195 static const struct output_driver_class cairo_driver_class =
1203 /* GUI rendering helpers. */
1207 struct output_item *item;
1210 struct render_page *page;
1211 struct xr_driver *xr;
1216 #define CHART_WIDTH 500
1217 #define CHART_HEIGHT 375
1222 xr_driver_create (cairo_t *cairo, struct string_map *options)
1224 struct xr_driver *xr = xr_allocate ("cairo", 0, options);
1225 if (!xr_set_cairo (xr, cairo))
1227 output_driver_destroy (&xr->driver);
1233 /* Destroy XR, which should have been created with xr_driver_create(). Any
1234 cairo_t added to XR is not destroyed, because it is owned by the client. */
1236 xr_driver_destroy (struct xr_driver *xr)
1241 output_driver_destroy (&xr->driver);
1245 static struct xr_rendering *
1246 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1248 struct table_item *table_item;
1249 struct xr_rendering *r;
1251 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
1252 r = xr_rendering_create (xr, &table_item->output_item, cr);
1253 table_item_unref (table_item);
1259 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1261 if (is_table_item (xr->item))
1262 apply_options (xr->xr, o);
1265 struct xr_rendering *
1266 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1269 struct xr_rendering *r = NULL;
1271 if (is_text_item (item))
1272 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1274 else if (is_message_item (item))
1276 const struct message_item *message_item = to_message_item (item);
1277 const struct msg *msg = message_item_get_msg (message_item);
1278 char *s = msg_to_string (msg, NULL);
1279 r = xr_rendering_create_text (xr, s, cr);
1282 else if (is_table_item (item))
1284 r = xzalloc (sizeof *r);
1285 r->item = output_item_ref (item);
1287 xr_set_cairo (xr, cr);
1288 r->page = xr_render_table_item (xr, to_table_item (item),
1289 &r->title_width, &r->title_height);
1291 else if (is_chart_item (item))
1293 r = xzalloc (sizeof *r);
1294 r->item = output_item_ref (item);
1301 xr_rendering_destroy (struct xr_rendering *r)
1305 output_item_unref (r->item);
1306 render_page_unref (r->page);
1312 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
1314 if (is_table_item (r->item))
1316 int w0 = render_page_get_size (r->page, H);
1317 int w1 = r->title_width;
1318 *w = MAX (w0, w1) / XR_POINT;
1319 *h = (render_page_get_size (r->page, V) + r->title_height) / XR_POINT;
1328 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1329 double x, double y, double width, double height);
1331 /* Draws onto CR at least the region of R that is enclosed in (X,Y)-(X+W,Y+H),
1332 and possibly some additional parts. */
1334 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1335 int x, int y, int w, int h)
1337 if (is_table_item (r->item))
1339 struct xr_driver *xr = r->xr;
1341 xr_set_cairo (xr, cr);
1343 if (r->title_height > 0)
1346 xr_draw_title (xr, table_item_get_caption (to_table_item (r->item)),
1347 r->title_width, r->title_height);
1350 xr->y = r->title_height;
1351 render_page_draw_region (r->page,
1352 x * XR_POINT, (y * XR_POINT) - r->title_height,
1353 w * XR_POINT, h * XR_POINT);
1356 xr_draw_chart (to_chart_item (r->item), cr,
1357 0, 0, CHART_WIDTH, CHART_HEIGHT);
1361 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1362 double x, double y, double width, double height)
1364 struct xrchart_geometry geom;
1367 cairo_translate (cr, x, y + height);
1368 cairo_scale (cr, 1.0, -1.0);
1369 xrchart_geometry_init (cr, &geom, width, height);
1370 if (is_boxplot (chart_item))
1371 xrchart_draw_boxplot (chart_item, cr, &geom);
1372 else if (is_histogram_chart (chart_item))
1373 xrchart_draw_histogram (chart_item, cr, &geom);
1374 else if (is_np_plot_chart (chart_item))
1375 xrchart_draw_np_plot (chart_item, cr, &geom);
1376 else if (is_piechart (chart_item))
1377 xrchart_draw_piechart (chart_item, cr, &geom);
1378 else if (is_roc_chart (chart_item))
1379 xrchart_draw_roc (chart_item, cr, &geom);
1380 else if (is_scree (chart_item))
1381 xrchart_draw_scree (chart_item, cr, &geom);
1382 else if (is_spreadlevel_plot_chart (chart_item))
1383 xrchart_draw_spreadlevel (chart_item, cr, &geom);
1386 xrchart_geometry_free (cr, &geom);
1392 xr_draw_png_chart (const struct chart_item *item,
1393 const char *file_name_template, int number,
1394 const struct xr_color *fg,
1395 const struct xr_color *bg
1398 const int width = 640;
1399 const int length = 480;
1401 cairo_surface_t *surface;
1402 cairo_status_t status;
1403 const char *number_pos;
1407 number_pos = strchr (file_name_template, '#');
1408 if (number_pos != NULL)
1409 file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1410 file_name_template, number, number_pos + 1);
1412 file_name = xstrdup (file_name_template);
1414 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1415 cr = cairo_create (surface);
1417 cairo_set_source_rgb (cr, bg->red, bg->green, bg->blue);
1420 cairo_set_source_rgb (cr, fg->red, fg->green, fg->blue);
1422 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1424 status = cairo_surface_write_to_png (surface, file_name);
1425 if (status != CAIRO_STATUS_SUCCESS)
1426 msg (ME, _("error writing output file `%s': %s"),
1427 file_name, cairo_status_to_string (status));
1430 cairo_surface_destroy (surface);
1435 struct xr_table_state
1437 struct xr_render_fsm fsm;
1438 struct table_item *table_item;
1439 struct render_break x_break;
1440 struct render_break y_break;
1445 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1447 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1451 struct render_page *y_slice;
1454 while (!render_break_has_next (&ts->y_break))
1456 struct render_page *x_slice;
1458 render_break_destroy (&ts->y_break);
1459 if (!render_break_has_next (&ts->x_break))
1462 x_slice = render_break_next (&ts->x_break, xr->width);
1463 render_break_init (&ts->y_break, x_slice, V);
1466 space = xr->length - xr->y;
1467 if (render_break_next_size (&ts->y_break) > space)
1473 y_slice = render_break_next (&ts->y_break, space);
1474 if (ts->caption_height)
1477 xr_draw_title (xr, table_item_get_caption (ts->table_item),
1478 xr->width, ts->caption_height);
1480 xr->y += ts->caption_height;
1481 ts->caption_height = 0;
1485 render_page_draw (y_slice);
1486 xr->y += render_page_get_size (y_slice, V);
1487 render_page_unref (y_slice);
1492 xr_table_destroy (struct xr_render_fsm *fsm)
1494 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1496 table_item_unref (ts->table_item);
1497 render_break_destroy (&ts->x_break);
1498 render_break_destroy (&ts->y_break);
1502 static struct xr_render_fsm *
1503 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1505 struct xr_table_state *ts;
1506 struct render_page *page;
1509 ts = xmalloc (sizeof *ts);
1510 ts->fsm.render = xr_table_render;
1511 ts->fsm.destroy = xr_table_destroy;
1512 ts->table_item = table_item_ref (table_item);
1515 xr->y += xr->char_height;
1517 page = xr_render_table_item (xr, table_item,
1518 &caption_width, &ts->caption_height);
1519 xr->params->size[V] = xr->length - ts->caption_height;
1521 render_break_init (&ts->x_break, page, H);
1522 render_break_init_empty (&ts->y_break);
1527 struct xr_chart_state
1529 struct xr_render_fsm fsm;
1530 struct chart_item *chart_item;
1534 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1536 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
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));
1550 xr_chart_destroy (struct xr_render_fsm *fsm)
1552 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1554 chart_item_unref (cs->chart_item);
1558 static struct xr_render_fsm *
1559 xr_render_chart (const struct chart_item *chart_item)
1561 struct xr_chart_state *cs;
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);
1572 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1578 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1580 /* Nothing to do. */
1583 static struct xr_render_fsm *
1584 xr_render_eject (void)
1586 static struct xr_render_fsm eject_renderer =
1592 return &eject_renderer;
1595 static struct xr_render_fsm *
1596 xr_create_text_renderer (struct xr_driver *xr, const char *text)
1598 struct table_item *table_item;
1599 struct xr_render_fsm *fsm;
1601 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
1602 fsm = xr_render_table (xr, table_item);
1603 table_item_unref (table_item);
1608 static struct xr_render_fsm *
1609 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1611 enum text_item_type type = text_item_get_type (text_item);
1612 const char *text = text_item_get_text (text_item);
1616 case TEXT_ITEM_TITLE:
1618 xr->title = xstrdup (text);
1621 case TEXT_ITEM_SUBTITLE:
1622 free (xr->subtitle);
1623 xr->subtitle = xstrdup (text);
1626 case TEXT_ITEM_COMMAND_CLOSE:
1629 case TEXT_ITEM_BLANK_LINE:
1631 xr->y += xr->char_height;
1634 case TEXT_ITEM_EJECT_PAGE:
1636 return xr_render_eject ();
1640 return xr_create_text_renderer (xr, text);
1646 static struct xr_render_fsm *
1647 xr_render_message (struct xr_driver *xr,
1648 const struct message_item *message_item)
1650 const struct msg *msg = message_item_get_msg (message_item);
1651 struct xr_render_fsm *fsm;
1654 s = msg_to_string (msg, xr->command_name);
1655 fsm = xr_create_text_renderer (xr, s);
1661 static struct xr_render_fsm *
1662 xr_render_output_item (struct xr_driver *xr,
1663 const struct output_item *output_item)
1665 if (is_table_item (output_item))
1666 return xr_render_table (xr, to_table_item (output_item));
1667 else if (is_chart_item (output_item))
1668 return xr_render_chart (to_chart_item (output_item));
1669 else if (is_text_item (output_item))
1670 return xr_render_text (xr, to_text_item (output_item));
1671 else if (is_message_item (output_item))
1672 return xr_render_message (xr, to_message_item (output_item));