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_submit (struct output_driver *driver, const struct output_item *output_item)
537 struct xr_driver *xr = xr_driver_cast (driver);
539 output_driver_track_current_command (output_item, &xr->command_name);
541 xr_driver_output_item (xr, output_item);
542 while (xr_driver_need_new_page (xr))
544 cairo_restore (xr->cairo);
545 cairo_show_page (xr->cairo);
546 cairo_save (xr->cairo);
547 xr_driver_next_page (xr, xr->cairo);
551 /* Functions for rendering a series of output items to a series of Cairo
552 contexts, with pagination.
554 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
555 its underlying implementation.
557 See the big comment in cairo.h for intended usage. */
559 /* Gives new page CAIRO to XR for output. CAIRO may be null to skip actually
560 rendering the page (which might be useful to find out how many pages an
561 output document has without actually rendering it). */
563 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
568 cairo_set_source_rgb (cairo, xr->bg.red, xr->bg.green, xr->bg.blue);
569 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
571 cairo_restore (cairo);
573 cairo_translate (cairo,
574 xr_to_pt (xr->left_margin),
575 xr_to_pt (xr->top_margin));
581 xr_driver_run_fsm (xr);
584 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
585 rendering a previous output item, that is, only if xr_driver_need_new_page()
588 xr_driver_output_item (struct xr_driver *xr,
589 const struct output_item *output_item)
591 assert (xr->fsm == NULL);
592 xr->fsm = xr_render_output_item (xr, output_item);
593 xr_driver_run_fsm (xr);
596 /* Returns true if XR is in the middle of rendering an output item and needs a
597 new page to be appended using xr_driver_next_page() to make progress,
600 xr_driver_need_new_page (const struct xr_driver *xr)
602 return xr->fsm != NULL;
605 /* Returns true if the current page doesn't have any content yet. */
607 xr_driver_is_page_blank (const struct xr_driver *xr)
613 xr_driver_destroy_fsm (struct xr_driver *xr)
617 xr->fsm->destroy (xr->fsm);
623 xr_driver_run_fsm (struct xr_driver *xr)
625 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
626 xr_driver_destroy_fsm (xr);
630 xr_layout_cell (struct xr_driver *, const struct table_cell *,
631 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
632 int *width, int *height, int *brk);
635 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
637 cairo_new_path (xr->cairo);
638 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
639 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
640 cairo_stroke (xr->cairo);
644 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
646 cairo_new_path (xr->cairo);
647 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
648 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y0 + xr->y));
649 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
650 cairo_line_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y1 + xr->y));
651 cairo_close_path (xr->cairo);
652 cairo_stroke (xr->cairo);
655 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
656 shortening it to X0...X1 if SHORTEN is true.
657 Draws a horizontal line X1...X3 at Y if RIGHT says so,
658 shortening it to X2...X3 if SHORTEN is true. */
660 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
661 enum render_line_style left, enum render_line_style right,
664 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
665 dump_line (xr, x0, y, x3, y);
668 if (left != RENDER_LINE_NONE)
669 dump_line (xr, x0, y, shorten ? x1 : x2, y);
670 if (right != RENDER_LINE_NONE)
671 dump_line (xr, shorten ? x2 : x1, y, x3, y);
675 /* Draws a vertical line Y0...Y2 at X if TOP says so,
676 shortening it to Y0...Y1 if SHORTEN is true.
677 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
678 shortening it to Y2...Y3 if SHORTEN is true. */
680 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
681 enum render_line_style top, enum render_line_style bottom,
684 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
685 dump_line (xr, x, y0, x, y3);
688 if (top != RENDER_LINE_NONE)
689 dump_line (xr, x, y0, x, shorten ? y1 : y2);
690 if (bottom != RENDER_LINE_NONE)
691 dump_line (xr, x, shorten ? y2 : y1, x, y3);
696 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
697 enum render_line_style styles[TABLE_N_AXES][2])
699 const int x0 = bb[H][0];
700 const int y0 = bb[V][0];
701 const int x3 = bb[H][1];
702 const int y3 = bb[V][1];
703 const int top = styles[H][0];
704 const int left = styles[V][0];
705 const int bottom = styles[H][1];
706 const int right = styles[V][1];
708 /* The algorithm here is somewhat subtle, to allow it to handle
709 all the kinds of intersections that we need.
711 Three additional ordinates are assigned along the x axis. The
712 first is xc, midway between x0 and x3. The others are x1 and
713 x2; for a single vertical line these are equal to xc, and for
714 a double vertical line they are the ordinates of the left and
715 right half of the double line.
717 yc, y1, and y2 are assigned similarly along the y axis.
719 The following diagram shows the coordinate system and output
720 for double top and bottom lines, single left line, and no
724 y0 ________________________
730 y1 = y2 = yc |######### # |
735 y3 |________#_____#_______|
737 struct xr_driver *xr = xr_;
739 /* Offset from center of each line in a pair of double lines. */
740 int double_line_ofs = (xr->line_space + xr->line_width) / 2;
742 /* Are the lines along each axis single or double?
743 (It doesn't make sense to have different kinds of line on the
744 same axis, so we don't try to gracefully handle that case.) */
745 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
746 bool double_horz = left == RENDER_LINE_DOUBLE || right == RENDER_LINE_DOUBLE;
748 /* When horizontal lines are doubled,
749 the left-side line along y1 normally runs from x0 to x2,
750 and the right-side line along y1 from x3 to x1.
751 If the top-side line is also doubled, we shorten the y1 lines,
752 so that the left-side line runs only to x1,
753 and the right-side line only to x2.
754 Otherwise, the horizontal line at y = y1 below would cut off
755 the intersection, which looks ugly:
757 y0 ________________________
762 y1 |######### ########|
765 y2 |######################|
768 y3 |______________________|
769 It is more of a judgment call when the horizontal line is
770 single. We actually choose to cut off the line anyhow, as
771 shown in the first diagram above.
773 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
774 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
775 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
776 int horz_line_ofs = double_vert ? double_line_ofs : 0;
777 int xc = (x0 + x3) / 2;
778 int x1 = xc - horz_line_ofs;
779 int x2 = xc + horz_line_ofs;
781 bool shorten_x1_lines = left == RENDER_LINE_DOUBLE;
782 bool shorten_x2_lines = right == RENDER_LINE_DOUBLE;
783 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
784 int vert_line_ofs = double_horz ? double_line_ofs : 0;
785 int yc = (y0 + y3) / 2;
786 int y1 = yc - vert_line_ofs;
787 int y2 = yc + vert_line_ofs;
790 horz_line (xr, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
793 horz_line (xr, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
794 horz_line (xr, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
798 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
801 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
802 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
807 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
808 int *min_width, int *max_width)
810 struct xr_driver *xr = xr_;
811 int bb[TABLE_N_AXES][2];
812 int clip[TABLE_N_AXES][2];
819 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
820 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
823 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
827 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
829 struct xr_driver *xr = xr_;
830 int bb[TABLE_N_AXES][2];
831 int clip[TABLE_N_AXES][2];
838 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
839 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
844 xr_draw_cell (void *xr_, const struct table_cell *cell,
845 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
847 struct xr_driver *xr = xr_;
850 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
854 xr_adjust_break (void *xr_, const struct table_cell *cell,
855 int width, int height)
857 struct xr_driver *xr = xr_;
858 int bb[TABLE_N_AXES][2];
859 int clip[TABLE_N_AXES][2];
862 if (xr_measure_cell_height (xr_, cell, width) < height)
869 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
870 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
875 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
877 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
879 double x0 = xr_to_pt (clip[H][0] + xr->x);
880 double y0 = xr_to_pt (clip[V][0] + xr->y);
881 double x1 = xr_to_pt (clip[H][1] + xr->x);
882 double y1 = xr_to_pt (clip[V][1] + xr->y);
884 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
885 cairo_clip (xr->cairo);
890 xr_layout_cell_text (struct xr_driver *xr,
891 const struct cell_contents *contents,
892 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
893 int y, int *widthp, int *brk)
895 unsigned int options = contents->options;
896 struct xr_font *font;
899 font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
900 : options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
901 : &xr->fonts[XR_FONT_PROPORTIONAL]);
903 pango_layout_set_text (font->layout, contents->text, -1);
905 pango_layout_set_alignment (
907 ((options & TAB_ALIGNMENT) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
908 : (options & TAB_ALIGNMENT) == TAB_LEFT ? PANGO_ALIGN_LEFT
909 : PANGO_ALIGN_CENTER));
910 pango_layout_set_width (
912 bb[H][1] == INT_MAX ? -1 : xr_to_pango (bb[H][1] - bb[H][0]));
913 pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
915 if (clip[H][0] != clip[H][1])
917 cairo_save (xr->cairo);
919 cairo_translate (xr->cairo,
920 xr_to_pt (bb[H][0] + xr->x),
921 xr_to_pt (y + xr->y));
922 pango_cairo_show_layout (xr->cairo, font->layout);
924 /* If enabled, this draws a blue rectangle around the extents of each
925 line of text, which can be rather useful for debugging layout
929 PangoLayoutIter *iter;
930 iter = pango_layout_get_iter (font->layout);
933 PangoRectangle extents;
935 pango_layout_iter_get_line_extents (iter, &extents, NULL);
936 cairo_save (xr->cairo);
937 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
939 pango_to_xr (extents.x) - xr->x,
940 pango_to_xr (extents.y) - xr->y,
941 pango_to_xr (extents.x + extents.width) - xr->x,
942 pango_to_xr (extents.y + extents.height) - xr->y);
943 cairo_restore (xr->cairo);
945 while (pango_layout_iter_next_line (iter));
946 pango_layout_iter_free (iter);
949 cairo_restore (xr->cairo);
952 pango_layout_get_size (font->layout, &w, &h);
957 if (y + h >= bb[V][1])
959 PangoLayoutIter *iter;
962 /* Choose a breakpoint between lines instead of in the middle of one. */
963 iter = pango_layout_get_iter (font->layout);
966 PangoRectangle extents;
970 pango_layout_iter_get_line_extents (iter, NULL, &extents);
971 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
972 extents.x = pango_to_xr (extents.x);
973 extents.y = pango_to_xr (y0);
974 extents.width = pango_to_xr (extents.width);
975 extents.height = pango_to_xr (y1 - y0);
976 bottom = y + extents.y + extents.height;
977 if (bottom < bb[V][1])
979 if (brk && clip[H][0] != clip[H][1])
986 while (pango_layout_iter_next_line (iter));
988 /* If enabled, draws a green line across the chosen breakpoint, which can
989 be useful for debugging issues with breaking. */
992 if (best && !xr->nest)
994 cairo_save (xr->cairo);
995 cairo_set_source_rgb (xr->cairo, 0, 1, 0);
996 dump_line (xr, -xr->left_margin, best, xr->width + xr->right_margin, best);
997 cairo_restore (xr->cairo);
1005 xr_layout_cell_subtable (struct xr_driver *xr,
1006 const struct cell_contents *contents,
1007 int bb[TABLE_N_AXES][2],
1008 int clip[TABLE_N_AXES][2], int *widthp, int *brk)
1010 int single_width, double_width;
1011 struct render_params params;
1012 struct render_pager *p;
1013 int r[TABLE_N_AXES][2];
1017 params.draw_line = xr_draw_line;
1018 params.measure_cell_width = xr_measure_cell_width;
1019 params.measure_cell_height = xr_measure_cell_height;
1020 params.adjust_break = NULL;
1021 params.draw_cell = xr_draw_cell;
1023 params.size[H] = bb[H][1] - bb[H][0];
1024 params.size[V] = bb[V][1] - bb[V][0];
1025 params.font_size[H] = xr->char_width;
1026 params.font_size[V] = xr->char_height;
1028 single_width = 2 * xr->line_gutter + xr->line_width;
1029 double_width = 2 * xr->line_gutter + xr->line_space + 2 * xr->line_width;
1030 for (i = 0; i < TABLE_N_AXES; i++)
1032 params.line_widths[i][RENDER_LINE_NONE] = 0;
1033 params.line_widths[i][RENDER_LINE_SINGLE] = single_width;
1034 params.line_widths[i][RENDER_LINE_DOUBLE] = double_width;
1038 p = render_pager_create (¶ms, contents->table);
1039 width = render_pager_get_size (p, H);
1040 height = render_pager_get_size (p, V);
1041 if (bb[V][0] + height >= bb[V][1])
1042 *brk = bb[V][0] + render_pager_get_best_breakpoint (p, bb[V][1] - bb[V][0]);
1044 /* r = intersect(bb, clip) - bb. */
1045 for (i = 0; i < TABLE_N_AXES; i++)
1047 r[i][0] = MAX (bb[i][0], clip[i][0]) - bb[i][0];
1048 r[i][1] = MIN (bb[i][1], clip[i][1]) - bb[i][0];
1051 if (r[H][0] < r[H][1] && r[V][0] < r[V][1])
1053 unsigned int alignment = contents->options & TAB_ALIGNMENT;
1056 cairo_save (xr->cairo);
1059 if (alignment == TAB_RIGHT)
1060 xr->x += params.size[H] - width;
1061 else if (alignment == TAB_CENTER)
1062 xr->x += (params.size[H] - width) / 2;
1064 render_pager_draw_region (p, r[H][0], r[V][0],
1065 r[H][1] - r[H][0], r[V][1] - r[V][0]);
1068 cairo_restore (xr->cairo);
1070 render_pager_destroy (p);
1073 if (width > *widthp)
1075 return bb[V][0] + height;
1079 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1080 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1081 int *width, int *height, int *brk)
1083 int bb[TABLE_N_AXES][2];
1091 memcpy (bb, bb_, sizeof bb);
1093 /* If enabled, draws a blue rectangle around the cell extents, which can be
1094 useful for debugging layout. */
1097 if (clip[H][0] != clip[H][1])
1099 int offset = (xr->nest) * XR_POINT;
1101 cairo_save (xr->cairo);
1102 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1104 bb[H][0] + offset, bb[V][0] + offset,
1105 bb[H][1] - offset, bb[V][1] - offset);
1106 cairo_restore (xr->cairo);
1110 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
1112 const struct cell_contents *contents = &cell->contents[i];
1118 bb[V][0] += xr->char_height / 2;
1119 if (bb[V][0] >= bb[V][1])
1126 bb[V][0] = xr_layout_cell_text (xr, contents, bb, clip,
1127 bb[V][0], width, brk);
1129 bb[V][0] = xr_layout_cell_subtable (xr, contents, bb, clip, width, brk);
1131 *height = bb[V][0] - bb_[V][0];
1134 struct output_driver_factory pdf_driver_factory =
1135 { "pdf", "pspp.pdf", xr_pdf_create };
1136 struct output_driver_factory ps_driver_factory =
1137 { "ps", "pspp.ps", xr_ps_create };
1138 struct output_driver_factory svg_driver_factory =
1139 { "svg", "pspp.svg", xr_svg_create };
1141 static const struct output_driver_class cairo_driver_class =
1149 /* GUI rendering helpers. */
1153 struct output_item *item;
1156 struct render_pager *p;
1157 struct xr_driver *xr;
1160 #define CHART_WIDTH 500
1161 #define CHART_HEIGHT 375
1166 xr_driver_create (cairo_t *cairo, struct string_map *options)
1168 struct xr_driver *xr = xr_allocate ("cairo", 0, options);
1169 if (!xr_set_cairo (xr, cairo))
1171 output_driver_destroy (&xr->driver);
1177 /* Destroy XR, which should have been created with xr_driver_create(). Any
1178 cairo_t added to XR is not destroyed, because it is owned by the client. */
1180 xr_driver_destroy (struct xr_driver *xr)
1185 output_driver_destroy (&xr->driver);
1189 static struct xr_rendering *
1190 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1192 struct table_item *table_item;
1193 struct xr_rendering *r;
1195 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
1196 r = xr_rendering_create (xr, &table_item->output_item, cr);
1197 table_item_unref (table_item);
1203 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1205 if (is_table_item (xr->item))
1206 apply_options (xr->xr, o);
1209 struct xr_rendering *
1210 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1213 struct xr_rendering *r = NULL;
1215 if (is_text_item (item))
1216 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1218 else if (is_message_item (item))
1220 const struct message_item *message_item = to_message_item (item);
1221 const struct msg *msg = message_item_get_msg (message_item);
1222 char *s = msg_to_string (msg, NULL);
1223 r = xr_rendering_create_text (xr, s, cr);
1226 else if (is_table_item (item))
1228 r = xzalloc (sizeof *r);
1229 r->item = output_item_ref (item);
1231 xr_set_cairo (xr, cr);
1232 r->p = render_pager_create (xr->params, to_table_item (item));
1234 else if (is_chart_item (item))
1236 r = xzalloc (sizeof *r);
1237 r->item = output_item_ref (item);
1244 xr_rendering_destroy (struct xr_rendering *r)
1248 output_item_unref (r->item);
1249 render_pager_destroy (r->p);
1255 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
1257 if (is_table_item (r->item))
1259 *w = render_pager_get_size (r->p, H) / XR_POINT;
1260 *h = render_pager_get_size (r->p, V) / XR_POINT;
1269 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1270 double x, double y, double width, double height);
1272 /* Draws onto CR at least the region of R that is enclosed in (X,Y)-(X+W,Y+H),
1273 and possibly some additional parts. */
1275 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1276 int x, int y, int w, int h)
1278 if (is_table_item (r->item))
1280 struct xr_driver *xr = r->xr;
1282 xr_set_cairo (xr, cr);
1285 render_pager_draw_region (r->p,
1286 x * XR_POINT, y * XR_POINT,
1287 w * XR_POINT, h * XR_POINT);
1290 xr_draw_chart (to_chart_item (r->item), cr,
1291 0, 0, CHART_WIDTH, CHART_HEIGHT);
1295 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1296 double x, double y, double width, double height)
1298 struct xrchart_geometry geom;
1301 cairo_translate (cr, x, y + height);
1302 cairo_scale (cr, 1.0, -1.0);
1303 xrchart_geometry_init (cr, &geom, width, height);
1304 if (is_boxplot (chart_item))
1305 xrchart_draw_boxplot (chart_item, cr, &geom);
1306 else if (is_histogram_chart (chart_item))
1307 xrchart_draw_histogram (chart_item, cr, &geom);
1308 else if (is_np_plot_chart (chart_item))
1309 xrchart_draw_np_plot (chart_item, cr, &geom);
1310 else if (is_piechart (chart_item))
1311 xrchart_draw_piechart (chart_item, cr, &geom);
1312 else if (is_roc_chart (chart_item))
1313 xrchart_draw_roc (chart_item, cr, &geom);
1314 else if (is_scree (chart_item))
1315 xrchart_draw_scree (chart_item, cr, &geom);
1316 else if (is_spreadlevel_plot_chart (chart_item))
1317 xrchart_draw_spreadlevel (chart_item, cr, &geom);
1320 xrchart_geometry_free (cr, &geom);
1326 xr_draw_png_chart (const struct chart_item *item,
1327 const char *file_name_template, int number,
1328 const struct xr_color *fg,
1329 const struct xr_color *bg
1332 const int width = 640;
1333 const int length = 480;
1335 cairo_surface_t *surface;
1336 cairo_status_t status;
1337 const char *number_pos;
1341 number_pos = strchr (file_name_template, '#');
1342 if (number_pos != NULL)
1343 file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1344 file_name_template, number, number_pos + 1);
1346 file_name = xstrdup (file_name_template);
1348 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1349 cr = cairo_create (surface);
1351 cairo_set_source_rgb (cr, bg->red, bg->green, bg->blue);
1354 cairo_set_source_rgb (cr, fg->red, fg->green, fg->blue);
1356 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1358 status = cairo_surface_write_to_png (surface, file_name);
1359 if (status != CAIRO_STATUS_SUCCESS)
1360 msg (ME, _("error writing output file `%s': %s"),
1361 file_name, cairo_status_to_string (status));
1364 cairo_surface_destroy (surface);
1369 struct xr_table_state
1371 struct xr_render_fsm fsm;
1372 struct table_item *table_item;
1373 struct render_pager *p;
1377 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1379 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1381 while (render_pager_has_next (ts->p))
1385 used = render_pager_draw_next (ts->p, xr->length - xr->y);
1398 xr_table_destroy (struct xr_render_fsm *fsm)
1400 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1402 table_item_unref (ts->table_item);
1403 render_pager_destroy (ts->p);
1407 static struct xr_render_fsm *
1408 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1410 struct xr_table_state *ts;
1412 ts = xmalloc (sizeof *ts);
1413 ts->fsm.render = xr_table_render;
1414 ts->fsm.destroy = xr_table_destroy;
1415 ts->table_item = table_item_ref (table_item);
1418 xr->y += xr->char_height;
1420 ts->p = render_pager_create (xr->params, table_item);
1425 struct xr_chart_state
1427 struct xr_render_fsm fsm;
1428 struct chart_item *chart_item;
1432 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1434 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1439 if (xr->cairo != NULL)
1440 xr_draw_chart (cs->chart_item, xr->cairo, 0.0, 0.0,
1441 xr_to_pt (xr->width), xr_to_pt (xr->length));
1448 xr_chart_destroy (struct xr_render_fsm *fsm)
1450 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1452 chart_item_unref (cs->chart_item);
1456 static struct xr_render_fsm *
1457 xr_render_chart (const struct chart_item *chart_item)
1459 struct xr_chart_state *cs;
1461 cs = xmalloc (sizeof *cs);
1462 cs->fsm.render = xr_chart_render;
1463 cs->fsm.destroy = xr_chart_destroy;
1464 cs->chart_item = chart_item_ref (chart_item);
1470 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1476 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1478 /* Nothing to do. */
1481 static struct xr_render_fsm *
1482 xr_render_eject (void)
1484 static struct xr_render_fsm eject_renderer =
1490 return &eject_renderer;
1493 static struct xr_render_fsm *
1494 xr_create_text_renderer (struct xr_driver *xr, const char *text)
1496 struct table_item *table_item;
1497 struct xr_render_fsm *fsm;
1499 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
1500 fsm = xr_render_table (xr, table_item);
1501 table_item_unref (table_item);
1506 static struct xr_render_fsm *
1507 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1509 enum text_item_type type = text_item_get_type (text_item);
1510 const char *text = text_item_get_text (text_item);
1514 case TEXT_ITEM_TITLE:
1516 xr->title = xstrdup (text);
1519 case TEXT_ITEM_SUBTITLE:
1520 free (xr->subtitle);
1521 xr->subtitle = xstrdup (text);
1524 case TEXT_ITEM_COMMAND_CLOSE:
1527 case TEXT_ITEM_BLANK_LINE:
1529 xr->y += xr->char_height;
1532 case TEXT_ITEM_EJECT_PAGE:
1534 return xr_render_eject ();
1538 return xr_create_text_renderer (xr, text);
1544 static struct xr_render_fsm *
1545 xr_render_message (struct xr_driver *xr,
1546 const struct message_item *message_item)
1548 const struct msg *msg = message_item_get_msg (message_item);
1549 struct xr_render_fsm *fsm;
1552 s = msg_to_string (msg, xr->command_name);
1553 fsm = xr_create_text_renderer (xr, s);
1559 static struct xr_render_fsm *
1560 xr_render_output_item (struct xr_driver *xr,
1561 const struct output_item *output_item)
1563 if (is_table_item (output_item))
1564 return xr_render_table (xr, to_table_item (output_item));
1565 else if (is_chart_item (output_item))
1566 return xr_render_chart (to_chart_item (output_item));
1567 else if (is_text_item (output_item))
1568 return xr_render_text (xr, to_text_item (output_item));
1569 else if (is_message_item (output_item))
1570 return xr_render_message (xr, to_message_item (output_item));