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. */
99 PangoFontDescription *desc;
101 PangoFontMetrics *metrics;
104 /* An output item whose rendering is in progress. */
107 /* Renders as much of itself as it can on the current page. Returns true
108 if rendering is complete, false if the output item needs another
110 bool (*render) (struct xr_render_fsm *, struct xr_driver *);
112 /* Destroys the output item. */
113 void (*destroy) (struct xr_render_fsm *);
116 /* Cairo output driver. */
119 struct output_driver driver;
121 /* User parameters. */
122 struct xr_font fonts[XR_N_FONTS];
123 int font_height; /* In XR units. */
125 int width; /* Page width minus margins. */
126 int length; /* Page length minus margins and header. */
128 int left_margin; /* Left margin in XR units. */
129 int right_margin; /* Right margin in XR units. */
130 int top_margin; /* Top margin in XR units. */
131 int bottom_margin; /* Bottom margin in XR units. */
133 int line_gutter; /* Space around lines. */
134 int line_space; /* Space between lines. */
135 int line_width; /* Width of lines. */
137 /* Internal state. */
138 struct render_params *params;
143 int page_number; /* Current page number. */
145 struct xr_render_fsm *fsm;
148 static const struct output_driver_class cairo_driver_class;
150 static void xr_driver_destroy_fsm (struct xr_driver *);
151 static void xr_driver_run_fsm (struct xr_driver *);
153 static bool load_font (struct xr_driver *, struct xr_font *);
154 static void free_font (struct xr_font *);
156 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
157 enum render_line_style styles[TABLE_N_AXES][2]);
158 static void xr_measure_cell_width (void *, const struct table_cell *,
160 static int xr_measure_cell_height (void *, const struct table_cell *,
162 static void xr_draw_cell (void *, const struct table_cell *,
163 int bb[TABLE_N_AXES][2],
164 int clip[TABLE_N_AXES][2]);
166 static struct xr_render_fsm *xr_render_output_item (
167 struct xr_driver *, const struct output_item *);
169 /* Output driver basics. */
171 static struct xr_driver *
172 xr_driver_cast (struct output_driver *driver)
174 assert (driver->class == &cairo_driver_class);
175 return UP_CAST (driver, struct xr_driver, driver);
178 static struct driver_option *
179 opt (struct output_driver *d, struct string_map *options, const char *key,
180 const char *default_value)
182 return driver_option_get (d, options, key, default_value);
185 static struct xr_driver *
186 xr_allocate (const char *name, int device_type, struct string_map *o)
188 int paper_width, paper_length;
189 struct output_driver *d;
190 struct xr_driver *xr;
192 xr = xzalloc (sizeof *xr);
194 output_driver_init (d, &cairo_driver_class, name, device_type);
195 xr->font_height = XR_POINT * 10;
196 xr->fonts[XR_FONT_FIXED].string
197 = parse_string (opt (d, o, "fixed-font", "monospace"));
198 xr->fonts[XR_FONT_PROPORTIONAL].string
199 = parse_string (opt (d, o, "prop-font", "serif"));
200 xr->fonts[XR_FONT_EMPHASIS].string
201 = parse_string (opt (d, o, "emph-font", "serif italic"));
202 xr->line_gutter = XR_POINT;
203 xr->line_space = XR_POINT;
204 xr->line_width = XR_POINT / 2;
207 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
208 xr->left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
209 xr->right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
210 xr->top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
211 xr->bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
213 xr->width = paper_width - xr->left_margin - xr->right_margin;
214 xr->length = paper_length - xr->top_margin - xr->bottom_margin;
220 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
226 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
228 for (i = 0; i < XR_N_FONTS; i++)
229 if (!load_font (xr, &xr->fonts[i]))
232 if (xr->params == NULL)
234 int single_width, double_width;
236 xr->params = xmalloc (sizeof *xr->params);
237 xr->params->draw_line = xr_draw_line;
238 xr->params->measure_cell_width = xr_measure_cell_width;
239 xr->params->measure_cell_height = xr_measure_cell_height;
240 xr->params->draw_cell = xr_draw_cell;
241 xr->params->aux = xr;
242 xr->params->size[H] = xr->width;
243 xr->params->size[V] = xr->length;
244 xr->params->font_size[H] = xr->font_height / 2; /* XXX */
245 xr->params->font_size[V] = xr->font_height;
247 single_width = 2 * xr->line_gutter + xr->line_width;
248 double_width = 2 * xr->line_gutter + xr->line_space + 2 * xr->line_width;
249 for (i = 0; i < TABLE_N_AXES; i++)
251 xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
252 xr->params->line_widths[i][RENDER_LINE_SINGLE] = single_width;
253 xr->params->line_widths[i][RENDER_LINE_DOUBLE] = double_width;
260 static struct output_driver *
261 xr_create (const char *file_name, enum settings_output_devices device_type,
262 struct string_map *o, enum xr_output_type file_type)
264 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
265 struct output_driver *d;
266 struct xr_driver *xr;
267 cairo_surface_t *surface;
268 cairo_status_t status;
269 double width_pt, length_pt;
271 xr = xr_allocate (file_name, device_type, o);
274 width_pt = (xr->width + xr->left_margin + xr->right_margin) / 1000.0;
275 length_pt = (xr->length + xr->top_margin + xr->bottom_margin) / 1000.0;
276 if (file_type == XR_PDF)
277 surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
278 else if (file_type == XR_PS)
279 surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
280 else if (file_type == XR_SVG)
281 surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
285 status = cairo_surface_status (surface);
286 if (status != CAIRO_STATUS_SUCCESS)
288 error (0, 0, _("error opening output file \"%s\": %s"),
289 file_name, cairo_status_to_string (status));
290 cairo_surface_destroy (surface);
294 xr->cairo = cairo_create (surface);
295 cairo_surface_destroy (surface);
297 if (!xr_set_cairo (xr, xr->cairo))
300 cairo_save (xr->cairo);
301 xr_driver_next_page (xr, xr->cairo);
303 if (xr->width / (xr->font_height / 2) < MIN_WIDTH)
305 error (0, 0, _("The defined page is not wide enough to hold at least %d "
306 "characters in the default font. In fact, there's only "
307 "room for %d characters."),
309 xr->width / (xr->font_height / 2));
313 if (xr->length / xr->font_height < MIN_LENGTH)
315 error (0, 0, _("The defined page is not long enough to hold at least %d "
316 "lines in the default font. In fact, there's only "
317 "room for %d lines."),
319 xr->length / xr->font_height);
326 output_driver_destroy (&xr->driver);
330 static struct output_driver *
331 xr_pdf_create (const char *file_name, enum settings_output_devices device_type,
332 struct string_map *o)
334 return xr_create (file_name, device_type, o, XR_PDF);
337 static struct output_driver *
338 xr_ps_create (const char *file_name, enum settings_output_devices device_type,
339 struct string_map *o)
341 return xr_create (file_name, device_type, o, XR_PS);
344 static struct output_driver *
345 xr_svg_create (const char *file_name, enum settings_output_devices device_type,
346 struct string_map *o)
348 return xr_create (file_name, device_type, o, XR_SVG);
352 xr_destroy (struct output_driver *driver)
354 struct xr_driver *xr = xr_driver_cast (driver);
357 xr_driver_destroy_fsm (xr);
359 if (xr->cairo != NULL)
361 cairo_status_t status;
363 cairo_surface_finish (cairo_get_target (xr->cairo));
364 status = cairo_status (xr->cairo);
365 if (status != CAIRO_STATUS_SUCCESS)
366 error (0, 0, _("error drawing output for %s driver: %s"),
367 output_driver_get_name (driver),
368 cairo_status_to_string (status));
369 cairo_destroy (xr->cairo);
372 free (xr->command_name);
373 for (i = 0; i < XR_N_FONTS; i++)
374 free_font (&xr->fonts[i]);
380 xr_flush (struct output_driver *driver)
382 struct xr_driver *xr = xr_driver_cast (driver);
384 cairo_surface_flush (cairo_get_target (xr->cairo));
388 xr_init_caption_cell (const char *caption, struct table_cell *cell)
390 cell->contents = caption;
391 cell->options = TAB_LEFT;
392 cell->destructor = NULL;
395 static struct render_page *
396 xr_render_table_item (struct xr_driver *xr, const struct table_item *item,
397 int *caption_heightp)
399 const char *caption = table_item_get_caption (item);
403 /* XXX doesn't do well with very large captions */
404 struct table_cell cell;
405 xr_init_caption_cell (caption, &cell);
406 *caption_heightp = xr_measure_cell_height (xr, &cell, xr->width);
409 *caption_heightp = 0;
411 return render_page_create (xr->params, table_item_get_table (item));
415 xr_submit (struct output_driver *driver, const struct output_item *output_item)
417 struct xr_driver *xr = xr_driver_cast (driver);
419 xr_driver_output_item (xr, output_item);
420 while (xr_driver_need_new_page (xr))
422 cairo_restore (xr->cairo);
423 cairo_show_page (xr->cairo);
424 cairo_save (xr->cairo);
425 xr_driver_next_page (xr, xr->cairo);
429 /* Functions for rendering a series of output items to a series of Cairo
430 contexts, with pagination.
432 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
433 its underlying implementation.
435 See the big comment in cairo.h for intended usage. */
437 /* Gives new page CAIRO to XR for output. CAIRO may be null to skip actually
438 rendering the page (which might be useful to find out how many pages an
439 output document has without actually rendering it). */
441 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
444 cairo_translate (cairo,
445 xr_to_pt (xr->left_margin),
446 xr_to_pt (xr->top_margin));
451 xr_driver_run_fsm (xr);
454 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
455 rendering a previous output item, that is, only if xr_driver_need_new_page()
458 xr_driver_output_item (struct xr_driver *xr,
459 const struct output_item *output_item)
461 assert (xr->fsm == NULL);
462 xr->fsm = xr_render_output_item (xr, output_item);
463 xr_driver_run_fsm (xr);
466 /* Returns true if XR is in the middle of rendering an output item and needs a
467 new page to be appended using xr_driver_next_page() to make progress,
470 xr_driver_need_new_page (const struct xr_driver *xr)
472 return xr->fsm != NULL;
475 /* Returns true if the current page doesn't have any content yet. */
477 xr_driver_is_page_blank (const struct xr_driver *xr)
483 xr_driver_destroy_fsm (struct xr_driver *xr)
487 xr->fsm->destroy (xr->fsm);
493 xr_driver_run_fsm (struct xr_driver *xr)
495 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
496 xr_driver_destroy_fsm (xr);
500 xr_layout_cell (struct xr_driver *, const struct table_cell *,
501 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
502 PangoWrapMode, int *width, int *height);
505 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
507 cairo_new_path (xr->cairo);
508 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
509 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
510 cairo_stroke (xr->cairo);
513 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
514 shortening it to X0...X1 if SHORTEN is true.
515 Draws a horizontal line X1...X3 at Y if RIGHT says so,
516 shortening it to X2...X3 if SHORTEN is true. */
518 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
519 enum render_line_style left, enum render_line_style right,
522 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
523 dump_line (xr, x0, y, x3, y);
526 if (left != RENDER_LINE_NONE)
527 dump_line (xr, x0, y, shorten ? x1 : x2, y);
528 if (right != RENDER_LINE_NONE)
529 dump_line (xr, shorten ? x2 : x1, y, x3, y);
533 /* Draws a vertical line Y0...Y2 at X if TOP says so,
534 shortening it to Y0...Y1 if SHORTEN is true.
535 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
536 shortening it to Y2...Y3 if SHORTEN is true. */
538 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
539 enum render_line_style top, enum render_line_style bottom,
542 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
543 dump_line (xr, x, y0, x, y3);
546 if (top != RENDER_LINE_NONE)
547 dump_line (xr, x, y0, x, shorten ? y1 : y2);
548 if (bottom != RENDER_LINE_NONE)
549 dump_line (xr, x, shorten ? y2 : y1, x, y3);
554 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
555 enum render_line_style styles[TABLE_N_AXES][2])
557 const int x0 = bb[H][0];
558 const int y0 = bb[V][0];
559 const int x3 = bb[H][1];
560 const int y3 = bb[V][1];
561 const int top = styles[H][0];
562 const int left = styles[V][0];
563 const int bottom = styles[H][1];
564 const int right = styles[V][1];
566 /* The algorithm here is somewhat subtle, to allow it to handle
567 all the kinds of intersections that we need.
569 Three additional ordinates are assigned along the x axis. The
570 first is xc, midway between x0 and x3. The others are x1 and
571 x2; for a single vertical line these are equal to xc, and for
572 a double vertical line they are the ordinates of the left and
573 right half of the double line.
575 yc, y1, and y2 are assigned similarly along the y axis.
577 The following diagram shows the coordinate system and output
578 for double top and bottom lines, single left line, and no
582 y0 ________________________
588 y1 = y2 = yc |######### # |
593 y3 |________#_____#_______|
595 struct xr_driver *xr = xr_;
597 /* Offset from center of each line in a pair of double lines. */
598 int double_line_ofs = (xr->line_space + xr->line_width) / 2;
600 /* Are the lines along each axis single or double?
601 (It doesn't make sense to have different kinds of line on the
602 same axis, so we don't try to gracefully handle that case.) */
603 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
604 bool double_horz = left == RENDER_LINE_DOUBLE || right == RENDER_LINE_DOUBLE;
606 /* When horizontal lines are doubled,
607 the left-side line along y1 normally runs from x0 to x2,
608 and the right-side line along y1 from x3 to x1.
609 If the top-side line is also doubled, we shorten the y1 lines,
610 so that the left-side line runs only to x1,
611 and the right-side line only to x2.
612 Otherwise, the horizontal line at y = y1 below would cut off
613 the intersection, which looks ugly:
615 y0 ________________________
620 y1 |######### ########|
623 y2 |######################|
626 y3 |______________________|
627 It is more of a judgment call when the horizontal line is
628 single. We actually choose to cut off the line anyhow, as
629 shown in the first diagram above.
631 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
632 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
633 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
634 int horz_line_ofs = double_vert ? double_line_ofs : 0;
635 int xc = (x0 + x3) / 2;
636 int x1 = xc - horz_line_ofs;
637 int x2 = xc + horz_line_ofs;
639 bool shorten_x1_lines = left == RENDER_LINE_DOUBLE;
640 bool shorten_x2_lines = right == RENDER_LINE_DOUBLE;
641 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
642 int vert_line_ofs = double_horz ? double_line_ofs : 0;
643 int yc = (y0 + y3) / 2;
644 int y1 = yc - vert_line_ofs;
645 int y2 = yc + vert_line_ofs;
648 horz_line (xr, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
651 horz_line (xr, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
652 horz_line (xr, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
656 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
659 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
660 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
665 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
666 int *min_width, int *max_width)
668 struct xr_driver *xr = xr_;
669 int bb[TABLE_N_AXES][2];
670 int clip[TABLE_N_AXES][2];
677 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
678 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, max_width, &h);
681 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, min_width, &h);
685 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
687 struct xr_driver *xr = xr_;
688 int bb[TABLE_N_AXES][2];
689 int clip[TABLE_N_AXES][2];
696 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
697 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
702 xr_draw_cell (void *xr_, const struct table_cell *cell,
703 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
705 struct xr_driver *xr = xr_;
708 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
712 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
713 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
714 PangoWrapMode wrap, int *width, int *height)
716 struct xr_font *font;
718 font = (cell->options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
719 : cell->options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
720 : &xr->fonts[XR_FONT_PROPORTIONAL]);
722 pango_layout_set_text (font->layout, cell->contents, -1);
724 pango_layout_set_alignment (
726 ((cell->options & TAB_ALIGNMENT) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
727 : (cell->options & TAB_ALIGNMENT) == TAB_LEFT ? PANGO_ALIGN_LEFT
728 : PANGO_ALIGN_CENTER));
729 pango_layout_set_width (font->layout,
730 bb[H][1] == INT_MAX ? -1 : bb[H][1] - bb[H][0]);
731 pango_layout_set_wrap (font->layout, wrap);
733 if (clip[H][0] != clip[H][1])
735 cairo_save (xr->cairo);
737 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
739 double x0 = xr_to_pt (clip[H][0]);
740 double y0 = xr_to_pt (clip[V][0] + xr->y);
741 double x1 = xr_to_pt (clip[H][1]);
742 double y1 = xr_to_pt (clip[V][1] + xr->y);
744 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
745 cairo_clip (xr->cairo);
748 cairo_translate (xr->cairo,
750 xr_to_pt (bb[V][0] + xr->y));
751 pango_cairo_show_layout (xr->cairo, font->layout);
752 cairo_restore (xr->cairo);
755 if (width != NULL || height != NULL)
759 pango_layout_get_size (font->layout, &w, &h);
767 /* Attempts to load FONT, initializing its other members based on
768 its 'string' member and the information in DRIVER. Returns true
769 if successful, otherwise false. */
771 load_font (struct xr_driver *xr, struct xr_font *font)
773 PangoContext *context;
774 PangoLanguage *language;
776 font->desc = pango_font_description_from_string (font->string);
777 if (font->desc == NULL)
779 error (0, 0, _("\"%s\": bad font specification"), font->string);
782 pango_font_description_set_absolute_size (font->desc, xr->font_height);
784 font->layout = pango_cairo_create_layout (xr->cairo);
785 pango_layout_set_font_description (font->layout, font->desc);
787 language = pango_language_get_default ();
788 context = pango_layout_get_context (font->layout);
789 font->metrics = pango_context_get_metrics (context, font->desc, language);
796 free_font (struct xr_font *font)
799 if (font->desc != NULL)
800 pango_font_description_free (font->desc);
801 pango_font_metrics_unref (font->metrics);
802 if (font->layout != NULL)
803 g_object_unref (font->layout);
806 struct output_driver_factory pdf_driver_factory = { "pdf", xr_pdf_create };
807 struct output_driver_factory ps_driver_factory = { "ps", xr_ps_create };
808 struct output_driver_factory svg_driver_factory = { "svg", xr_svg_create };
810 static const struct output_driver_class cairo_driver_class =
818 /* GUI rendering helpers. */
823 struct render_page *page;
824 struct xr_driver *xr;
828 struct chart_item *chart;
831 #define CHART_WIDTH 500
832 #define CHART_HEIGHT 375
835 xr_driver_create (cairo_t *cairo, struct string_map *options)
837 struct xr_driver *xr = xr_allocate ("cairo", 0, options);
838 if (!xr_set_cairo (xr, cairo))
840 output_driver_destroy (&xr->driver);
846 /* Destroy XR, which should have been created with xr_driver_create(). Any
847 cairo_t added to XR is not destroyed, because it is owned by the client. */
849 xr_driver_destroy (struct xr_driver *xr)
854 output_driver_destroy (&xr->driver);
858 static struct xr_rendering *
859 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
861 struct table_item *table_item;
862 struct xr_rendering *r;
864 table_item = table_item_create (table_from_string (0, text), NULL);
865 r = xr_rendering_create (xr, &table_item->output_item, cr);
866 table_item_unref (table_item);
871 struct xr_rendering *
872 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
875 struct xr_rendering *r = NULL;
877 if (is_text_item (item))
878 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
880 else if (is_message_item (item))
882 const struct message_item *message_item = to_message_item (item);
883 const struct msg *msg = message_item_get_msg (message_item);
884 char *s = msg_to_string (msg, NULL);
885 r = xr_rendering_create_text (xr, s, cr);
888 else if (is_table_item (item))
890 r = xzalloc (sizeof *r);
892 xr_set_cairo (xr, cr);
893 r->page = xr_render_table_item (xr, to_table_item (item),
896 else if (is_chart_item (item))
898 r = xzalloc (sizeof *r);
899 r->chart = to_chart_item (output_item_ref (item));
906 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
908 if (r->chart == NULL)
910 *w = render_page_get_size (r->page, H) / 1024;
911 *h = (render_page_get_size (r->page, V) + r->title_height) / 1024;
920 /* Draws onto CR at least the region of R that is enclosed in (X,Y)-(X+W,Y+H),
921 and possibly some additional parts. */
923 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
924 int x, int y, int w, int h)
926 if (r->chart == NULL)
928 struct xr_driver *xr = r->xr;
930 xr_set_cairo (xr, cr);
932 render_page_draw_region (r->page,
933 x * 1024, y * 1024, w * 1024, h * 1024);
936 xr_draw_chart (r->chart, cr, 0, 0, CHART_WIDTH, CHART_HEIGHT);
940 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
941 double x, double y, double width, double height)
943 struct xrchart_geometry geom;
946 cairo_translate (cr, x, y + height);
947 cairo_scale (cr, 1.0, -1.0);
948 xrchart_geometry_init (cr, &geom, width, height);
949 if (is_boxplot (chart_item))
950 xrchart_draw_boxplot (chart_item, cr, &geom);
951 else if (is_histogram_chart (chart_item))
952 xrchart_draw_histogram (chart_item, cr, &geom);
953 else if (is_np_plot_chart (chart_item))
954 xrchart_draw_np_plot (chart_item, cr, &geom);
955 else if (is_piechart (chart_item))
956 xrchart_draw_piechart (chart_item, cr, &geom);
957 else if (is_roc_chart (chart_item))
958 xrchart_draw_roc (chart_item, cr, &geom);
959 else if (is_scree (chart_item))
960 xrchart_draw_scree (chart_item, cr, &geom);
963 xrchart_geometry_free (cr, &geom);
969 xr_draw_png_chart (const struct chart_item *item,
970 const char *file_name_template, int number)
972 const int width = 640;
973 const int length = 480;
975 cairo_surface_t *surface;
976 cairo_status_t status;
977 const char *number_pos;
981 number_pos = strchr (file_name_template, '#');
982 if (number_pos != NULL)
983 file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
984 file_name_template, number, number_pos + 1);
986 file_name = xstrdup (file_name_template);
988 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
989 cr = cairo_create (surface);
992 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
993 cairo_rectangle (cr, 0, 0, width, length);
997 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
999 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1001 status = cairo_surface_write_to_png (surface, file_name);
1002 if (status != CAIRO_STATUS_SUCCESS)
1003 error (0, 0, _("error writing output file \"%s\": %s"),
1004 file_name, cairo_status_to_string (status));
1007 cairo_surface_destroy (surface);
1012 struct xr_table_state
1014 struct xr_render_fsm fsm;
1015 struct table_item *table_item;
1016 struct render_break x_break;
1017 struct render_break y_break;
1022 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1024 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1028 struct render_page *y_slice;
1031 while (!render_break_has_next (&ts->y_break))
1033 struct render_page *x_slice;
1035 render_break_destroy (&ts->y_break);
1036 if (!render_break_has_next (&ts->x_break))
1039 x_slice = render_break_next (&ts->x_break, xr->width);
1040 render_break_init (&ts->y_break, x_slice, V);
1043 space = xr->length - xr->y;
1044 if (render_break_next_size (&ts->y_break) > space)
1050 y_slice = render_break_next (&ts->y_break, space);
1051 if (ts->caption_height)
1055 struct table_cell cell;
1056 int bb[TABLE_N_AXES][2];
1058 xr_init_caption_cell (table_item_get_caption (ts->table_item),
1061 bb[H][1] = xr->width;
1063 bb[V][1] = ts->caption_height;
1064 xr_draw_cell (xr, &cell, bb, bb);
1066 xr->y += ts->caption_height;
1067 ts->caption_height = 0;
1071 render_page_draw (y_slice);
1072 xr->y += render_page_get_size (y_slice, V);
1073 render_page_unref (y_slice);
1078 xr_table_destroy (struct xr_render_fsm *fsm)
1080 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1082 table_item_unref (ts->table_item);
1083 render_break_destroy (&ts->x_break);
1084 render_break_destroy (&ts->y_break);
1088 static struct xr_render_fsm *
1089 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1091 struct xr_table_state *ts;
1092 struct render_page *page;
1094 ts = xmalloc (sizeof *ts);
1095 ts->fsm.render = xr_table_render;
1096 ts->fsm.destroy = xr_table_destroy;
1097 ts->table_item = table_item_ref (table_item);
1100 xr->y += xr->font_height;
1102 page = xr_render_table_item (xr, table_item, &ts->caption_height);
1103 xr->params->size[V] = xr->length - ts->caption_height;
1105 render_break_init (&ts->x_break, page, H);
1106 render_break_init_empty (&ts->y_break);
1111 struct xr_chart_state
1113 struct xr_render_fsm fsm;
1114 struct chart_item *chart_item;
1118 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1120 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1125 if (xr->cairo != NULL)
1126 xr_draw_chart (cs->chart_item, xr->cairo, 0.0, 0.0,
1127 xr_to_pt (xr->width), xr_to_pt (xr->length));
1134 xr_chart_destroy (struct xr_render_fsm *fsm)
1136 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1138 chart_item_unref (cs->chart_item);
1142 static struct xr_render_fsm *
1143 xr_render_chart (const struct chart_item *chart_item)
1145 struct xr_chart_state *cs;
1147 cs = xmalloc (sizeof *cs);
1148 cs->fsm.render = xr_chart_render;
1149 cs->fsm.destroy = xr_chart_destroy;
1150 cs->chart_item = chart_item_ref (chart_item);
1156 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1162 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1164 /* Nothing to do. */
1167 static struct xr_render_fsm *
1168 xr_render_eject (void)
1170 static struct xr_render_fsm eject_renderer =
1176 return &eject_renderer;
1179 static struct xr_render_fsm *
1180 xr_create_text_renderer (struct xr_driver *xr, const char *text)
1182 struct table_item *table_item;
1183 struct xr_render_fsm *fsm;
1185 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
1186 fsm = xr_render_table (xr, table_item);
1187 table_item_unref (table_item);
1192 static struct xr_render_fsm *
1193 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1195 enum text_item_type type = text_item_get_type (text_item);
1196 const char *text = text_item_get_text (text_item);
1200 case TEXT_ITEM_TITLE:
1202 xr->title = xstrdup (text);
1205 case TEXT_ITEM_SUBTITLE:
1206 free (xr->subtitle);
1207 xr->subtitle = xstrdup (text);
1210 case TEXT_ITEM_COMMAND_CLOSE:
1213 case TEXT_ITEM_BLANK_LINE:
1215 xr->y += xr->font_height;
1218 case TEXT_ITEM_EJECT_PAGE:
1220 return xr_render_eject ();
1224 return xr_create_text_renderer (xr, text);
1230 static struct xr_render_fsm *
1231 xr_render_message (struct xr_driver *xr,
1232 const struct message_item *message_item)
1234 const struct msg *msg = message_item_get_msg (message_item);
1235 struct xr_render_fsm *fsm;
1238 s = msg_to_string (msg, xr->command_name);
1239 fsm = xr_create_text_renderer (xr, s);
1245 static struct xr_render_fsm *
1246 xr_render_output_item (struct xr_driver *xr,
1247 const struct output_item *output_item)
1249 if (is_table_item (output_item))
1250 return xr_render_table (xr, to_table_item (output_item));
1251 else if (is_chart_item (output_item))
1252 return xr_render_chart (to_chart_item (output_item));
1253 else if (is_text_item (output_item))
1254 return xr_render_text (xr, to_text_item (output_item));
1255 else if (is_message_item (output_item))
1256 return xr_render_message (xr, to_message_item (output_item));