1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010, 2011, 2012 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/scree.h"
36 #include "output/driver-provider.h"
37 #include "output/message-item.h"
38 #include "output/options.h"
39 #include "output/render.h"
40 #include "output/tab.h"
41 #include "output/table-item.h"
42 #include "output/table.h"
43 #include "output/text-item.h"
45 #include <cairo/cairo-pdf.h>
46 #include <cairo/cairo-ps.h>
47 #include <cairo/cairo-svg.h>
48 #include <cairo/cairo.h>
49 #include <pango/pango-font.h>
50 #include <pango/pango-layout.h>
51 #include <pango/pango.h>
52 #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. */
68 #define XR_POINT PANGO_SCALE
69 #define XR_INCH (XR_POINT * 72)
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 XR units. */
126 int right_margin; /* Right margin in XR units. */
127 int top_margin; /* Top margin in XR units. */
128 int bottom_margin; /* Bottom margin in XR units. */
130 int line_gutter; /* Space around lines. */
131 int line_space; /* Space between lines. */
132 int line_width; /* Width of lines. */
134 double bg_red, bg_green, bg_blue; /* Background color */
135 double fg_red, fg_green, fg_blue; /* Foreground color */
137 /* Internal state. */
138 struct render_params *params;
139 int char_width, char_height;
144 int page_number; /* Current page number. */
146 struct xr_render_fsm *fsm;
149 static const struct output_driver_class cairo_driver_class;
151 static void xr_driver_destroy_fsm (struct xr_driver *);
152 static void xr_driver_run_fsm (struct xr_driver *);
154 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
155 enum render_line_style styles[TABLE_N_AXES][2]);
156 static void xr_measure_cell_width (void *, const struct table_cell *,
158 static int xr_measure_cell_height (void *, const struct table_cell *,
160 static void xr_draw_cell (void *, const struct table_cell *,
161 int bb[TABLE_N_AXES][2],
162 int clip[TABLE_N_AXES][2]);
164 static struct xr_render_fsm *xr_render_output_item (
165 struct xr_driver *, const struct output_item *);
167 /* Output driver basics. */
169 static struct xr_driver *
170 xr_driver_cast (struct output_driver *driver)
172 assert (driver->class == &cairo_driver_class);
173 return UP_CAST (driver, struct xr_driver, driver);
176 static struct driver_option *
177 opt (struct output_driver *d, struct string_map *options, const char *key,
178 const char *default_value)
180 return driver_option_get (d, options, key, default_value);
183 /* Parse color information specified by KEY into {RED,GREEN,BLUE}.
184 Currently, the input string must be of the form "#RRRRGGGGBBBB"
185 Future implementations might allow things like "yellow" and
186 "sky-blue-ultra-brown"
189 parse_color (struct output_driver *d, struct string_map *options,
190 const char *key, const char *default_value,
191 double *dred, double *dgreen, double *dblue)
193 int red, green, blue;
194 char *string = parse_string (opt (d, options, key, default_value));
196 if (3 != sscanf (string, "#%04x%04x%04x", &red, &green, &blue))
198 /* If the parsed option string fails, then try the default value */
199 if ( 3 != sscanf (default_value, "#%04x%04x%04x", &red, &green, &blue))
201 /* ... and if that fails set everything to zero */
202 red = green = blue = 0;
206 /* Convert 16 bit ints to float */
207 *dred = red / (double) 0xFFFF;
208 *dgreen = green / (double) 0xFFFF;
209 *dblue = blue / (double) 0xFFFF;
212 static PangoFontDescription *
213 parse_font (struct output_driver *d, struct string_map *options,
214 const char *key, const char *default_value,
217 PangoFontDescription *desc;
220 /* Parse KEY as a font description. */
221 string = parse_string (opt (d, options, key, default_value));
222 desc = pango_font_description_from_string (string);
225 error (0, 0, _("`%s': bad font specification"), string);
227 /* Fall back to DEFAULT_VALUE, which had better be a valid font
229 desc = pango_font_description_from_string (default_value);
230 assert (desc != NULL);
234 /* If the font description didn't include an explicit font size, then set it
235 to DEFAULT_POINTS. */
236 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
237 pango_font_description_set_size (desc,
238 default_points / 1000.0 * PANGO_SCALE);
245 apply_options (struct xr_driver *xr, struct string_map *o)
247 struct output_driver *d = &xr->driver;
249 int paper_width, paper_length;
251 int font_points = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
252 xr->fonts[XR_FONT_FIXED].desc = parse_font (d, o, "fixed-font", "monospace",
254 xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font (d, o, "prop-font",
255 "serif", font_points);
256 xr->fonts[XR_FONT_EMPHASIS].desc = parse_font (d, o, "emph-font",
257 "serif italic", font_points);
259 xr->line_gutter = XR_POINT;
260 xr->line_space = XR_POINT;
261 xr->line_width = XR_POINT / 2;
264 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg_red, &xr->bg_green, &xr->bg_blue);
265 parse_color (d, o, "foreground-color", "#000000000000", &xr->fg_red, &xr->fg_green, &xr->fg_blue);
267 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
268 xr->left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
269 xr->right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
270 xr->top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
271 xr->bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
273 xr->width = paper_width - xr->left_margin - xr->right_margin;
274 xr->length = paper_length - xr->top_margin - xr->bottom_margin;
277 static struct xr_driver *
278 xr_allocate (const char *name, int device_type, struct string_map *o)
280 struct xr_driver *xr = xzalloc (sizeof *xr);
281 struct output_driver *d = &xr->driver;
283 output_driver_init (d, &cairo_driver_class, name, device_type);
285 apply_options (xr, o);
291 xr_is_72dpi (cairo_t *cr)
293 cairo_surface_type_t type;
294 cairo_surface_t *surface;
296 surface = cairo_get_target (cr);
297 type = cairo_surface_get_type (surface);
298 return type == CAIRO_SURFACE_TYPE_PDF || type == CAIRO_SURFACE_TYPE_PS;
302 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
304 PangoContext *context;
310 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
312 map = pango_cairo_font_map_get_default ();
313 context = pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (map));
314 if (xr_is_72dpi (cairo))
316 /* Pango seems to always scale fonts according to the DPI specified
317 in the font map, even if the surface has a real DPI. The default
318 DPI is 96, so on a 72 DPI device fonts end up being 96/72 = 133%
319 of their desired size. We deal with this by fixing the resolution
320 here. Presumably there is a better solution, but what? */
321 pango_cairo_context_set_resolution (context, 72.0);
326 for (i = 0; i < XR_N_FONTS; i++)
328 struct xr_font *font = &xr->fonts[i];
329 int char_width, char_height;
331 font->layout = pango_layout_new (context);
332 pango_layout_set_font_description (font->layout, font->desc);
334 pango_layout_set_text (font->layout, "0", 1);
335 pango_layout_get_size (font->layout, &char_width, &char_height);
336 xr->char_width = MAX (xr->char_width, char_width);
337 xr->char_height = MAX (xr->char_height, char_height);
340 g_object_unref (G_OBJECT (context));
342 if (xr->params == NULL)
344 int single_width, double_width;
346 xr->params = xmalloc (sizeof *xr->params);
347 xr->params->draw_line = xr_draw_line;
348 xr->params->measure_cell_width = xr_measure_cell_width;
349 xr->params->measure_cell_height = xr_measure_cell_height;
350 xr->params->draw_cell = xr_draw_cell;
351 xr->params->aux = xr;
352 xr->params->size[H] = xr->width;
353 xr->params->size[V] = xr->length;
354 xr->params->font_size[H] = xr->char_width;
355 xr->params->font_size[V] = xr->char_height;
357 single_width = 2 * xr->line_gutter + xr->line_width;
358 double_width = 2 * xr->line_gutter + xr->line_space + 2 * xr->line_width;
359 for (i = 0; i < TABLE_N_AXES; i++)
361 xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
362 xr->params->line_widths[i][RENDER_LINE_SINGLE] = single_width;
363 xr->params->line_widths[i][RENDER_LINE_DOUBLE] = double_width;
367 cairo_set_source_rgb (xr->cairo, xr->fg_red, xr->fg_green, xr->fg_blue);
372 static struct output_driver *
373 xr_create (const char *file_name, enum settings_output_devices device_type,
374 struct string_map *o, enum xr_output_type file_type)
376 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
377 struct output_driver *d;
378 struct xr_driver *xr;
379 cairo_surface_t *surface;
380 cairo_status_t status;
381 double width_pt, length_pt;
383 xr = xr_allocate (file_name, device_type, o);
386 width_pt = (xr->width + xr->left_margin + xr->right_margin) / 1000.0;
387 length_pt = (xr->length + xr->top_margin + xr->bottom_margin) / 1000.0;
388 if (file_type == XR_PDF)
389 surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
390 else if (file_type == XR_PS)
391 surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
392 else if (file_type == XR_SVG)
393 surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
397 status = cairo_surface_status (surface);
398 if (status != CAIRO_STATUS_SUCCESS)
400 error (0, 0, _("error opening output file `%s': %s"),
401 file_name, cairo_status_to_string (status));
402 cairo_surface_destroy (surface);
406 xr->cairo = cairo_create (surface);
407 cairo_surface_destroy (surface);
409 if (!xr_set_cairo (xr, xr->cairo))
412 cairo_save (xr->cairo);
413 xr_driver_next_page (xr, xr->cairo);
415 if (xr->width / xr->char_width < MIN_WIDTH)
417 error (0, 0, _("The defined page is not wide enough to hold at least %d "
418 "characters in the default font. In fact, there's only "
419 "room for %d characters."),
421 xr->width / xr->char_width);
425 if (xr->length / xr->char_height < MIN_LENGTH)
427 error (0, 0, _("The defined page is not long enough to hold at least %d "
428 "lines in the default font. In fact, there's only "
429 "room for %d lines."),
431 xr->length / xr->char_height);
438 output_driver_destroy (&xr->driver);
442 static struct output_driver *
443 xr_pdf_create (const char *file_name, enum settings_output_devices device_type,
444 struct string_map *o)
446 return xr_create (file_name, device_type, o, XR_PDF);
449 static struct output_driver *
450 xr_ps_create (const char *file_name, enum settings_output_devices device_type,
451 struct string_map *o)
453 return xr_create (file_name, device_type, o, XR_PS);
456 static struct output_driver *
457 xr_svg_create (const char *file_name, enum settings_output_devices device_type,
458 struct string_map *o)
460 return xr_create (file_name, device_type, o, XR_SVG);
464 xr_destroy (struct output_driver *driver)
466 struct xr_driver *xr = xr_driver_cast (driver);
469 xr_driver_destroy_fsm (xr);
471 if (xr->cairo != NULL)
473 cairo_status_t status;
475 cairo_surface_finish (cairo_get_target (xr->cairo));
476 status = cairo_status (xr->cairo);
477 if (status != CAIRO_STATUS_SUCCESS)
478 error (0, 0, _("error drawing output for %s driver: %s"),
479 output_driver_get_name (driver),
480 cairo_status_to_string (status));
481 cairo_destroy (xr->cairo);
484 free (xr->command_name);
485 for (i = 0; i < XR_N_FONTS; i++)
487 struct xr_font *font = &xr->fonts[i];
489 if (font->desc != NULL)
490 pango_font_description_free (font->desc);
491 if (font->layout != NULL)
492 g_object_unref (font->layout);
500 xr_flush (struct output_driver *driver)
502 struct xr_driver *xr = xr_driver_cast (driver);
504 cairo_surface_flush (cairo_get_target (xr->cairo));
508 xr_init_caption_cell (const char *caption, struct table_cell *cell)
510 cell->contents = caption;
511 cell->options = TAB_LEFT;
512 cell->destructor = NULL;
515 static struct render_page *
516 xr_render_table_item (struct xr_driver *xr, const struct table_item *item,
517 int *caption_widthp, int *caption_heightp)
519 const char *caption = table_item_get_caption (item);
523 /* XXX doesn't do well with very large captions */
524 int min_width, max_width;
525 struct table_cell cell;
527 xr_init_caption_cell (caption, &cell);
529 xr_measure_cell_width (xr, &cell, &min_width, &max_width);
530 *caption_widthp = MIN (max_width, xr->width);
531 *caption_heightp = xr_measure_cell_height (xr, &cell, *caption_widthp);
534 *caption_heightp = 0;
536 return render_page_create (xr->params, table_item_get_table (item));
540 xr_submit (struct output_driver *driver, const struct output_item *output_item)
542 struct xr_driver *xr = xr_driver_cast (driver);
544 xr_driver_output_item (xr, output_item);
545 while (xr_driver_need_new_page (xr))
547 cairo_restore (xr->cairo);
548 cairo_show_page (xr->cairo);
549 cairo_save (xr->cairo);
550 xr_driver_next_page (xr, xr->cairo);
554 /* Functions for rendering a series of output items to a series of Cairo
555 contexts, with pagination.
557 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
558 its underlying implementation.
560 See the big comment in cairo.h for intended usage. */
562 /* Gives new page CAIRO to XR for output. CAIRO may be null to skip actually
563 rendering the page (which might be useful to find out how many pages an
564 output document has without actually rendering it). */
566 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
571 cairo_set_source_rgb (cairo, xr->bg_red, xr->bg_green, xr->bg_blue);
572 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
574 cairo_restore (cairo);
576 cairo_translate (cairo,
577 xr_to_pt (xr->left_margin),
578 xr_to_pt (xr->top_margin));
584 xr_driver_run_fsm (xr);
587 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
588 rendering a previous output item, that is, only if xr_driver_need_new_page()
591 xr_driver_output_item (struct xr_driver *xr,
592 const struct output_item *output_item)
594 assert (xr->fsm == NULL);
595 xr->fsm = xr_render_output_item (xr, output_item);
596 xr_driver_run_fsm (xr);
599 /* Returns true if XR is in the middle of rendering an output item and needs a
600 new page to be appended using xr_driver_next_page() to make progress,
603 xr_driver_need_new_page (const struct xr_driver *xr)
605 return xr->fsm != NULL;
608 /* Returns true if the current page doesn't have any content yet. */
610 xr_driver_is_page_blank (const struct xr_driver *xr)
616 xr_driver_destroy_fsm (struct xr_driver *xr)
620 xr->fsm->destroy (xr->fsm);
626 xr_driver_run_fsm (struct xr_driver *xr)
628 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
629 xr_driver_destroy_fsm (xr);
633 xr_layout_cell (struct xr_driver *, const struct table_cell *,
634 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
635 PangoWrapMode, int *width, int *height);
638 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
640 cairo_new_path (xr->cairo);
641 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
642 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
643 cairo_stroke (xr->cairo);
646 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
647 shortening it to X0...X1 if SHORTEN is true.
648 Draws a horizontal line X1...X3 at Y if RIGHT says so,
649 shortening it to X2...X3 if SHORTEN is true. */
651 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
652 enum render_line_style left, enum render_line_style right,
655 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
656 dump_line (xr, x0, y, x3, y);
659 if (left != RENDER_LINE_NONE)
660 dump_line (xr, x0, y, shorten ? x1 : x2, y);
661 if (right != RENDER_LINE_NONE)
662 dump_line (xr, shorten ? x2 : x1, y, x3, y);
666 /* Draws a vertical line Y0...Y2 at X if TOP says so,
667 shortening it to Y0...Y1 if SHORTEN is true.
668 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
669 shortening it to Y2...Y3 if SHORTEN is true. */
671 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
672 enum render_line_style top, enum render_line_style bottom,
675 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
676 dump_line (xr, x, y0, x, y3);
679 if (top != RENDER_LINE_NONE)
680 dump_line (xr, x, y0, x, shorten ? y1 : y2);
681 if (bottom != RENDER_LINE_NONE)
682 dump_line (xr, x, shorten ? y2 : y1, x, y3);
687 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
688 enum render_line_style styles[TABLE_N_AXES][2])
690 const int x0 = bb[H][0];
691 const int y0 = bb[V][0];
692 const int x3 = bb[H][1];
693 const int y3 = bb[V][1];
694 const int top = styles[H][0];
695 const int left = styles[V][0];
696 const int bottom = styles[H][1];
697 const int right = styles[V][1];
699 /* The algorithm here is somewhat subtle, to allow it to handle
700 all the kinds of intersections that we need.
702 Three additional ordinates are assigned along the x axis. The
703 first is xc, midway between x0 and x3. The others are x1 and
704 x2; for a single vertical line these are equal to xc, and for
705 a double vertical line they are the ordinates of the left and
706 right half of the double line.
708 yc, y1, and y2 are assigned similarly along the y axis.
710 The following diagram shows the coordinate system and output
711 for double top and bottom lines, single left line, and no
715 y0 ________________________
721 y1 = y2 = yc |######### # |
726 y3 |________#_____#_______|
728 struct xr_driver *xr = xr_;
730 /* Offset from center of each line in a pair of double lines. */
731 int double_line_ofs = (xr->line_space + xr->line_width) / 2;
733 /* Are the lines along each axis single or double?
734 (It doesn't make sense to have different kinds of line on the
735 same axis, so we don't try to gracefully handle that case.) */
736 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
737 bool double_horz = left == RENDER_LINE_DOUBLE || right == RENDER_LINE_DOUBLE;
739 /* When horizontal lines are doubled,
740 the left-side line along y1 normally runs from x0 to x2,
741 and the right-side line along y1 from x3 to x1.
742 If the top-side line is also doubled, we shorten the y1 lines,
743 so that the left-side line runs only to x1,
744 and the right-side line only to x2.
745 Otherwise, the horizontal line at y = y1 below would cut off
746 the intersection, which looks ugly:
748 y0 ________________________
753 y1 |######### ########|
756 y2 |######################|
759 y3 |______________________|
760 It is more of a judgment call when the horizontal line is
761 single. We actually choose to cut off the line anyhow, as
762 shown in the first diagram above.
764 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
765 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
766 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
767 int horz_line_ofs = double_vert ? double_line_ofs : 0;
768 int xc = (x0 + x3) / 2;
769 int x1 = xc - horz_line_ofs;
770 int x2 = xc + horz_line_ofs;
772 bool shorten_x1_lines = left == RENDER_LINE_DOUBLE;
773 bool shorten_x2_lines = right == RENDER_LINE_DOUBLE;
774 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
775 int vert_line_ofs = double_horz ? double_line_ofs : 0;
776 int yc = (y0 + y3) / 2;
777 int y1 = yc - vert_line_ofs;
778 int y2 = yc + vert_line_ofs;
781 horz_line (xr, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
784 horz_line (xr, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
785 horz_line (xr, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
789 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
792 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
793 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
798 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
799 int *min_width, int *max_width)
801 struct xr_driver *xr = xr_;
802 int bb[TABLE_N_AXES][2];
803 int clip[TABLE_N_AXES][2];
810 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
811 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, max_width, &h);
814 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, min_width, &h);
818 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
820 struct xr_driver *xr = xr_;
821 int bb[TABLE_N_AXES][2];
822 int clip[TABLE_N_AXES][2];
829 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
830 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
835 xr_draw_cell (void *xr_, const struct table_cell *cell,
836 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
838 struct xr_driver *xr = xr_;
841 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
845 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
846 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
847 PangoWrapMode wrap, int *width, int *height)
849 struct xr_font *font;
851 font = (cell->options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
852 : cell->options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
853 : &xr->fonts[XR_FONT_PROPORTIONAL]);
855 pango_layout_set_text (font->layout, cell->contents, -1);
857 pango_layout_set_alignment (
859 ((cell->options & TAB_ALIGNMENT) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
860 : (cell->options & TAB_ALIGNMENT) == TAB_LEFT ? PANGO_ALIGN_LEFT
861 : PANGO_ALIGN_CENTER));
862 pango_layout_set_width (font->layout,
863 bb[H][1] == INT_MAX ? -1 : bb[H][1] - bb[H][0]);
864 pango_layout_set_wrap (font->layout, wrap);
866 if (clip[H][0] != clip[H][1])
868 cairo_save (xr->cairo);
870 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
872 double x0 = xr_to_pt (clip[H][0]);
873 double y0 = xr_to_pt (clip[V][0] + xr->y);
874 double x1 = xr_to_pt (clip[H][1]);
875 double y1 = xr_to_pt (clip[V][1] + xr->y);
877 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
878 cairo_clip (xr->cairo);
881 cairo_translate (xr->cairo,
883 xr_to_pt (bb[V][0] + xr->y));
884 pango_cairo_show_layout (xr->cairo, font->layout);
885 cairo_restore (xr->cairo);
888 if (width != NULL || height != NULL)
892 pango_layout_get_size (font->layout, &w, &h);
901 xr_draw_title (struct xr_driver *xr, const char *title,
902 int title_width, int title_height)
904 struct table_cell cell;
905 int bb[TABLE_N_AXES][2];
907 xr_init_caption_cell (title, &cell);
909 bb[H][1] = title_width;
911 bb[V][1] = title_height;
912 xr_draw_cell (xr, &cell, bb, bb);
915 struct output_driver_factory pdf_driver_factory = { "pdf", xr_pdf_create };
916 struct output_driver_factory ps_driver_factory = { "ps", xr_ps_create };
917 struct output_driver_factory svg_driver_factory = { "svg", xr_svg_create };
919 static const struct output_driver_class cairo_driver_class =
927 /* GUI rendering helpers. */
931 struct output_item *item;
934 struct render_page *page;
935 struct xr_driver *xr;
940 #define CHART_WIDTH 500
941 #define CHART_HEIGHT 375
946 xr_driver_create (cairo_t *cairo, struct string_map *options)
948 struct xr_driver *xr = xr_allocate ("cairo", 0, options);
949 if (!xr_set_cairo (xr, cairo))
951 output_driver_destroy (&xr->driver);
957 /* Destroy XR, which should have been created with xr_driver_create(). Any
958 cairo_t added to XR is not destroyed, because it is owned by the client. */
960 xr_driver_destroy (struct xr_driver *xr)
965 output_driver_destroy (&xr->driver);
969 static struct xr_rendering *
970 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
972 struct table_item *table_item;
973 struct xr_rendering *r;
975 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
976 r = xr_rendering_create (xr, &table_item->output_item, cr);
977 table_item_unref (table_item);
983 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
985 if (is_table_item (xr->item))
986 apply_options (xr->xr, o);
989 struct xr_rendering *
990 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
993 struct xr_rendering *r = NULL;
995 if (is_text_item (item))
996 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
998 else if (is_message_item (item))
1000 const struct message_item *message_item = to_message_item (item);
1001 const struct msg *msg = message_item_get_msg (message_item);
1002 char *s = msg_to_string (msg, NULL);
1003 r = xr_rendering_create_text (xr, s, cr);
1006 else if (is_table_item (item))
1008 r = xzalloc (sizeof *r);
1009 r->item = output_item_ref (item);
1011 xr_set_cairo (xr, cr);
1012 r->page = xr_render_table_item (xr, to_table_item (item),
1013 &r->title_width, &r->title_height);
1015 else if (is_chart_item (item))
1017 r = xzalloc (sizeof *r);
1018 r->item = output_item_ref (item);
1025 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
1027 if (is_table_item (r->item))
1029 int w0 = render_page_get_size (r->page, H);
1030 int w1 = r->title_width;
1031 *w = MAX (w0, w1) / 1024;
1032 *h = (render_page_get_size (r->page, V) + r->title_height) / 1024;
1041 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1042 double x, double y, double width, double height);
1044 /* Draws onto CR at least the region of R that is enclosed in (X,Y)-(X+W,Y+H),
1045 and possibly some additional parts. */
1047 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1048 int x, int y, int w, int h)
1050 if (is_table_item (r->item))
1052 struct xr_driver *xr = r->xr;
1054 xr_set_cairo (xr, cr);
1056 if (r->title_height > 0)
1059 xr_draw_title (xr, table_item_get_caption (to_table_item (r->item)),
1060 r->title_width, r->title_height);
1063 xr->y = r->title_height;
1064 render_page_draw_region (r->page, x * 1024, (y * 1024) - r->title_height,
1065 w * 1024, h * 1024);
1068 xr_draw_chart (to_chart_item (r->item), cr,
1069 0, 0, CHART_WIDTH, CHART_HEIGHT);
1073 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1074 double x, double y, double width, double height)
1076 struct xrchart_geometry geom;
1079 cairo_translate (cr, x, y + height);
1080 cairo_scale (cr, 1.0, -1.0);
1081 xrchart_geometry_init (cr, &geom, width, height);
1082 if (is_boxplot (chart_item))
1083 xrchart_draw_boxplot (chart_item, cr, &geom);
1084 else if (is_histogram_chart (chart_item))
1085 xrchart_draw_histogram (chart_item, cr, &geom);
1086 else if (is_np_plot_chart (chart_item))
1087 xrchart_draw_np_plot (chart_item, cr, &geom);
1088 else if (is_piechart (chart_item))
1089 xrchart_draw_piechart (chart_item, cr, &geom);
1090 else if (is_roc_chart (chart_item))
1091 xrchart_draw_roc (chart_item, cr, &geom);
1092 else if (is_scree (chart_item))
1093 xrchart_draw_scree (chart_item, cr, &geom);
1096 xrchart_geometry_free (cr, &geom);
1102 xr_draw_png_chart (const struct chart_item *item,
1103 const char *file_name_template, int number)
1105 const int width = 640;
1106 const int length = 480;
1108 cairo_surface_t *surface;
1109 cairo_status_t status;
1110 const char *number_pos;
1114 number_pos = strchr (file_name_template, '#');
1115 if (number_pos != NULL)
1116 file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1117 file_name_template, number, number_pos + 1);
1119 file_name = xstrdup (file_name_template);
1121 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1122 cr = cairo_create (surface);
1124 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
1126 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1128 status = cairo_surface_write_to_png (surface, file_name);
1129 if (status != CAIRO_STATUS_SUCCESS)
1130 error (0, 0, _("error writing output file `%s': %s"),
1131 file_name, cairo_status_to_string (status));
1134 cairo_surface_destroy (surface);
1139 struct xr_table_state
1141 struct xr_render_fsm fsm;
1142 struct table_item *table_item;
1143 struct render_break x_break;
1144 struct render_break y_break;
1149 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1151 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1155 struct render_page *y_slice;
1158 while (!render_break_has_next (&ts->y_break))
1160 struct render_page *x_slice;
1162 render_break_destroy (&ts->y_break);
1163 if (!render_break_has_next (&ts->x_break))
1166 x_slice = render_break_next (&ts->x_break, xr->width);
1167 render_break_init (&ts->y_break, x_slice, V);
1170 space = xr->length - xr->y;
1171 if (render_break_next_size (&ts->y_break) > space)
1177 y_slice = render_break_next (&ts->y_break, space);
1178 if (ts->caption_height)
1181 xr_draw_title (xr, table_item_get_caption (ts->table_item),
1182 xr->width, ts->caption_height);
1184 xr->y += ts->caption_height;
1185 ts->caption_height = 0;
1189 render_page_draw (y_slice);
1190 xr->y += render_page_get_size (y_slice, V);
1191 render_page_unref (y_slice);
1196 xr_table_destroy (struct xr_render_fsm *fsm)
1198 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1200 table_item_unref (ts->table_item);
1201 render_break_destroy (&ts->x_break);
1202 render_break_destroy (&ts->y_break);
1206 static struct xr_render_fsm *
1207 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1209 struct xr_table_state *ts;
1210 struct render_page *page;
1213 ts = xmalloc (sizeof *ts);
1214 ts->fsm.render = xr_table_render;
1215 ts->fsm.destroy = xr_table_destroy;
1216 ts->table_item = table_item_ref (table_item);
1219 xr->y += xr->char_height;
1221 page = xr_render_table_item (xr, table_item,
1222 &caption_width, &ts->caption_height);
1223 xr->params->size[V] = xr->length - ts->caption_height;
1225 render_break_init (&ts->x_break, page, H);
1226 render_break_init_empty (&ts->y_break);
1231 struct xr_chart_state
1233 struct xr_render_fsm fsm;
1234 struct chart_item *chart_item;
1238 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1240 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1245 if (xr->cairo != NULL)
1246 xr_draw_chart (cs->chart_item, xr->cairo, 0.0, 0.0,
1247 xr_to_pt (xr->width), xr_to_pt (xr->length));
1254 xr_chart_destroy (struct xr_render_fsm *fsm)
1256 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1258 chart_item_unref (cs->chart_item);
1262 static struct xr_render_fsm *
1263 xr_render_chart (const struct chart_item *chart_item)
1265 struct xr_chart_state *cs;
1267 cs = xmalloc (sizeof *cs);
1268 cs->fsm.render = xr_chart_render;
1269 cs->fsm.destroy = xr_chart_destroy;
1270 cs->chart_item = chart_item_ref (chart_item);
1276 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1282 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1284 /* Nothing to do. */
1287 static struct xr_render_fsm *
1288 xr_render_eject (void)
1290 static struct xr_render_fsm eject_renderer =
1296 return &eject_renderer;
1299 static struct xr_render_fsm *
1300 xr_create_text_renderer (struct xr_driver *xr, const char *text)
1302 struct table_item *table_item;
1303 struct xr_render_fsm *fsm;
1305 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
1306 fsm = xr_render_table (xr, table_item);
1307 table_item_unref (table_item);
1312 static struct xr_render_fsm *
1313 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1315 enum text_item_type type = text_item_get_type (text_item);
1316 const char *text = text_item_get_text (text_item);
1320 case TEXT_ITEM_TITLE:
1322 xr->title = xstrdup (text);
1325 case TEXT_ITEM_SUBTITLE:
1326 free (xr->subtitle);
1327 xr->subtitle = xstrdup (text);
1330 case TEXT_ITEM_COMMAND_CLOSE:
1333 case TEXT_ITEM_BLANK_LINE:
1335 xr->y += xr->char_height;
1338 case TEXT_ITEM_EJECT_PAGE:
1340 return xr_render_eject ();
1344 return xr_create_text_renderer (xr, text);
1350 static struct xr_render_fsm *
1351 xr_render_message (struct xr_driver *xr,
1352 const struct message_item *message_item)
1354 const struct msg *msg = message_item_get_msg (message_item);
1355 struct xr_render_fsm *fsm;
1358 s = msg_to_string (msg, xr->command_name);
1359 fsm = xr_create_text_renderer (xr, s);
1365 static struct xr_render_fsm *
1366 xr_render_output_item (struct xr_driver *xr,
1367 const struct output_item *output_item)
1369 if (is_table_item (output_item))
1370 return xr_render_table (xr, to_table_item (output_item));
1371 else if (is_chart_item (output_item))
1372 return xr_render_chart (to_chart_item (output_item));
1373 else if (is_text_item (output_item))
1374 return xr_render_text (xr, to_text_item (output_item));
1375 else if (is_message_item (output_item))
1376 return xr_render_message (xr, to_message_item (output_item));