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 bool headers; /* Draw headers at top of page? */
124 struct xr_font fonts[XR_N_FONTS];
125 int font_height; /* In XR units. */
127 int width; /* Page width minus margins. */
128 int length; /* Page length minus margins and header. */
130 int left_margin; /* Left margin in XR units. */
131 int right_margin; /* Right margin in XR units. */
132 int top_margin; /* Top margin in XR units. */
133 int bottom_margin; /* Bottom margin in XR units. */
135 int line_gutter; /* Space around lines. */
136 int line_space; /* Space between lines. */
137 int line_width; /* Width of lines. */
139 /* Internal state. */
140 struct render_params *params;
145 int page_number; /* Current page number. */
147 struct xr_render_fsm *fsm;
150 static const struct output_driver_class cairo_driver_class;
152 static void draw_headers (struct xr_driver *);
153 static void xr_driver_destroy_fsm (struct xr_driver *);
154 static void xr_driver_run_fsm (struct xr_driver *);
156 static bool load_font (struct xr_driver *, struct xr_font *);
157 static void free_font (struct xr_font *);
159 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
160 enum render_line_style styles[TABLE_N_AXES][2]);
161 static void xr_measure_cell_width (void *, const struct table_cell *,
163 static int xr_measure_cell_height (void *, const struct table_cell *,
165 static void xr_draw_cell (void *, const struct table_cell *,
166 int bb[TABLE_N_AXES][2],
167 int clip[TABLE_N_AXES][2]);
169 static struct xr_render_fsm *xr_render_output_item (
170 struct xr_driver *, const struct output_item *);
172 /* Output driver basics. */
174 static struct xr_driver *
175 xr_driver_cast (struct output_driver *driver)
177 assert (driver->class == &cairo_driver_class);
178 return UP_CAST (driver, struct xr_driver, driver);
181 static struct driver_option *
182 opt (struct output_driver *d, struct string_map *options, const char *key,
183 const char *default_value)
185 return driver_option_get (d, options, key, default_value);
188 static struct xr_driver *
189 xr_allocate (const char *name, int device_type, struct string_map *o)
191 struct output_driver *d;
192 struct xr_driver *xr;
194 xr = xzalloc (sizeof *xr);
196 output_driver_init (d, &cairo_driver_class, name, device_type);
198 xr->font_height = XR_POINT * 10;
199 xr->fonts[XR_FONT_FIXED].string
200 = parse_string (opt (d, o, "fixed-font", "monospace"));
201 xr->fonts[XR_FONT_PROPORTIONAL].string
202 = parse_string (opt (d, o, "prop-font", "serif"));
203 xr->fonts[XR_FONT_EMPHASIS].string
204 = parse_string (opt (d, o, "emph-font", "serif italic"));
205 xr->line_gutter = XR_POINT;
206 xr->line_space = XR_POINT;
207 xr->line_width = XR_POINT / 2;
214 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
220 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
222 for (i = 0; i < XR_N_FONTS; i++)
223 if (!load_font (xr, &xr->fonts[i]))
226 if (xr->params == NULL)
228 int single_width, double_width;
230 xr->params = xmalloc (sizeof *xr->params);
231 xr->params->draw_line = xr_draw_line;
232 xr->params->measure_cell_width = xr_measure_cell_width;
233 xr->params->measure_cell_height = xr_measure_cell_height;
234 xr->params->draw_cell = xr_draw_cell;
235 xr->params->aux = xr;
236 xr->params->size[H] = xr->width;
237 xr->params->size[V] = xr->length;
238 xr->params->font_size[H] = xr->font_height / 2; /* XXX */
239 xr->params->font_size[V] = xr->font_height;
241 single_width = 2 * xr->line_gutter + xr->line_width;
242 double_width = 2 * xr->line_gutter + xr->line_space + 2 * xr->line_width;
243 for (i = 0; i < TABLE_N_AXES; i++)
245 xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
246 xr->params->line_widths[i][RENDER_LINE_SINGLE] = single_width;
247 xr->params->line_widths[i][RENDER_LINE_DOUBLE] = double_width;
254 static struct output_driver *
255 xr_create (const char *file_name, enum settings_output_devices device_type,
256 struct string_map *o, enum xr_output_type file_type)
258 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
259 struct output_driver *d;
260 struct xr_driver *xr;
261 cairo_surface_t *surface;
262 cairo_status_t status;
263 double width_pt, length_pt;
264 int paper_width, paper_length;
266 xr = xr_allocate (file_name, device_type, o);
269 xr->headers = parse_boolean (opt (d, o, "headers", "true"));
271 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
272 xr->left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
273 xr->right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
274 xr->top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
275 xr->bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
278 xr->top_margin += 3 * xr->font_height;
279 xr->width = paper_width - xr->left_margin - xr->right_margin;
280 xr->length = paper_length - xr->top_margin - xr->bottom_margin;
282 width_pt = paper_width / 1000.0;
283 length_pt = paper_length / 1000.0;
284 if (file_type == XR_PDF)
285 surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
286 else if (file_type == XR_PS)
287 surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
288 else if (file_type == XR_SVG)
289 surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
293 status = cairo_surface_status (surface);
294 if (status != CAIRO_STATUS_SUCCESS)
296 error (0, 0, _("error opening output file \"%s\": %s"),
297 file_name, cairo_status_to_string (status));
298 cairo_surface_destroy (surface);
302 xr->cairo = cairo_create (surface);
303 cairo_surface_destroy (surface);
305 if (!xr_set_cairo (xr, xr->cairo))
308 cairo_save (xr->cairo);
309 xr_driver_next_page (xr, xr->cairo);
311 if (xr->width / (xr->font_height / 2) < MIN_WIDTH)
313 error (0, 0, _("The defined page is not wide enough to hold at least %d "
314 "characters in the default font. In fact, there's only "
315 "room for %d characters."),
317 xr->width / (xr->font_height / 2));
321 if (xr->length / xr->font_height < MIN_LENGTH)
323 error (0, 0, _("The defined page is not long "
324 "enough to hold margins and headers, plus least %d "
325 "lines of the default fonts. In fact, there's only "
326 "room for %d lines."),
328 xr->length / xr->font_height);
337 output_driver_destroy (&xr->driver);
341 static struct output_driver *
342 xr_pdf_create (const char *file_name, enum settings_output_devices device_type,
343 struct string_map *o)
345 return xr_create (file_name, device_type, o, XR_PDF);
348 static struct output_driver *
349 xr_ps_create (const char *file_name, enum settings_output_devices device_type,
350 struct string_map *o)
352 return xr_create (file_name, device_type, o, XR_PS);
355 static struct output_driver *
356 xr_svg_create (const char *file_name, enum settings_output_devices device_type,
357 struct string_map *o)
359 return xr_create (file_name, device_type, o, XR_SVG);
363 xr_destroy (struct output_driver *driver)
365 struct xr_driver *xr = xr_driver_cast (driver);
368 xr_driver_destroy_fsm (xr);
370 if (xr->cairo != NULL)
372 cairo_status_t status;
374 cairo_surface_finish (cairo_get_target (xr->cairo));
375 status = cairo_status (xr->cairo);
376 if (status != CAIRO_STATUS_SUCCESS)
377 error (0, 0, _("error drawing output for %s driver: %s"),
378 output_driver_get_name (driver),
379 cairo_status_to_string (status));
380 cairo_destroy (xr->cairo);
383 free (xr->command_name);
384 for (i = 0; i < XR_N_FONTS; i++)
385 free_font (&xr->fonts[i]);
391 xr_flush (struct output_driver *driver)
393 struct xr_driver *xr = xr_driver_cast (driver);
395 cairo_surface_flush (cairo_get_target (xr->cairo));
399 xr_init_caption_cell (const char *caption, struct table_cell *cell)
401 cell->contents = caption;
402 cell->options = TAB_LEFT;
403 cell->destructor = NULL;
406 static struct render_page *
407 xr_render_table_item (struct xr_driver *xr, const struct table_item *item,
408 int *caption_heightp)
410 const char *caption = table_item_get_caption (item);
414 /* XXX doesn't do well with very large captions */
415 struct table_cell cell;
416 xr_init_caption_cell (caption, &cell);
417 *caption_heightp = xr_measure_cell_height (xr, &cell, xr->width);
420 *caption_heightp = 0;
422 return render_page_create (xr->params, table_item_get_table (item));
426 xr_submit (struct output_driver *driver, const struct output_item *output_item)
428 struct xr_driver *xr = xr_driver_cast (driver);
430 xr_driver_output_item (xr, output_item);
431 while (xr_driver_need_new_page (xr))
433 cairo_restore (xr->cairo);
434 cairo_show_page (xr->cairo);
435 cairo_save (xr->cairo);
436 xr_driver_next_page (xr, xr->cairo);
440 /* Functions for rendering a series of output items to a series of Cairo
441 contexts, with pagination, possibly including headers.
443 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
444 its underlying implementation.
446 See the big comment in cairo.h for intended usage. */
448 /* Gives new page CAIRO to XR for output. CAIRO may be null to skip actually
449 rendering the page (which might be useful to find out how many pages an
450 output document has without actually rendering it). */
452 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
455 cairo_translate (cairo,
456 xr_to_pt (xr->left_margin),
457 xr_to_pt (xr->top_margin));
463 xr_driver_run_fsm (xr);
466 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
467 rendering a previous output item, that is, only if xr_driver_need_new_page()
470 xr_driver_output_item (struct xr_driver *xr,
471 const struct output_item *output_item)
473 assert (xr->fsm == NULL);
474 xr->fsm = xr_render_output_item (xr, output_item);
475 xr_driver_run_fsm (xr);
478 /* Returns true if XR is in the middle of rendering an output item and needs a
479 new page to be appended using xr_driver_next_page() to make progress,
482 xr_driver_need_new_page (const struct xr_driver *xr)
484 return xr->fsm != NULL;
487 /* Returns true if the current page doesn't have any content yet (besides
488 headers, if enabled). */
490 xr_driver_is_page_blank (const struct xr_driver *xr)
496 xr_driver_destroy_fsm (struct xr_driver *xr)
500 xr->fsm->destroy (xr->fsm);
506 xr_driver_run_fsm (struct xr_driver *xr)
508 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
509 xr_driver_destroy_fsm (xr);
513 xr_layout_cell (struct xr_driver *, const struct table_cell *,
514 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
515 PangoWrapMode, int *width, int *height);
518 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
520 cairo_new_path (xr->cairo);
521 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
522 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
523 cairo_stroke (xr->cairo);
526 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
527 shortening it to X0...X1 if SHORTEN is true.
528 Draws a horizontal line X1...X3 at Y if RIGHT says so,
529 shortening it to X2...X3 if SHORTEN is true. */
531 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
532 enum render_line_style left, enum render_line_style right,
535 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
536 dump_line (xr, x0, y, x3, y);
539 if (left != RENDER_LINE_NONE)
540 dump_line (xr, x0, y, shorten ? x1 : x2, y);
541 if (right != RENDER_LINE_NONE)
542 dump_line (xr, shorten ? x2 : x1, y, x3, y);
546 /* Draws a vertical line Y0...Y2 at X if TOP says so,
547 shortening it to Y0...Y1 if SHORTEN is true.
548 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
549 shortening it to Y2...Y3 if SHORTEN is true. */
551 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
552 enum render_line_style top, enum render_line_style bottom,
555 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
556 dump_line (xr, x, y0, x, y3);
559 if (top != RENDER_LINE_NONE)
560 dump_line (xr, x, y0, x, shorten ? y1 : y2);
561 if (bottom != RENDER_LINE_NONE)
562 dump_line (xr, x, shorten ? y2 : y1, x, y3);
567 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
568 enum render_line_style styles[TABLE_N_AXES][2])
570 const int x0 = bb[H][0];
571 const int y0 = bb[V][0];
572 const int x3 = bb[H][1];
573 const int y3 = bb[V][1];
574 const int top = styles[H][0];
575 const int left = styles[V][0];
576 const int bottom = styles[H][1];
577 const int right = styles[V][1];
579 /* The algorithm here is somewhat subtle, to allow it to handle
580 all the kinds of intersections that we need.
582 Three additional ordinates are assigned along the x axis. The
583 first is xc, midway between x0 and x3. The others are x1 and
584 x2; for a single vertical line these are equal to xc, and for
585 a double vertical line they are the ordinates of the left and
586 right half of the double line.
588 yc, y1, and y2 are assigned similarly along the y axis.
590 The following diagram shows the coordinate system and output
591 for double top and bottom lines, single left line, and no
595 y0 ________________________
601 y1 = y2 = yc |######### # |
606 y3 |________#_____#_______|
608 struct xr_driver *xr = xr_;
610 /* Offset from center of each line in a pair of double lines. */
611 int double_line_ofs = (xr->line_space + xr->line_width) / 2;
613 /* Are the lines along each axis single or double?
614 (It doesn't make sense to have different kinds of line on the
615 same axis, so we don't try to gracefully handle that case.) */
616 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
617 bool double_horz = left == RENDER_LINE_DOUBLE || right == RENDER_LINE_DOUBLE;
619 /* When horizontal lines are doubled,
620 the left-side line along y1 normally runs from x0 to x2,
621 and the right-side line along y1 from x3 to x1.
622 If the top-side line is also doubled, we shorten the y1 lines,
623 so that the left-side line runs only to x1,
624 and the right-side line only to x2.
625 Otherwise, the horizontal line at y = y1 below would cut off
626 the intersection, which looks ugly:
628 y0 ________________________
633 y1 |######### ########|
636 y2 |######################|
639 y3 |______________________|
640 It is more of a judgment call when the horizontal line is
641 single. We actually choose to cut off the line anyhow, as
642 shown in the first diagram above.
644 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
645 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
646 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
647 int horz_line_ofs = double_vert ? double_line_ofs : 0;
648 int xc = (x0 + x3) / 2;
649 int x1 = xc - horz_line_ofs;
650 int x2 = xc + horz_line_ofs;
652 bool shorten_x1_lines = left == RENDER_LINE_DOUBLE;
653 bool shorten_x2_lines = right == RENDER_LINE_DOUBLE;
654 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
655 int vert_line_ofs = double_horz ? double_line_ofs : 0;
656 int yc = (y0 + y3) / 2;
657 int y1 = yc - vert_line_ofs;
658 int y2 = yc + vert_line_ofs;
661 horz_line (xr, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
664 horz_line (xr, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
665 horz_line (xr, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
669 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
672 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
673 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
678 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
679 int *min_width, int *max_width)
681 struct xr_driver *xr = xr_;
682 int bb[TABLE_N_AXES][2];
683 int clip[TABLE_N_AXES][2];
690 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
691 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, max_width, &h);
694 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, min_width, &h);
698 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
700 struct xr_driver *xr = xr_;
701 int bb[TABLE_N_AXES][2];
702 int clip[TABLE_N_AXES][2];
709 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
710 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
715 xr_draw_cell (void *xr_, const struct table_cell *cell,
716 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
718 struct xr_driver *xr = xr_;
721 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
724 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
725 and with the given cell OPTIONS for XR. */
727 draw_text (struct xr_driver *xr, const char *string, int x, int y,
728 int max_width, unsigned int options)
730 struct table_cell cell;
731 int bb[TABLE_N_AXES][2];
734 cell.contents = string;
735 cell.options = options;
737 bb[V][0] = y - xr->y;
738 bb[H][1] = x + max_width;
739 bb[V][1] = xr->font_height - xr->y;
740 xr_layout_cell (xr, &cell, bb, bb, PANGO_WRAP_WORD_CHAR, &w, &h);
744 /* Writes LEFT left-justified and RIGHT right-justified within
745 (X0...X1) at Y. LEFT or RIGHT or both may be null. */
747 draw_header_line (struct xr_driver *xr, const char *left, const char *right,
748 int x0, int x1, int y)
752 right_width = (draw_text (xr, right, x0, y, x1 - x0, TAB_RIGHT)
753 + xr->font_height / 2);
755 draw_text (xr, left, x0, y, x1 - x0 - right_width, TAB_LEFT);
758 /* Draw top of page headers for XR. */
760 draw_headers (struct xr_driver *xr)
766 if (!xr->headers || xr->cairo == NULL)
769 y = -3 * xr->font_height;
770 x0 = xr->font_height / 2;
771 x1 = xr->width - xr->font_height / 2;
774 cairo_rectangle (xr->cairo, 0, xr_to_pt (y), xr_to_pt (xr->width),
775 xr_to_pt (2 * (xr->font_height
776 + xr->line_width + xr->line_gutter)));
777 cairo_save (xr->cairo);
778 cairo_set_source_rgb (xr->cairo, 0.9, 0.9, 0.9);
779 cairo_fill_preserve (xr->cairo);
780 cairo_restore (xr->cairo);
781 cairo_stroke (xr->cairo);
783 y += xr->line_width + xr->line_gutter;
785 r1 = xasprintf (_("%s - Page %d"), get_start_date (), xr->page_number);
786 r2 = xasprintf ("%s - %s", version, host_system);
788 draw_header_line (xr, xr->title, r1, x0, x1, y);
789 y += xr->font_height;
791 draw_header_line (xr, xr->subtitle, r2, x0, x1, y);
798 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
799 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
800 PangoWrapMode wrap, int *width, int *height)
802 struct xr_font *font;
804 font = (cell->options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
805 : cell->options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
806 : &xr->fonts[XR_FONT_PROPORTIONAL]);
808 pango_layout_set_text (font->layout, cell->contents, -1);
810 pango_layout_set_alignment (
812 ((cell->options & TAB_ALIGNMENT) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
813 : (cell->options & TAB_ALIGNMENT) == TAB_LEFT ? PANGO_ALIGN_LEFT
814 : PANGO_ALIGN_CENTER));
815 pango_layout_set_width (font->layout,
816 bb[H][1] == INT_MAX ? -1 : bb[H][1] - bb[H][0]);
817 pango_layout_set_wrap (font->layout, wrap);
819 if (clip[H][0] != clip[H][1])
821 cairo_save (xr->cairo);
823 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
825 double x0 = xr_to_pt (clip[H][0]);
826 double y0 = xr_to_pt (clip[V][0] + xr->y);
827 double x1 = xr_to_pt (clip[H][1]);
828 double y1 = xr_to_pt (clip[V][1] + xr->y);
830 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
831 cairo_clip (xr->cairo);
834 cairo_translate (xr->cairo,
836 xr_to_pt (bb[V][0] + xr->y));
837 pango_cairo_show_layout (xr->cairo, font->layout);
838 cairo_restore (xr->cairo);
841 if (width != NULL || height != NULL)
845 pango_layout_get_size (font->layout, &w, &h);
853 /* Attempts to load FONT, initializing its other members based on
854 its 'string' member and the information in DRIVER. Returns true
855 if successful, otherwise false. */
857 load_font (struct xr_driver *xr, struct xr_font *font)
859 PangoContext *context;
860 PangoLanguage *language;
862 font->desc = pango_font_description_from_string (font->string);
863 if (font->desc == NULL)
865 error (0, 0, _("\"%s\": bad font specification"), font->string);
868 pango_font_description_set_absolute_size (font->desc, xr->font_height);
870 font->layout = pango_cairo_create_layout (xr->cairo);
871 pango_layout_set_font_description (font->layout, font->desc);
873 language = pango_language_get_default ();
874 context = pango_layout_get_context (font->layout);
875 font->metrics = pango_context_get_metrics (context, font->desc, language);
882 free_font (struct xr_font *font)
885 if (font->desc != NULL)
886 pango_font_description_free (font->desc);
887 pango_font_metrics_unref (font->metrics);
888 if (font->layout != NULL)
889 g_object_unref (font->layout);
892 struct output_driver_factory pdf_driver_factory = { "pdf", xr_pdf_create };
893 struct output_driver_factory ps_driver_factory = { "ps", xr_ps_create };
894 struct output_driver_factory svg_driver_factory = { "svg", xr_svg_create };
896 static const struct output_driver_class cairo_driver_class =
904 /* GUI rendering helpers. */
909 struct render_page *page;
910 struct xr_driver *xr;
914 struct chart_item *chart;
917 #define CHART_WIDTH 500
918 #define CHART_HEIGHT 375
921 xr_driver_create (cairo_t *cairo, struct string_map *options)
923 struct xr_driver *xr = xr_allocate ("cairo", 0, options);
924 xr->width = INT_MAX / 8;
925 xr->length = INT_MAX / 8;
926 if (!xr_set_cairo (xr, cairo))
928 output_driver_destroy (&xr->driver);
934 static struct xr_rendering *
935 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
937 struct table_item *table_item;
938 struct xr_rendering *r;
940 table_item = table_item_create (table_from_string (0, text), NULL);
941 r = xr_rendering_create (xr, &table_item->output_item, cr);
942 table_item_unref (table_item);
947 struct xr_rendering *
948 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
951 struct xr_rendering *r = NULL;
953 if (is_text_item (item))
954 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
956 else if (is_message_item (item))
958 const struct message_item *message_item = to_message_item (item);
959 const struct msg *msg = message_item_get_msg (message_item);
960 char *s = msg_to_string (msg, NULL);
961 r = xr_rendering_create_text (xr, s, cr);
964 else if (is_table_item (item))
966 r = xzalloc (sizeof *r);
968 xr_set_cairo (xr, cr);
969 r->page = xr_render_table_item (xr, to_table_item (item),
972 else if (is_chart_item (item))
974 r = xzalloc (sizeof *r);
975 r->chart = to_chart_item (output_item_ref (item));
982 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
984 if (r->chart == NULL)
986 *w = render_page_get_size (r->page, H) / 1024;
987 *h = (render_page_get_size (r->page, V) + r->title_height) / 1024;
997 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr)
999 if (r->chart == NULL)
1001 struct xr_driver *xr = r->xr;
1003 xr_set_cairo (xr, cr);
1005 render_page_draw (r->page);
1008 xr_draw_chart (r->chart, cr, 0, 0, CHART_WIDTH, CHART_HEIGHT);
1012 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1013 double x, double y, double width, double height)
1015 struct xrchart_geometry geom;
1018 cairo_translate (cr, x, y + height);
1019 cairo_scale (cr, 1.0, -1.0);
1020 xrchart_geometry_init (cr, &geom, width, height);
1021 if (is_boxplot (chart_item))
1022 xrchart_draw_boxplot (chart_item, cr, &geom);
1023 else if (is_histogram_chart (chart_item))
1024 xrchart_draw_histogram (chart_item, cr, &geom);
1025 else if (is_np_plot_chart (chart_item))
1026 xrchart_draw_np_plot (chart_item, cr, &geom);
1027 else if (is_piechart (chart_item))
1028 xrchart_draw_piechart (chart_item, cr, &geom);
1029 else if (is_roc_chart (chart_item))
1030 xrchart_draw_roc (chart_item, cr, &geom);
1031 else if (is_scree (chart_item))
1032 xrchart_draw_scree (chart_item, cr, &geom);
1035 xrchart_geometry_free (cr, &geom);
1041 xr_draw_png_chart (const struct chart_item *item,
1042 const char *file_name_template, int number)
1044 const int width = 640;
1045 const int length = 480;
1047 cairo_surface_t *surface;
1048 cairo_status_t status;
1049 const char *number_pos;
1053 number_pos = strchr (file_name_template, '#');
1054 if (number_pos != NULL)
1055 file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1056 file_name_template, number, number_pos + 1);
1058 file_name = xstrdup (file_name_template);
1060 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1061 cr = cairo_create (surface);
1064 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
1065 cairo_rectangle (cr, 0, 0, width, length);
1069 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
1071 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1073 status = cairo_surface_write_to_png (surface, file_name);
1074 if (status != CAIRO_STATUS_SUCCESS)
1075 error (0, 0, _("error writing output file \"%s\": %s"),
1076 file_name, cairo_status_to_string (status));
1079 cairo_surface_destroy (surface);
1084 struct xr_table_state
1086 struct xr_render_fsm fsm;
1087 struct table_item *table_item;
1088 struct render_break x_break;
1089 struct render_break y_break;
1094 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1096 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1100 struct render_page *y_slice;
1103 while (!render_break_has_next (&ts->y_break))
1105 struct render_page *x_slice;
1107 render_break_destroy (&ts->y_break);
1108 if (!render_break_has_next (&ts->x_break))
1111 x_slice = render_break_next (&ts->x_break, xr->width);
1112 render_break_init (&ts->y_break, x_slice, V);
1115 space = xr->length - xr->y;
1116 if (render_break_next_size (&ts->y_break) > space)
1122 y_slice = render_break_next (&ts->y_break, space);
1123 if (ts->caption_height)
1127 struct table_cell cell;
1128 int bb[TABLE_N_AXES][2];
1130 xr_init_caption_cell (table_item_get_caption (ts->table_item),
1133 bb[H][1] = xr->width;
1135 bb[V][1] = ts->caption_height;
1136 xr_draw_cell (xr, &cell, bb, bb);
1138 xr->y += ts->caption_height;
1139 ts->caption_height = 0;
1143 render_page_draw (y_slice);
1144 xr->y += render_page_get_size (y_slice, V);
1145 render_page_unref (y_slice);
1150 xr_table_destroy (struct xr_render_fsm *fsm)
1152 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1154 table_item_unref (ts->table_item);
1155 render_break_destroy (&ts->x_break);
1156 render_break_destroy (&ts->y_break);
1160 static struct xr_render_fsm *
1161 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1163 struct xr_table_state *ts;
1164 struct render_page *page;
1166 ts = xmalloc (sizeof *ts);
1167 ts->fsm.render = xr_table_render;
1168 ts->fsm.destroy = xr_table_destroy;
1169 ts->table_item = table_item_ref (table_item);
1172 xr->y += xr->font_height;
1174 page = xr_render_table_item (xr, table_item, &ts->caption_height);
1175 xr->params->size[V] = xr->length - ts->caption_height;
1177 render_break_init (&ts->x_break, page, H);
1178 render_break_init_empty (&ts->y_break);
1183 struct xr_chart_state
1185 struct xr_render_fsm fsm;
1186 struct chart_item *chart_item;
1190 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1192 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1197 if (xr->cairo != NULL)
1198 xr_draw_chart (cs->chart_item, xr->cairo, 0.0, 0.0,
1199 xr_to_pt (xr->width), xr_to_pt (xr->length));
1206 xr_chart_destroy (struct xr_render_fsm *fsm)
1208 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1210 chart_item_unref (cs->chart_item);
1214 static struct xr_render_fsm *
1215 xr_render_chart (const struct chart_item *chart_item)
1217 struct xr_chart_state *cs;
1219 cs = xmalloc (sizeof *cs);
1220 cs->fsm.render = xr_chart_render;
1221 cs->fsm.destroy = xr_chart_destroy;
1222 cs->chart_item = chart_item_ref (chart_item);
1228 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1234 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1236 /* Nothing to do. */
1239 static struct xr_render_fsm *
1240 xr_render_eject (void)
1242 static struct xr_render_fsm eject_renderer =
1248 return &eject_renderer;
1251 static struct xr_render_fsm *
1252 xr_create_text_renderer (struct xr_driver *xr, const char *text)
1254 struct table_item *table_item;
1255 struct xr_render_fsm *fsm;
1257 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
1258 fsm = xr_render_table (xr, table_item);
1259 table_item_unref (table_item);
1264 static struct xr_render_fsm *
1265 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1267 enum text_item_type type = text_item_get_type (text_item);
1268 const char *text = text_item_get_text (text_item);
1272 case TEXT_ITEM_TITLE:
1274 xr->title = xstrdup (text);
1278 case TEXT_ITEM_SUBTITLE:
1279 free (xr->subtitle);
1280 xr->subtitle = xstrdup (text);
1284 case TEXT_ITEM_COMMAND_CLOSE:
1287 case TEXT_ITEM_BLANK_LINE:
1289 xr->y += xr->font_height;
1292 case TEXT_ITEM_EJECT_PAGE:
1294 return xr_render_eject ();
1298 return xr_create_text_renderer (xr, text);
1304 static struct xr_render_fsm *
1305 xr_render_message (struct xr_driver *xr,
1306 const struct message_item *message_item)
1308 const struct msg *msg = message_item_get_msg (message_item);
1309 struct xr_render_fsm *fsm;
1312 s = msg_to_string (msg, xr->command_name);
1313 fsm = xr_create_text_renderer (xr, s);
1319 static struct xr_render_fsm *
1320 xr_render_output_item (struct xr_driver *xr,
1321 const struct output_item *output_item)
1323 if (is_table_item (output_item))
1324 return xr_render_table (xr, to_table_item (output_item));
1325 else if (is_chart_item (output_item))
1326 return xr_render_chart (to_chart_item (output_item));
1327 else if (is_text_item (output_item))
1328 return xr_render_text (xr, to_text_item (output_item));
1329 else if (is_message_item (output_item))
1330 return xr_render_message (xr, to_message_item (output_item));