1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010 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 /* Internal state. */
135 struct render_params *params;
136 int char_width, char_height;
141 int page_number; /* Current page number. */
143 struct xr_render_fsm *fsm;
146 static const struct output_driver_class cairo_driver_class;
148 static void xr_driver_destroy_fsm (struct xr_driver *);
149 static void xr_driver_run_fsm (struct xr_driver *);
151 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
152 enum render_line_style styles[TABLE_N_AXES][2]);
153 static void xr_measure_cell_width (void *, const struct table_cell *,
155 static int xr_measure_cell_height (void *, const struct table_cell *,
157 static void xr_draw_cell (void *, const struct table_cell *,
158 int bb[TABLE_N_AXES][2],
159 int clip[TABLE_N_AXES][2]);
161 static struct xr_render_fsm *xr_render_output_item (
162 struct xr_driver *, const struct output_item *);
164 /* Output driver basics. */
166 static struct xr_driver *
167 xr_driver_cast (struct output_driver *driver)
169 assert (driver->class == &cairo_driver_class);
170 return UP_CAST (driver, struct xr_driver, driver);
173 static struct driver_option *
174 opt (struct output_driver *d, struct string_map *options, const char *key,
175 const char *default_value)
177 return driver_option_get (d, options, key, default_value);
180 static PangoFontDescription *
181 parse_font (struct output_driver *d, struct string_map *options,
182 const char *key, const char *default_value,
185 PangoFontDescription *desc;
188 /* Parse KEY as a font description. */
189 string = parse_string (opt (d, options, key, default_value));
190 desc = pango_font_description_from_string (string);
193 error (0, 0, _("`%s': bad font specification"), string);
195 /* Fall back to DEFAULT_VALUE, which had better be a valid font
197 desc = pango_font_description_from_string (default_value);
198 assert (desc != NULL);
202 /* If the font description didn't include an explicit font size, then set it
203 to DEFAULT_POINTS. */
204 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
205 pango_font_description_set_size (desc,
206 default_points / 1000.0 * PANGO_SCALE);
211 static struct xr_driver *
212 xr_allocate (const char *name, int device_type, struct string_map *o)
214 int paper_width, paper_length;
215 struct output_driver *d;
216 struct xr_driver *xr;
219 xr = xzalloc (sizeof *xr);
221 output_driver_init (d, &cairo_driver_class, name, device_type);
223 font_points = parse_int (opt (d, o, "font-size", "10000"),
225 xr->fonts[XR_FONT_FIXED].desc = parse_font (d, o, "fixed-font", "monospace",
227 xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font (d, o, "prop-font",
228 "serif", font_points);
229 xr->fonts[XR_FONT_EMPHASIS].desc = parse_font (d, o, "emph-font",
230 "serif italic", font_points);
232 xr->line_gutter = XR_POINT;
233 xr->line_space = XR_POINT;
234 xr->line_width = XR_POINT / 2;
237 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
238 xr->left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
239 xr->right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
240 xr->top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
241 xr->bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
243 xr->width = paper_width - xr->left_margin - xr->right_margin;
244 xr->length = paper_length - xr->top_margin - xr->bottom_margin;
250 xr_is_72dpi (cairo_t *cr)
252 cairo_surface_type_t type;
253 cairo_surface_t *surface;
255 surface = cairo_get_target (cr);
256 type = cairo_surface_get_type (surface);
257 return type == CAIRO_SURFACE_TYPE_PDF || type == CAIRO_SURFACE_TYPE_PS;
261 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
263 PangoContext *context;
269 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
271 map = pango_cairo_font_map_get_default ();
272 context = pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (map));
273 if (xr_is_72dpi (cairo))
275 /* Pango seems to always scale fonts according to the DPI specified
276 in the font map, even if the surface has a real DPI. The default
277 DPI is 96, so on a 72 DPI device fonts end up being 96/72 = 133%
278 of their desired size. We deal with this by fixing the resolution
279 here. Presumably there is a better solution, but what? */
280 pango_cairo_context_set_resolution (context, 72.0);
285 for (i = 0; i < XR_N_FONTS; i++)
287 struct xr_font *font = &xr->fonts[i];
288 int char_width, char_height;
290 font->layout = pango_layout_new (context);
291 pango_layout_set_font_description (font->layout, font->desc);
293 pango_layout_set_text (font->layout, "0", 1);
294 pango_layout_get_size (font->layout, &char_width, &char_height);
295 xr->char_width = MAX (xr->char_width, char_width);
296 xr->char_height = MAX (xr->char_height, char_height);
299 g_object_unref (G_OBJECT (context));
301 if (xr->params == NULL)
303 int single_width, double_width;
305 xr->params = xmalloc (sizeof *xr->params);
306 xr->params->draw_line = xr_draw_line;
307 xr->params->measure_cell_width = xr_measure_cell_width;
308 xr->params->measure_cell_height = xr_measure_cell_height;
309 xr->params->draw_cell = xr_draw_cell;
310 xr->params->aux = xr;
311 xr->params->size[H] = xr->width;
312 xr->params->size[V] = xr->length;
313 xr->params->font_size[H] = xr->char_width;
314 xr->params->font_size[V] = xr->char_height;
316 single_width = 2 * xr->line_gutter + xr->line_width;
317 double_width = 2 * xr->line_gutter + xr->line_space + 2 * xr->line_width;
318 for (i = 0; i < TABLE_N_AXES; i++)
320 xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
321 xr->params->line_widths[i][RENDER_LINE_SINGLE] = single_width;
322 xr->params->line_widths[i][RENDER_LINE_DOUBLE] = double_width;
329 static struct output_driver *
330 xr_create (const char *file_name, enum settings_output_devices device_type,
331 struct string_map *o, enum xr_output_type file_type)
333 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
334 struct output_driver *d;
335 struct xr_driver *xr;
336 cairo_surface_t *surface;
337 cairo_status_t status;
338 double width_pt, length_pt;
340 xr = xr_allocate (file_name, device_type, o);
343 width_pt = (xr->width + xr->left_margin + xr->right_margin) / 1000.0;
344 length_pt = (xr->length + xr->top_margin + xr->bottom_margin) / 1000.0;
345 if (file_type == XR_PDF)
346 surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
347 else if (file_type == XR_PS)
348 surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
349 else if (file_type == XR_SVG)
350 surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
354 status = cairo_surface_status (surface);
355 if (status != CAIRO_STATUS_SUCCESS)
357 error (0, 0, _("error opening output file `%s': %s"),
358 file_name, cairo_status_to_string (status));
359 cairo_surface_destroy (surface);
363 xr->cairo = cairo_create (surface);
364 cairo_surface_destroy (surface);
366 if (!xr_set_cairo (xr, xr->cairo))
369 cairo_save (xr->cairo);
370 xr_driver_next_page (xr, xr->cairo);
372 if (xr->width / xr->char_width < MIN_WIDTH)
374 error (0, 0, _("The defined page is not wide enough to hold at least %d "
375 "characters in the default font. In fact, there's only "
376 "room for %d characters."),
378 xr->width / xr->char_width);
382 if (xr->length / xr->char_height < MIN_LENGTH)
384 error (0, 0, _("The defined page is not long enough to hold at least %d "
385 "lines in the default font. In fact, there's only "
386 "room for %d lines."),
388 xr->length / xr->char_height);
395 output_driver_destroy (&xr->driver);
399 static struct output_driver *
400 xr_pdf_create (const char *file_name, enum settings_output_devices device_type,
401 struct string_map *o)
403 return xr_create (file_name, device_type, o, XR_PDF);
406 static struct output_driver *
407 xr_ps_create (const char *file_name, enum settings_output_devices device_type,
408 struct string_map *o)
410 return xr_create (file_name, device_type, o, XR_PS);
413 static struct output_driver *
414 xr_svg_create (const char *file_name, enum settings_output_devices device_type,
415 struct string_map *o)
417 return xr_create (file_name, device_type, o, XR_SVG);
421 xr_destroy (struct output_driver *driver)
423 struct xr_driver *xr = xr_driver_cast (driver);
426 xr_driver_destroy_fsm (xr);
428 if (xr->cairo != NULL)
430 cairo_status_t status;
432 cairo_surface_finish (cairo_get_target (xr->cairo));
433 status = cairo_status (xr->cairo);
434 if (status != CAIRO_STATUS_SUCCESS)
435 error (0, 0, _("error drawing output for %s driver: %s"),
436 output_driver_get_name (driver),
437 cairo_status_to_string (status));
438 cairo_destroy (xr->cairo);
441 free (xr->command_name);
442 for (i = 0; i < XR_N_FONTS; i++)
444 struct xr_font *font = &xr->fonts[i];
446 if (font->desc != NULL)
447 pango_font_description_free (font->desc);
448 if (font->layout != NULL)
449 g_object_unref (font->layout);
457 xr_flush (struct output_driver *driver)
459 struct xr_driver *xr = xr_driver_cast (driver);
461 cairo_surface_flush (cairo_get_target (xr->cairo));
465 xr_init_caption_cell (const char *caption, struct table_cell *cell)
467 cell->contents = caption;
468 cell->options = TAB_LEFT;
469 cell->destructor = NULL;
472 static struct render_page *
473 xr_render_table_item (struct xr_driver *xr, const struct table_item *item,
474 int *caption_widthp, int *caption_heightp)
476 const char *caption = table_item_get_caption (item);
480 /* XXX doesn't do well with very large captions */
481 int min_width, max_width;
482 struct table_cell cell;
484 xr_init_caption_cell (caption, &cell);
486 xr_measure_cell_width (xr, &cell, &min_width, &max_width);
487 *caption_widthp = MIN (max_width, xr->width);
488 *caption_heightp = xr_measure_cell_height (xr, &cell, *caption_widthp);
491 *caption_heightp = 0;
493 return render_page_create (xr->params, table_item_get_table (item));
497 xr_submit (struct output_driver *driver, const struct output_item *output_item)
499 struct xr_driver *xr = xr_driver_cast (driver);
501 xr_driver_output_item (xr, output_item);
502 while (xr_driver_need_new_page (xr))
504 cairo_restore (xr->cairo);
505 cairo_show_page (xr->cairo);
506 cairo_save (xr->cairo);
507 xr_driver_next_page (xr, xr->cairo);
511 /* Functions for rendering a series of output items to a series of Cairo
512 contexts, with pagination.
514 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
515 its underlying implementation.
517 See the big comment in cairo.h for intended usage. */
519 /* Gives new page CAIRO to XR for output. CAIRO may be null to skip actually
520 rendering the page (which might be useful to find out how many pages an
521 output document has without actually rendering it). */
523 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
526 cairo_translate (cairo,
527 xr_to_pt (xr->left_margin),
528 xr_to_pt (xr->top_margin));
533 xr_driver_run_fsm (xr);
536 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
537 rendering a previous output item, that is, only if xr_driver_need_new_page()
540 xr_driver_output_item (struct xr_driver *xr,
541 const struct output_item *output_item)
543 assert (xr->fsm == NULL);
544 xr->fsm = xr_render_output_item (xr, output_item);
545 xr_driver_run_fsm (xr);
548 /* Returns true if XR is in the middle of rendering an output item and needs a
549 new page to be appended using xr_driver_next_page() to make progress,
552 xr_driver_need_new_page (const struct xr_driver *xr)
554 return xr->fsm != NULL;
557 /* Returns true if the current page doesn't have any content yet. */
559 xr_driver_is_page_blank (const struct xr_driver *xr)
565 xr_driver_destroy_fsm (struct xr_driver *xr)
569 xr->fsm->destroy (xr->fsm);
575 xr_driver_run_fsm (struct xr_driver *xr)
577 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
578 xr_driver_destroy_fsm (xr);
582 xr_layout_cell (struct xr_driver *, const struct table_cell *,
583 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
584 PangoWrapMode, int *width, int *height);
587 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
589 cairo_new_path (xr->cairo);
590 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
591 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
592 cairo_stroke (xr->cairo);
595 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
596 shortening it to X0...X1 if SHORTEN is true.
597 Draws a horizontal line X1...X3 at Y if RIGHT says so,
598 shortening it to X2...X3 if SHORTEN is true. */
600 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
601 enum render_line_style left, enum render_line_style right,
604 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
605 dump_line (xr, x0, y, x3, y);
608 if (left != RENDER_LINE_NONE)
609 dump_line (xr, x0, y, shorten ? x1 : x2, y);
610 if (right != RENDER_LINE_NONE)
611 dump_line (xr, shorten ? x2 : x1, y, x3, y);
615 /* Draws a vertical line Y0...Y2 at X if TOP says so,
616 shortening it to Y0...Y1 if SHORTEN is true.
617 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
618 shortening it to Y2...Y3 if SHORTEN is true. */
620 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
621 enum render_line_style top, enum render_line_style bottom,
624 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
625 dump_line (xr, x, y0, x, y3);
628 if (top != RENDER_LINE_NONE)
629 dump_line (xr, x, y0, x, shorten ? y1 : y2);
630 if (bottom != RENDER_LINE_NONE)
631 dump_line (xr, x, shorten ? y2 : y1, x, y3);
636 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
637 enum render_line_style styles[TABLE_N_AXES][2])
639 const int x0 = bb[H][0];
640 const int y0 = bb[V][0];
641 const int x3 = bb[H][1];
642 const int y3 = bb[V][1];
643 const int top = styles[H][0];
644 const int left = styles[V][0];
645 const int bottom = styles[H][1];
646 const int right = styles[V][1];
648 /* The algorithm here is somewhat subtle, to allow it to handle
649 all the kinds of intersections that we need.
651 Three additional ordinates are assigned along the x axis. The
652 first is xc, midway between x0 and x3. The others are x1 and
653 x2; for a single vertical line these are equal to xc, and for
654 a double vertical line they are the ordinates of the left and
655 right half of the double line.
657 yc, y1, and y2 are assigned similarly along the y axis.
659 The following diagram shows the coordinate system and output
660 for double top and bottom lines, single left line, and no
664 y0 ________________________
670 y1 = y2 = yc |######### # |
675 y3 |________#_____#_______|
677 struct xr_driver *xr = xr_;
679 /* Offset from center of each line in a pair of double lines. */
680 int double_line_ofs = (xr->line_space + xr->line_width) / 2;
682 /* Are the lines along each axis single or double?
683 (It doesn't make sense to have different kinds of line on the
684 same axis, so we don't try to gracefully handle that case.) */
685 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
686 bool double_horz = left == RENDER_LINE_DOUBLE || right == RENDER_LINE_DOUBLE;
688 /* When horizontal lines are doubled,
689 the left-side line along y1 normally runs from x0 to x2,
690 and the right-side line along y1 from x3 to x1.
691 If the top-side line is also doubled, we shorten the y1 lines,
692 so that the left-side line runs only to x1,
693 and the right-side line only to x2.
694 Otherwise, the horizontal line at y = y1 below would cut off
695 the intersection, which looks ugly:
697 y0 ________________________
702 y1 |######### ########|
705 y2 |######################|
708 y3 |______________________|
709 It is more of a judgment call when the horizontal line is
710 single. We actually choose to cut off the line anyhow, as
711 shown in the first diagram above.
713 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
714 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
715 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
716 int horz_line_ofs = double_vert ? double_line_ofs : 0;
717 int xc = (x0 + x3) / 2;
718 int x1 = xc - horz_line_ofs;
719 int x2 = xc + horz_line_ofs;
721 bool shorten_x1_lines = left == RENDER_LINE_DOUBLE;
722 bool shorten_x2_lines = right == RENDER_LINE_DOUBLE;
723 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
724 int vert_line_ofs = double_horz ? double_line_ofs : 0;
725 int yc = (y0 + y3) / 2;
726 int y1 = yc - vert_line_ofs;
727 int y2 = yc + vert_line_ofs;
730 horz_line (xr, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
733 horz_line (xr, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
734 horz_line (xr, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
738 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
741 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
742 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
747 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
748 int *min_width, int *max_width)
750 struct xr_driver *xr = xr_;
751 int bb[TABLE_N_AXES][2];
752 int clip[TABLE_N_AXES][2];
759 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
760 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, max_width, &h);
763 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, min_width, &h);
767 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
769 struct xr_driver *xr = xr_;
770 int bb[TABLE_N_AXES][2];
771 int clip[TABLE_N_AXES][2];
778 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
779 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
784 xr_draw_cell (void *xr_, const struct table_cell *cell,
785 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
787 struct xr_driver *xr = xr_;
790 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
794 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
795 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
796 PangoWrapMode wrap, int *width, int *height)
798 struct xr_font *font;
800 font = (cell->options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
801 : cell->options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
802 : &xr->fonts[XR_FONT_PROPORTIONAL]);
804 pango_layout_set_text (font->layout, cell->contents, -1);
806 pango_layout_set_alignment (
808 ((cell->options & TAB_ALIGNMENT) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
809 : (cell->options & TAB_ALIGNMENT) == TAB_LEFT ? PANGO_ALIGN_LEFT
810 : PANGO_ALIGN_CENTER));
811 pango_layout_set_width (font->layout,
812 bb[H][1] == INT_MAX ? -1 : bb[H][1] - bb[H][0]);
813 pango_layout_set_wrap (font->layout, wrap);
815 if (clip[H][0] != clip[H][1])
817 cairo_save (xr->cairo);
819 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
821 double x0 = xr_to_pt (clip[H][0]);
822 double y0 = xr_to_pt (clip[V][0] + xr->y);
823 double x1 = xr_to_pt (clip[H][1]);
824 double y1 = xr_to_pt (clip[V][1] + xr->y);
826 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
827 cairo_clip (xr->cairo);
830 cairo_translate (xr->cairo,
832 xr_to_pt (bb[V][0] + xr->y));
833 pango_cairo_show_layout (xr->cairo, font->layout);
834 cairo_restore (xr->cairo);
837 if (width != NULL || height != NULL)
841 pango_layout_get_size (font->layout, &w, &h);
850 xr_draw_title (struct xr_driver *xr, const char *title,
851 int title_width, int title_height)
853 struct table_cell cell;
854 int bb[TABLE_N_AXES][2];
856 xr_init_caption_cell (title, &cell);
858 bb[H][1] = title_width;
860 bb[V][1] = title_height;
861 xr_draw_cell (xr, &cell, bb, bb);
864 struct output_driver_factory pdf_driver_factory = { "pdf", xr_pdf_create };
865 struct output_driver_factory ps_driver_factory = { "ps", xr_ps_create };
866 struct output_driver_factory svg_driver_factory = { "svg", xr_svg_create };
868 static const struct output_driver_class cairo_driver_class =
876 /* GUI rendering helpers. */
880 struct output_item *item;
883 struct render_page *page;
884 struct xr_driver *xr;
889 #define CHART_WIDTH 500
890 #define CHART_HEIGHT 375
893 xr_driver_create (cairo_t *cairo, struct string_map *options)
895 struct xr_driver *xr = xr_allocate ("cairo", 0, options);
896 if (!xr_set_cairo (xr, cairo))
898 output_driver_destroy (&xr->driver);
904 /* Destroy XR, which should have been created with xr_driver_create(). Any
905 cairo_t added to XR is not destroyed, because it is owned by the client. */
907 xr_driver_destroy (struct xr_driver *xr)
912 output_driver_destroy (&xr->driver);
916 static struct xr_rendering *
917 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
919 struct table_item *table_item;
920 struct xr_rendering *r;
922 table_item = table_item_create (table_from_string (0, text), NULL);
923 r = xr_rendering_create (xr, &table_item->output_item, cr);
924 table_item_unref (table_item);
929 struct xr_rendering *
930 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
933 struct xr_rendering *r = NULL;
935 if (is_text_item (item))
936 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
938 else if (is_message_item (item))
940 const struct message_item *message_item = to_message_item (item);
941 const struct msg *msg = message_item_get_msg (message_item);
942 char *s = msg_to_string (msg, NULL);
943 r = xr_rendering_create_text (xr, s, cr);
946 else if (is_table_item (item))
948 r = xzalloc (sizeof *r);
949 r->item = output_item_ref (item);
951 xr_set_cairo (xr, cr);
952 r->page = xr_render_table_item (xr, to_table_item (item),
953 &r->title_width, &r->title_height);
955 else if (is_chart_item (item))
957 r = xzalloc (sizeof *r);
958 r->item = output_item_ref (item);
965 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
967 if (is_table_item (r->item))
969 int w0 = render_page_get_size (r->page, H);
970 int w1 = r->title_width;
971 *w = MAX (w0, w1) / 1024;
972 *h = (render_page_get_size (r->page, V) + r->title_height) / 1024;
981 /* Draws onto CR at least the region of R that is enclosed in (X,Y)-(X+W,Y+H),
982 and possibly some additional parts. */
984 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
985 int x, int y, int w, int h)
987 if (is_table_item (r->item))
989 struct xr_driver *xr = r->xr;
991 xr_set_cairo (xr, cr);
993 if (r->title_height > 0)
996 xr_draw_title (xr, table_item_get_caption (to_table_item (r->item)),
997 r->title_width, r->title_height);
1000 xr->y = r->title_height;
1001 render_page_draw_region (r->page,
1002 x * 1024, y * 1024, w * 1024, h * 1024);
1005 xr_draw_chart (to_chart_item (r->item), cr,
1006 0, 0, CHART_WIDTH, CHART_HEIGHT);
1010 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1011 double x, double y, double width, double height)
1013 struct xrchart_geometry geom;
1016 cairo_translate (cr, x, y + height);
1017 cairo_scale (cr, 1.0, -1.0);
1018 xrchart_geometry_init (cr, &geom, width, height);
1019 if (is_boxplot (chart_item))
1020 xrchart_draw_boxplot (chart_item, cr, &geom);
1021 else if (is_histogram_chart (chart_item))
1022 xrchart_draw_histogram (chart_item, cr, &geom);
1023 else if (is_np_plot_chart (chart_item))
1024 xrchart_draw_np_plot (chart_item, cr, &geom);
1025 else if (is_piechart (chart_item))
1026 xrchart_draw_piechart (chart_item, cr, &geom);
1027 else if (is_roc_chart (chart_item))
1028 xrchart_draw_roc (chart_item, cr, &geom);
1029 else if (is_scree (chart_item))
1030 xrchart_draw_scree (chart_item, cr, &geom);
1033 xrchart_geometry_free (cr, &geom);
1039 xr_draw_png_chart (const struct chart_item *item,
1040 const char *file_name_template, int number)
1042 const int width = 640;
1043 const int length = 480;
1045 cairo_surface_t *surface;
1046 cairo_status_t status;
1047 const char *number_pos;
1051 number_pos = strchr (file_name_template, '#');
1052 if (number_pos != NULL)
1053 file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1054 file_name_template, number, number_pos + 1);
1056 file_name = xstrdup (file_name_template);
1058 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1059 cr = cairo_create (surface);
1062 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
1063 cairo_rectangle (cr, 0, 0, width, length);
1067 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
1069 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1071 status = cairo_surface_write_to_png (surface, file_name);
1072 if (status != CAIRO_STATUS_SUCCESS)
1073 error (0, 0, _("error writing output file `%s': %s"),
1074 file_name, cairo_status_to_string (status));
1077 cairo_surface_destroy (surface);
1082 struct xr_table_state
1084 struct xr_render_fsm fsm;
1085 struct table_item *table_item;
1086 struct render_break x_break;
1087 struct render_break y_break;
1092 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1094 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1098 struct render_page *y_slice;
1101 while (!render_break_has_next (&ts->y_break))
1103 struct render_page *x_slice;
1105 render_break_destroy (&ts->y_break);
1106 if (!render_break_has_next (&ts->x_break))
1109 x_slice = render_break_next (&ts->x_break, xr->width);
1110 render_break_init (&ts->y_break, x_slice, V);
1113 space = xr->length - xr->y;
1114 if (render_break_next_size (&ts->y_break) > space)
1120 y_slice = render_break_next (&ts->y_break, space);
1121 if (ts->caption_height)
1124 xr_draw_title (xr, table_item_get_caption (ts->table_item),
1125 xr->width, ts->caption_height);
1127 xr->y += ts->caption_height;
1128 ts->caption_height = 0;
1132 render_page_draw (y_slice);
1133 xr->y += render_page_get_size (y_slice, V);
1134 render_page_unref (y_slice);
1139 xr_table_destroy (struct xr_render_fsm *fsm)
1141 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1143 table_item_unref (ts->table_item);
1144 render_break_destroy (&ts->x_break);
1145 render_break_destroy (&ts->y_break);
1149 static struct xr_render_fsm *
1150 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1152 struct xr_table_state *ts;
1153 struct render_page *page;
1156 ts = xmalloc (sizeof *ts);
1157 ts->fsm.render = xr_table_render;
1158 ts->fsm.destroy = xr_table_destroy;
1159 ts->table_item = table_item_ref (table_item);
1162 xr->y += xr->char_height;
1164 page = xr_render_table_item (xr, table_item,
1165 &caption_width, &ts->caption_height);
1166 xr->params->size[V] = xr->length - ts->caption_height;
1168 render_break_init (&ts->x_break, page, H);
1169 render_break_init_empty (&ts->y_break);
1174 struct xr_chart_state
1176 struct xr_render_fsm fsm;
1177 struct chart_item *chart_item;
1181 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1183 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1188 if (xr->cairo != NULL)
1189 xr_draw_chart (cs->chart_item, xr->cairo, 0.0, 0.0,
1190 xr_to_pt (xr->width), xr_to_pt (xr->length));
1197 xr_chart_destroy (struct xr_render_fsm *fsm)
1199 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1201 chart_item_unref (cs->chart_item);
1205 static struct xr_render_fsm *
1206 xr_render_chart (const struct chart_item *chart_item)
1208 struct xr_chart_state *cs;
1210 cs = xmalloc (sizeof *cs);
1211 cs->fsm.render = xr_chart_render;
1212 cs->fsm.destroy = xr_chart_destroy;
1213 cs->chart_item = chart_item_ref (chart_item);
1219 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1225 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1227 /* Nothing to do. */
1230 static struct xr_render_fsm *
1231 xr_render_eject (void)
1233 static struct xr_render_fsm eject_renderer =
1239 return &eject_renderer;
1242 static struct xr_render_fsm *
1243 xr_create_text_renderer (struct xr_driver *xr, const char *text)
1245 struct table_item *table_item;
1246 struct xr_render_fsm *fsm;
1248 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
1249 fsm = xr_render_table (xr, table_item);
1250 table_item_unref (table_item);
1255 static struct xr_render_fsm *
1256 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1258 enum text_item_type type = text_item_get_type (text_item);
1259 const char *text = text_item_get_text (text_item);
1263 case TEXT_ITEM_TITLE:
1265 xr->title = xstrdup (text);
1268 case TEXT_ITEM_SUBTITLE:
1269 free (xr->subtitle);
1270 xr->subtitle = xstrdup (text);
1273 case TEXT_ITEM_COMMAND_CLOSE:
1276 case TEXT_ITEM_BLANK_LINE:
1278 xr->y += xr->char_height;
1281 case TEXT_ITEM_EJECT_PAGE:
1283 return xr_render_eject ();
1287 return xr_create_text_renderer (xr, text);
1293 static struct xr_render_fsm *
1294 xr_render_message (struct xr_driver *xr,
1295 const struct message_item *message_item)
1297 const struct msg *msg = message_item_get_msg (message_item);
1298 struct xr_render_fsm *fsm;
1301 s = msg_to_string (msg, xr->command_name);
1302 fsm = xr_create_text_renderer (xr, s);
1308 static struct xr_render_fsm *
1309 xr_render_output_item (struct xr_driver *xr,
1310 const struct output_item *output_item)
1312 if (is_table_item (output_item))
1313 return xr_render_table (xr, to_table_item (output_item));
1314 else if (is_chart_item (output_item))
1315 return xr_render_chart (to_chart_item (output_item));
1316 else if (is_text_item (output_item))
1317 return xr_render_text (xr, to_text_item (output_item));
1318 else if (is_message_item (output_item))
1319 return xr_render_message (xr, to_message_item (output_item));