1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010, 2011, 2012, 2013 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>
50 #include <pango/pango-font.h>
51 #include <pango/pango-layout.h>
52 #include <pango/pango.h>
53 #include <pango/pangocairo.h>
56 #include "gl/intprops.h"
57 #include "gl/minmax.h"
58 #include "gl/xalloc.h"
61 #define _(msgid) gettext (msgid)
63 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
67 /* Measurements as we present to the rest of PSPP. */
70 /* Conversions to and from points. */
74 return x / (double) XR_POINT;
94 /* A font for use with Cairo. */
97 PangoFontDescription *desc;
101 /* An output item whose rendering is in progress. */
104 /* Renders as much of itself as it can on the current page. Returns true
105 if rendering is complete, false if the output item needs another
107 bool (*render) (struct xr_render_fsm *, struct xr_driver *);
109 /* Destroys the output item. */
110 void (*destroy) (struct xr_render_fsm *);
120 /* Cairo output driver. */
123 struct output_driver driver;
125 /* User parameters. */
126 struct xr_font fonts[XR_N_FONTS];
128 int width; /* Page width minus margins. */
129 int length; /* Page length minus margins and header. */
131 int left_margin; /* Left margin in XR units. */
132 int right_margin; /* Right margin in XR units. */
133 int top_margin; /* Top margin in XR units. */
134 int bottom_margin; /* Bottom margin in XR units. */
136 int line_gutter; /* Space around lines. */
137 int line_space; /* Space between lines. */
138 int line_width; /* Width of lines. */
140 struct xr_color bg; /* Background color */
141 struct xr_color fg; /* Foreground color */
143 /* Internal state. */
144 struct render_params *params;
145 int char_width, char_height;
150 int page_number; /* Current page number. */
152 struct xr_render_fsm *fsm;
155 static const struct output_driver_class cairo_driver_class;
157 static void xr_driver_destroy_fsm (struct xr_driver *);
158 static void xr_driver_run_fsm (struct xr_driver *);
160 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
161 enum render_line_style styles[TABLE_N_AXES][2]);
162 static void xr_measure_cell_width (void *, const struct table_cell *,
164 static int xr_measure_cell_height (void *, const struct table_cell *,
166 static void xr_draw_cell (void *, const struct table_cell *,
167 int bb[TABLE_N_AXES][2],
168 int clip[TABLE_N_AXES][2]);
170 static struct xr_render_fsm *xr_render_output_item (
171 struct xr_driver *, const struct output_item *);
173 /* Output driver basics. */
175 static struct xr_driver *
176 xr_driver_cast (struct output_driver *driver)
178 assert (driver->class == &cairo_driver_class);
179 return UP_CAST (driver, struct xr_driver, driver);
182 static struct driver_option *
183 opt (struct output_driver *d, struct string_map *options, const char *key,
184 const char *default_value)
186 return driver_option_get (d, options, key, default_value);
189 /* Parse color information specified by KEY into {RED,GREEN,BLUE}.
190 Currently, the input string must be of the form "#RRRRGGGGBBBB"
191 Future implementations might allow things like "yellow" and
192 "sky-blue-ultra-brown"
195 parse_color (struct output_driver *d, struct string_map *options,
196 const char *key, const char *default_value,
197 struct xr_color *color)
199 int red, green, blue;
200 char *string = parse_string (opt (d, options, key, default_value));
202 if (3 != sscanf (string, "#%04x%04x%04x", &red, &green, &blue))
204 /* If the parsed option string fails, then try the default value */
205 if ( 3 != sscanf (default_value, "#%04x%04x%04x", &red, &green, &blue))
207 /* ... and if that fails set everything to zero */
208 red = green = blue = 0;
214 /* Convert 16 bit ints to float */
215 color->red = red / (double) 0xFFFF;
216 color->green = green / (double) 0xFFFF;
217 color->blue = blue / (double) 0xFFFF;
220 static PangoFontDescription *
221 parse_font (struct output_driver *d, struct string_map *options,
222 const char *key, const char *default_value,
225 PangoFontDescription *desc;
228 /* Parse KEY as a font description. */
229 string = parse_string (opt (d, options, key, default_value));
230 desc = pango_font_description_from_string (string);
233 msg (MW, _("`%s': bad font specification"), string);
235 /* Fall back to DEFAULT_VALUE, which had better be a valid font
237 desc = pango_font_description_from_string (default_value);
238 assert (desc != NULL);
242 /* If the font description didn't include an explicit font size, then set it
243 to DEFAULT_POINTS. */
244 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
245 pango_font_description_set_size (desc,
246 default_points / 1000.0 * PANGO_SCALE);
253 apply_options (struct xr_driver *xr, struct string_map *o)
255 struct output_driver *d = &xr->driver;
257 int paper_width, paper_length, i;
259 int font_points = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
261 for (i = 0; i < XR_N_FONTS; i++)
263 struct xr_font *font = &xr->fonts[i];
265 if (font->desc != NULL)
266 pango_font_description_free (font->desc);
269 xr->fonts[XR_FONT_FIXED].desc = parse_font (d, o, "fixed-font", "monospace",
271 xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font (d, o, "prop-font",
272 "serif", font_points);
273 xr->fonts[XR_FONT_EMPHASIS].desc = parse_font (d, o, "emph-font",
274 "serif italic", font_points);
276 xr->line_gutter = parse_dimension (opt (d, o, "gutter", "3pt"));
277 xr->line_space = XR_POINT;
278 xr->line_width = XR_POINT / 2;
281 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg);
282 parse_color (d, o, "foreground-color", "#000000000000", &xr->fg);
284 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
285 xr->left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
286 xr->right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
287 xr->top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
288 xr->bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
290 xr->width = paper_width - xr->left_margin - xr->right_margin;
291 xr->length = paper_length - xr->top_margin - xr->bottom_margin;
294 static struct xr_driver *
295 xr_allocate (const char *name, int device_type, struct string_map *o)
297 struct xr_driver *xr = xzalloc (sizeof *xr);
298 struct output_driver *d = &xr->driver;
300 output_driver_init (d, &cairo_driver_class, name, device_type);
302 apply_options (xr, o);
308 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
314 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
318 for (i = 0; i < XR_N_FONTS; i++)
320 struct xr_font *font = &xr->fonts[i];
321 int char_width, char_height;
323 font->layout = pango_cairo_create_layout (cairo);
324 pango_layout_set_font_description (font->layout, font->desc);
326 pango_layout_set_text (font->layout, "0", 1);
327 pango_layout_get_size (font->layout, &char_width, &char_height);
328 xr->char_width = MAX (xr->char_width, char_width);
329 xr->char_height = MAX (xr->char_height, char_height);
332 if (xr->params == NULL)
334 int single_width, double_width;
336 xr->params = xmalloc (sizeof *xr->params);
337 xr->params->draw_line = xr_draw_line;
338 xr->params->measure_cell_width = xr_measure_cell_width;
339 xr->params->measure_cell_height = xr_measure_cell_height;
340 xr->params->draw_cell = xr_draw_cell;
341 xr->params->aux = xr;
342 xr->params->size[H] = xr->width;
343 xr->params->size[V] = xr->length;
344 xr->params->font_size[H] = xr->char_width;
345 xr->params->font_size[V] = xr->char_height;
347 single_width = 2 * xr->line_gutter + xr->line_width;
348 double_width = 2 * xr->line_gutter + xr->line_space + 2 * xr->line_width;
349 for (i = 0; i < TABLE_N_AXES; i++)
351 xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
352 xr->params->line_widths[i][RENDER_LINE_SINGLE] = single_width;
353 xr->params->line_widths[i][RENDER_LINE_DOUBLE] = double_width;
357 cairo_set_source_rgb (xr->cairo, xr->fg.red, xr->fg.green, xr->fg.blue);
362 static struct output_driver *
363 xr_create (const char *file_name, enum settings_output_devices device_type,
364 struct string_map *o, enum xr_output_type file_type)
366 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
367 struct xr_driver *xr;
368 cairo_surface_t *surface;
369 cairo_status_t status;
370 double width_pt, length_pt;
372 xr = xr_allocate (file_name, device_type, o);
374 width_pt = (xr->width + xr->left_margin + xr->right_margin) / 1000.0;
375 length_pt = (xr->length + xr->top_margin + xr->bottom_margin) / 1000.0;
376 if (file_type == XR_PDF)
377 surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
378 else if (file_type == XR_PS)
379 surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
380 else if (file_type == XR_SVG)
381 surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
385 status = cairo_surface_status (surface);
386 if (status != CAIRO_STATUS_SUCCESS)
388 msg (ME, _("error opening output file `%s': %s"),
389 file_name, cairo_status_to_string (status));
390 cairo_surface_destroy (surface);
394 xr->cairo = cairo_create (surface);
395 cairo_surface_destroy (surface);
397 if (!xr_set_cairo (xr, xr->cairo))
400 cairo_save (xr->cairo);
401 xr_driver_next_page (xr, xr->cairo);
403 if (xr->width / xr->char_width < MIN_WIDTH)
405 msg (ME, _("The defined page is not wide enough to hold at least %d "
406 "characters in the default font. In fact, there's only "
407 "room for %d characters."),
409 xr->width / xr->char_width);
413 if (xr->length / xr->char_height < MIN_LENGTH)
415 msg (ME, _("The defined page is not long enough to hold at least %d "
416 "lines in the default font. In fact, there's only "
417 "room for %d lines."),
419 xr->length / xr->char_height);
426 output_driver_destroy (&xr->driver);
430 static struct output_driver *
431 xr_pdf_create (const char *file_name, enum settings_output_devices device_type,
432 struct string_map *o)
434 return xr_create (file_name, device_type, o, XR_PDF);
437 static struct output_driver *
438 xr_ps_create (const char *file_name, enum settings_output_devices device_type,
439 struct string_map *o)
441 return xr_create (file_name, device_type, o, XR_PS);
444 static struct output_driver *
445 xr_svg_create (const char *file_name, enum settings_output_devices device_type,
446 struct string_map *o)
448 return xr_create (file_name, device_type, o, XR_SVG);
452 xr_destroy (struct output_driver *driver)
454 struct xr_driver *xr = xr_driver_cast (driver);
457 xr_driver_destroy_fsm (xr);
459 if (xr->cairo != NULL)
461 cairo_status_t status;
463 cairo_surface_finish (cairo_get_target (xr->cairo));
464 status = cairo_status (xr->cairo);
465 if (status != CAIRO_STATUS_SUCCESS)
466 msg (ME, _("error drawing output for %s driver: %s"),
467 output_driver_get_name (driver),
468 cairo_status_to_string (status));
469 cairo_destroy (xr->cairo);
472 free (xr->command_name);
473 for (i = 0; i < XR_N_FONTS; i++)
475 struct xr_font *font = &xr->fonts[i];
477 if (font->desc != NULL)
478 pango_font_description_free (font->desc);
479 if (font->layout != NULL)
480 g_object_unref (font->layout);
488 xr_flush (struct output_driver *driver)
490 struct xr_driver *xr = xr_driver_cast (driver);
492 cairo_surface_flush (cairo_get_target (xr->cairo));
496 xr_init_caption_cell (const char *caption, struct table_cell *cell)
498 cell->contents = caption;
499 cell->options = TAB_LEFT;
500 cell->destructor = NULL;
503 static struct render_page *
504 xr_render_table_item (struct xr_driver *xr, const struct table_item *item,
505 int *caption_widthp, int *caption_heightp)
507 const char *caption = table_item_get_caption (item);
511 /* XXX doesn't do well with very large captions */
512 int min_width, max_width;
513 struct table_cell cell;
515 xr_init_caption_cell (caption, &cell);
517 xr_measure_cell_width (xr, &cell, &min_width, &max_width);
518 *caption_widthp = MIN (max_width, xr->width);
519 *caption_heightp = xr_measure_cell_height (xr, &cell, *caption_widthp);
522 *caption_heightp = 0;
524 return render_page_create (xr->params, table_item_get_table (item));
528 xr_submit (struct output_driver *driver, const struct output_item *output_item)
530 struct xr_driver *xr = xr_driver_cast (driver);
532 output_driver_track_current_command (output_item, &xr->command_name);
534 xr_driver_output_item (xr, output_item);
535 while (xr_driver_need_new_page (xr))
537 cairo_restore (xr->cairo);
538 cairo_show_page (xr->cairo);
539 cairo_save (xr->cairo);
540 xr_driver_next_page (xr, xr->cairo);
544 /* Functions for rendering a series of output items to a series of Cairo
545 contexts, with pagination.
547 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
548 its underlying implementation.
550 See the big comment in cairo.h for intended usage. */
552 /* Gives new page CAIRO to XR for output. CAIRO may be null to skip actually
553 rendering the page (which might be useful to find out how many pages an
554 output document has without actually rendering it). */
556 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
561 cairo_set_source_rgb (cairo, xr->bg.red, xr->bg.green, xr->bg.blue);
562 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
564 cairo_restore (cairo);
566 cairo_translate (cairo,
567 xr_to_pt (xr->left_margin),
568 xr_to_pt (xr->top_margin));
574 xr_driver_run_fsm (xr);
577 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
578 rendering a previous output item, that is, only if xr_driver_need_new_page()
581 xr_driver_output_item (struct xr_driver *xr,
582 const struct output_item *output_item)
584 assert (xr->fsm == NULL);
585 xr->fsm = xr_render_output_item (xr, output_item);
586 xr_driver_run_fsm (xr);
589 /* Returns true if XR is in the middle of rendering an output item and needs a
590 new page to be appended using xr_driver_next_page() to make progress,
593 xr_driver_need_new_page (const struct xr_driver *xr)
595 return xr->fsm != NULL;
598 /* Returns true if the current page doesn't have any content yet. */
600 xr_driver_is_page_blank (const struct xr_driver *xr)
606 xr_driver_destroy_fsm (struct xr_driver *xr)
610 xr->fsm->destroy (xr->fsm);
616 xr_driver_run_fsm (struct xr_driver *xr)
618 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
619 xr_driver_destroy_fsm (xr);
623 xr_layout_cell (struct xr_driver *, const struct table_cell *,
624 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
625 PangoWrapMode, int *width, int *height);
628 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
630 cairo_new_path (xr->cairo);
631 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
632 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
633 cairo_stroke (xr->cairo);
636 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
637 shortening it to X0...X1 if SHORTEN is true.
638 Draws a horizontal line X1...X3 at Y if RIGHT says so,
639 shortening it to X2...X3 if SHORTEN is true. */
641 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
642 enum render_line_style left, enum render_line_style right,
645 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
646 dump_line (xr, x0, y, x3, y);
649 if (left != RENDER_LINE_NONE)
650 dump_line (xr, x0, y, shorten ? x1 : x2, y);
651 if (right != RENDER_LINE_NONE)
652 dump_line (xr, shorten ? x2 : x1, y, x3, y);
656 /* Draws a vertical line Y0...Y2 at X if TOP says so,
657 shortening it to Y0...Y1 if SHORTEN is true.
658 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
659 shortening it to Y2...Y3 if SHORTEN is true. */
661 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
662 enum render_line_style top, enum render_line_style bottom,
665 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
666 dump_line (xr, x, y0, x, y3);
669 if (top != RENDER_LINE_NONE)
670 dump_line (xr, x, y0, x, shorten ? y1 : y2);
671 if (bottom != RENDER_LINE_NONE)
672 dump_line (xr, x, shorten ? y2 : y1, x, y3);
677 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
678 enum render_line_style styles[TABLE_N_AXES][2])
680 const int x0 = bb[H][0];
681 const int y0 = bb[V][0];
682 const int x3 = bb[H][1];
683 const int y3 = bb[V][1];
684 const int top = styles[H][0];
685 const int left = styles[V][0];
686 const int bottom = styles[H][1];
687 const int right = styles[V][1];
689 /* The algorithm here is somewhat subtle, to allow it to handle
690 all the kinds of intersections that we need.
692 Three additional ordinates are assigned along the x axis. The
693 first is xc, midway between x0 and x3. The others are x1 and
694 x2; for a single vertical line these are equal to xc, and for
695 a double vertical line they are the ordinates of the left and
696 right half of the double line.
698 yc, y1, and y2 are assigned similarly along the y axis.
700 The following diagram shows the coordinate system and output
701 for double top and bottom lines, single left line, and no
705 y0 ________________________
711 y1 = y2 = yc |######### # |
716 y3 |________#_____#_______|
718 struct xr_driver *xr = xr_;
720 /* Offset from center of each line in a pair of double lines. */
721 int double_line_ofs = (xr->line_space + xr->line_width) / 2;
723 /* Are the lines along each axis single or double?
724 (It doesn't make sense to have different kinds of line on the
725 same axis, so we don't try to gracefully handle that case.) */
726 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
727 bool double_horz = left == RENDER_LINE_DOUBLE || right == RENDER_LINE_DOUBLE;
729 /* When horizontal lines are doubled,
730 the left-side line along y1 normally runs from x0 to x2,
731 and the right-side line along y1 from x3 to x1.
732 If the top-side line is also doubled, we shorten the y1 lines,
733 so that the left-side line runs only to x1,
734 and the right-side line only to x2.
735 Otherwise, the horizontal line at y = y1 below would cut off
736 the intersection, which looks ugly:
738 y0 ________________________
743 y1 |######### ########|
746 y2 |######################|
749 y3 |______________________|
750 It is more of a judgment call when the horizontal line is
751 single. We actually choose to cut off the line anyhow, as
752 shown in the first diagram above.
754 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
755 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
756 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
757 int horz_line_ofs = double_vert ? double_line_ofs : 0;
758 int xc = (x0 + x3) / 2;
759 int x1 = xc - horz_line_ofs;
760 int x2 = xc + horz_line_ofs;
762 bool shorten_x1_lines = left == RENDER_LINE_DOUBLE;
763 bool shorten_x2_lines = right == RENDER_LINE_DOUBLE;
764 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
765 int vert_line_ofs = double_horz ? double_line_ofs : 0;
766 int yc = (y0 + y3) / 2;
767 int y1 = yc - vert_line_ofs;
768 int y2 = yc + vert_line_ofs;
771 horz_line (xr, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
774 horz_line (xr, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
775 horz_line (xr, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
779 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
782 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
783 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
788 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
789 int *min_width, int *max_width)
791 struct xr_driver *xr = xr_;
792 int bb[TABLE_N_AXES][2];
793 int clip[TABLE_N_AXES][2];
800 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
801 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, max_width, &h);
804 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, min_width, &h);
808 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int 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, PANGO_WRAP_WORD, &w, &h);
825 xr_draw_cell (void *xr_, const struct table_cell *cell,
826 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
828 struct xr_driver *xr = xr_;
831 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
835 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
836 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
837 PangoWrapMode wrap, int *width, int *height)
839 struct xr_font *font;
841 font = (cell->options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
842 : cell->options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
843 : &xr->fonts[XR_FONT_PROPORTIONAL]);
845 pango_layout_set_text (font->layout, cell->contents, -1);
847 pango_layout_set_alignment (
849 ((cell->options & TAB_ALIGNMENT) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
850 : (cell->options & TAB_ALIGNMENT) == TAB_LEFT ? PANGO_ALIGN_LEFT
851 : PANGO_ALIGN_CENTER));
852 pango_layout_set_width (font->layout,
853 bb[H][1] == INT_MAX ? -1 : bb[H][1] - bb[H][0]);
854 pango_layout_set_wrap (font->layout, wrap);
856 if (clip[H][0] != clip[H][1])
858 cairo_save (xr->cairo);
860 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
862 double x0 = xr_to_pt (clip[H][0]);
863 double y0 = xr_to_pt (clip[V][0] + xr->y);
864 double x1 = xr_to_pt (clip[H][1]);
865 double y1 = xr_to_pt (clip[V][1] + xr->y);
867 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
868 cairo_clip (xr->cairo);
871 cairo_translate (xr->cairo,
873 xr_to_pt (bb[V][0] + xr->y));
874 pango_cairo_show_layout (xr->cairo, font->layout);
875 cairo_restore (xr->cairo);
878 if (width != NULL || height != NULL)
882 pango_layout_get_size (font->layout, &w, &h);
891 xr_draw_title (struct xr_driver *xr, const char *title,
892 int title_width, int title_height)
894 struct table_cell cell;
895 int bb[TABLE_N_AXES][2];
897 xr_init_caption_cell (title, &cell);
899 bb[H][1] = title_width;
901 bb[V][1] = title_height;
902 xr_draw_cell (xr, &cell, bb, bb);
905 struct output_driver_factory pdf_driver_factory =
906 { "pdf", "pspp.pdf", xr_pdf_create };
907 struct output_driver_factory ps_driver_factory =
908 { "ps", "pspp.ps", xr_ps_create };
909 struct output_driver_factory svg_driver_factory =
910 { "svg", "pspp.svg", xr_svg_create };
912 static const struct output_driver_class cairo_driver_class =
920 /* GUI rendering helpers. */
924 struct output_item *item;
927 struct render_page *page;
928 struct xr_driver *xr;
933 #define CHART_WIDTH 500
934 #define CHART_HEIGHT 375
939 xr_driver_create (cairo_t *cairo, struct string_map *options)
941 struct xr_driver *xr = xr_allocate ("cairo", 0, options);
942 if (!xr_set_cairo (xr, cairo))
944 output_driver_destroy (&xr->driver);
950 /* Destroy XR, which should have been created with xr_driver_create(). Any
951 cairo_t added to XR is not destroyed, because it is owned by the client. */
953 xr_driver_destroy (struct xr_driver *xr)
958 output_driver_destroy (&xr->driver);
962 static struct xr_rendering *
963 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
965 struct table_item *table_item;
966 struct xr_rendering *r;
968 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
969 r = xr_rendering_create (xr, &table_item->output_item, cr);
970 table_item_unref (table_item);
976 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
978 if (is_table_item (xr->item))
979 apply_options (xr->xr, o);
982 struct xr_rendering *
983 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
986 struct xr_rendering *r = NULL;
988 if (is_text_item (item))
989 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
991 else if (is_message_item (item))
993 const struct message_item *message_item = to_message_item (item);
994 const struct msg *msg = message_item_get_msg (message_item);
995 char *s = msg_to_string (msg, NULL);
996 r = xr_rendering_create_text (xr, s, cr);
999 else if (is_table_item (item))
1001 r = xzalloc (sizeof *r);
1002 r->item = output_item_ref (item);
1004 xr_set_cairo (xr, cr);
1005 r->page = xr_render_table_item (xr, to_table_item (item),
1006 &r->title_width, &r->title_height);
1008 else if (is_chart_item (item))
1010 r = xzalloc (sizeof *r);
1011 r->item = output_item_ref (item);
1018 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
1020 if (is_table_item (r->item))
1022 int w0 = render_page_get_size (r->page, H);
1023 int w1 = r->title_width;
1024 *w = MAX (w0, w1) / XR_POINT;
1025 *h = (render_page_get_size (r->page, V) + r->title_height) / XR_POINT;
1034 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1035 double x, double y, double width, double height);
1037 /* Draws onto CR at least the region of R that is enclosed in (X,Y)-(X+W,Y+H),
1038 and possibly some additional parts. */
1040 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1041 int x, int y, int w, int h)
1043 if (is_table_item (r->item))
1045 struct xr_driver *xr = r->xr;
1047 xr_set_cairo (xr, cr);
1049 if (r->title_height > 0)
1052 xr_draw_title (xr, table_item_get_caption (to_table_item (r->item)),
1053 r->title_width, r->title_height);
1056 xr->y = r->title_height;
1057 render_page_draw_region (r->page,
1058 x * XR_POINT, (y * XR_POINT) - r->title_height,
1059 w * XR_POINT, h * XR_POINT);
1062 xr_draw_chart (to_chart_item (r->item), cr,
1063 0, 0, CHART_WIDTH, CHART_HEIGHT);
1067 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1068 double x, double y, double width, double height)
1070 struct xrchart_geometry geom;
1073 cairo_translate (cr, x, y + height);
1074 cairo_scale (cr, 1.0, -1.0);
1075 xrchart_geometry_init (cr, &geom, width, height);
1076 if (is_boxplot (chart_item))
1077 xrchart_draw_boxplot (chart_item, cr, &geom);
1078 else if (is_histogram_chart (chart_item))
1079 xrchart_draw_histogram (chart_item, cr, &geom);
1080 else if (is_np_plot_chart (chart_item))
1081 xrchart_draw_np_plot (chart_item, cr, &geom);
1082 else if (is_piechart (chart_item))
1083 xrchart_draw_piechart (chart_item, cr, &geom);
1084 else if (is_roc_chart (chart_item))
1085 xrchart_draw_roc (chart_item, cr, &geom);
1086 else if (is_scree (chart_item))
1087 xrchart_draw_scree (chart_item, cr, &geom);
1088 else if (is_spreadlevel_plot_chart (chart_item))
1089 xrchart_draw_spreadlevel (chart_item, cr, &geom);
1092 xrchart_geometry_free (cr, &geom);
1098 xr_draw_png_chart (const struct chart_item *item,
1099 const char *file_name_template, int number)
1101 const int width = 640;
1102 const int length = 480;
1104 cairo_surface_t *surface;
1105 cairo_status_t status;
1106 const char *number_pos;
1110 number_pos = strchr (file_name_template, '#');
1111 if (number_pos != NULL)
1112 file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1113 file_name_template, number, number_pos + 1);
1115 file_name = xstrdup (file_name_template);
1117 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1118 cr = cairo_create (surface);
1120 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
1122 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1124 status = cairo_surface_write_to_png (surface, file_name);
1125 if (status != CAIRO_STATUS_SUCCESS)
1126 msg (ME, _("error writing output file `%s': %s"),
1127 file_name, cairo_status_to_string (status));
1130 cairo_surface_destroy (surface);
1135 struct xr_table_state
1137 struct xr_render_fsm fsm;
1138 struct table_item *table_item;
1139 struct render_break x_break;
1140 struct render_break y_break;
1145 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1147 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1151 struct render_page *y_slice;
1154 while (!render_break_has_next (&ts->y_break))
1156 struct render_page *x_slice;
1158 render_break_destroy (&ts->y_break);
1159 if (!render_break_has_next (&ts->x_break))
1162 x_slice = render_break_next (&ts->x_break, xr->width);
1163 render_break_init (&ts->y_break, x_slice, V);
1166 space = xr->length - xr->y;
1167 if (render_break_next_size (&ts->y_break) > space)
1173 y_slice = render_break_next (&ts->y_break, space);
1174 if (ts->caption_height)
1177 xr_draw_title (xr, table_item_get_caption (ts->table_item),
1178 xr->width, ts->caption_height);
1180 xr->y += ts->caption_height;
1181 ts->caption_height = 0;
1185 render_page_draw (y_slice);
1186 xr->y += render_page_get_size (y_slice, V);
1187 render_page_unref (y_slice);
1192 xr_table_destroy (struct xr_render_fsm *fsm)
1194 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1196 table_item_unref (ts->table_item);
1197 render_break_destroy (&ts->x_break);
1198 render_break_destroy (&ts->y_break);
1202 static struct xr_render_fsm *
1203 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1205 struct xr_table_state *ts;
1206 struct render_page *page;
1209 ts = xmalloc (sizeof *ts);
1210 ts->fsm.render = xr_table_render;
1211 ts->fsm.destroy = xr_table_destroy;
1212 ts->table_item = table_item_ref (table_item);
1215 xr->y += xr->char_height;
1217 page = xr_render_table_item (xr, table_item,
1218 &caption_width, &ts->caption_height);
1219 xr->params->size[V] = xr->length - ts->caption_height;
1221 render_break_init (&ts->x_break, page, H);
1222 render_break_init_empty (&ts->y_break);
1227 struct xr_chart_state
1229 struct xr_render_fsm fsm;
1230 struct chart_item *chart_item;
1234 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1236 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1241 if (xr->cairo != NULL)
1242 xr_draw_chart (cs->chart_item, xr->cairo, 0.0, 0.0,
1243 xr_to_pt (xr->width), xr_to_pt (xr->length));
1250 xr_chart_destroy (struct xr_render_fsm *fsm)
1252 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1254 chart_item_unref (cs->chart_item);
1258 static struct xr_render_fsm *
1259 xr_render_chart (const struct chart_item *chart_item)
1261 struct xr_chart_state *cs;
1263 cs = xmalloc (sizeof *cs);
1264 cs->fsm.render = xr_chart_render;
1265 cs->fsm.destroy = xr_chart_destroy;
1266 cs->chart_item = chart_item_ref (chart_item);
1272 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1278 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1280 /* Nothing to do. */
1283 static struct xr_render_fsm *
1284 xr_render_eject (void)
1286 static struct xr_render_fsm eject_renderer =
1292 return &eject_renderer;
1295 static struct xr_render_fsm *
1296 xr_create_text_renderer (struct xr_driver *xr, const char *text)
1298 struct table_item *table_item;
1299 struct xr_render_fsm *fsm;
1301 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
1302 fsm = xr_render_table (xr, table_item);
1303 table_item_unref (table_item);
1308 static struct xr_render_fsm *
1309 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1311 enum text_item_type type = text_item_get_type (text_item);
1312 const char *text = text_item_get_text (text_item);
1316 case TEXT_ITEM_TITLE:
1318 xr->title = xstrdup (text);
1321 case TEXT_ITEM_SUBTITLE:
1322 free (xr->subtitle);
1323 xr->subtitle = xstrdup (text);
1326 case TEXT_ITEM_COMMAND_CLOSE:
1329 case TEXT_ITEM_BLANK_LINE:
1331 xr->y += xr->char_height;
1334 case TEXT_ITEM_EJECT_PAGE:
1336 return xr_render_eject ();
1340 return xr_create_text_renderer (xr, text);
1346 static struct xr_render_fsm *
1347 xr_render_message (struct xr_driver *xr,
1348 const struct message_item *message_item)
1350 const struct msg *msg = message_item_get_msg (message_item);
1351 struct xr_render_fsm *fsm;
1354 s = msg_to_string (msg, xr->command_name);
1355 fsm = xr_create_text_renderer (xr, s);
1361 static struct xr_render_fsm *
1362 xr_render_output_item (struct xr_driver *xr,
1363 const struct output_item *output_item)
1365 if (is_table_item (output_item))
1366 return xr_render_table (xr, to_table_item (output_item));
1367 else if (is_chart_item (output_item))
1368 return xr_render_chart (to_chart_item (output_item));
1369 else if (is_text_item (output_item))
1370 return xr_render_text (xr, to_text_item (output_item));
1371 else if (is_message_item (output_item))
1372 return xr_render_message (xr, to_message_item (output_item));