1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 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/hash-functions.h"
24 #include "libpspp/message.h"
25 #include "libpspp/pool.h"
26 #include "libpspp/start-date.h"
27 #include "libpspp/str.h"
28 #include "libpspp/string-map.h"
29 #include "libpspp/version.h"
30 #include "data/file-handle-def.h"
31 #include "output/cairo-chart.h"
32 #include "output/chart-item-provider.h"
33 #include "output/charts/boxplot.h"
34 #include "output/charts/np-plot.h"
35 #include "output/charts/piechart.h"
36 #include "output/charts/barchart.h"
37 #include "output/charts/plot-hist.h"
38 #include "output/charts/roc-chart.h"
39 #include "output/charts/spreadlevel-plot.h"
40 #include "output/charts/scree.h"
41 #include "output/charts/scatterplot.h"
42 #include "output/driver-provider.h"
43 #include "output/group-item.h"
44 #include "output/message-item.h"
45 #include "output/options.h"
46 #include "output/page-setup-item.h"
47 #include "output/render.h"
48 #include "output/table-item.h"
49 #include "output/table.h"
50 #include "output/text-item.h"
52 #include <cairo/cairo-pdf.h>
53 #include <cairo/cairo-ps.h>
54 #include <cairo/cairo-svg.h>
55 #include <cairo/cairo.h>
58 #include <pango/pango-font.h>
59 #include <pango/pango-layout.h>
60 #include <pango/pango.h>
61 #include <pango/pangocairo.h>
64 #include "gl/c-ctype.h"
65 #include "gl/c-strcase.h"
66 #include "gl/intprops.h"
67 #include "gl/minmax.h"
68 #include "gl/xalloc.h"
71 #define _(msgid) gettext (msgid)
73 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
77 /* The unit used for internal measurements is inch/(72 * XR_POINT).
78 (Thus, XR_POINT units represent one point.) */
79 #define XR_POINT PANGO_SCALE
81 /* Conversions to and from points. */
85 return x / (double) XR_POINT;
88 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
92 return x * (PANGO_SCALE * 72 / 96);
95 /* Dimensions for drawing lines in tables. */
96 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
97 #define XR_LINE_SPACE XR_POINT /* Space between double lines. */
110 XR_FONT_PROPORTIONAL,
116 /* A font for use with Cairo. */
119 PangoFontDescription *desc;
123 /* An output item whose rendering is in progress. */
126 /* Renders as much of itself as it can on the current page. Returns true
127 if rendering is complete, false if the output item needs another
129 bool (*render) (struct xr_render_fsm *, struct xr_driver *);
131 /* Destroys the output item. */
132 void (*destroy) (struct xr_render_fsm *);
135 /* Cairo output driver. */
138 struct output_driver driver;
140 /* User parameters. */
141 struct xr_font fonts[XR_N_FONTS];
143 int width; /* Page width minus margins. */
144 int length; /* Page length minus margins and header. */
146 int left_margin; /* Left margin in inch/(72 * XR_POINT). */
147 int right_margin; /* Right margin in inch/(72 * XR_POINT). */
148 int top_margin; /* Top margin in inch/(72 * XR_POINT). */
149 int bottom_margin; /* Bottom margin in inch/(72 * XR_POINT). */
151 int line_space; /* Space between lines. */
152 int line_width; /* Width of lines. */
154 int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
155 int object_spacing; /* Space between output objects. */
157 struct cell_color bg; /* Background color */
158 struct cell_color fg; /* Foreground color */
159 bool transparent; /* true -> do not render background */
160 bool systemcolors; /* true -> do not change colors */
162 int initial_page_number;
164 struct page_heading headings[2]; /* Top and bottom headings. */
165 int headings_height[2];
167 /* Internal state. */
168 struct render_params *params;
170 int char_width, char_height;
175 cairo_surface_t *surface;
176 int page_number; /* Current page number. */
178 struct xr_render_fsm *fsm;
180 struct string_map heading_vars;
183 static const struct output_driver_class cairo_driver_class;
185 static void xr_driver_destroy_fsm (struct xr_driver *);
186 static void xr_driver_run_fsm (struct xr_driver *);
188 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
189 enum render_line_style styles[TABLE_N_AXES][2],
190 struct cell_color colors[TABLE_N_AXES][2]);
191 static void xr_measure_cell_width (void *, const struct table_cell *,
193 static int xr_measure_cell_height (void *, const struct table_cell *,
195 static void xr_draw_cell (void *, const struct table_cell *, int color_idx,
196 int bb[TABLE_N_AXES][2], int valign_offset,
197 int spill[TABLE_N_AXES][2],
198 int clip[TABLE_N_AXES][2]);
199 static int xr_adjust_break (void *, const struct table_cell *,
200 int width, int height);
202 static struct xr_render_fsm *xr_render_output_item (
203 struct xr_driver *, const struct output_item *);
205 /* Output driver basics. */
207 static struct xr_driver *
208 xr_driver_cast (struct output_driver *driver)
210 assert (driver->class == &cairo_driver_class);
211 return UP_CAST (driver, struct xr_driver, driver);
214 static struct driver_option *
215 opt (struct output_driver *d, struct string_map *options, const char *key,
216 const char *default_value)
218 return driver_option_get (d, options, key, default_value);
222 lookup_color_name (const char *s)
226 struct hmap_node hmap_node;
231 static struct color colors[] =
233 { .name = "aliceblue", .code = 0xf0f8ff },
234 { .name = "antiquewhite", .code = 0xfaebd7 },
235 { .name = "aqua", .code = 0x00ffff },
236 { .name = "aquamarine", .code = 0x7fffd4 },
237 { .name = "azure", .code = 0xf0ffff },
238 { .name = "beige", .code = 0xf5f5dc },
239 { .name = "bisque", .code = 0xffe4c4 },
240 { .name = "black", .code = 0x000000 },
241 { .name = "blanchedalmond", .code = 0xffebcd },
242 { .name = "blue", .code = 0x0000ff },
243 { .name = "blueviolet", .code = 0x8a2be2 },
244 { .name = "brown", .code = 0xa52a2a },
245 { .name = "burlywood", .code = 0xdeb887 },
246 { .name = "cadetblue", .code = 0x5f9ea0 },
247 { .name = "chartreuse", .code = 0x7fff00 },
248 { .name = "chocolate", .code = 0xd2691e },
249 { .name = "coral", .code = 0xff7f50 },
250 { .name = "cornflowerblue", .code = 0x6495ed },
251 { .name = "cornsilk", .code = 0xfff8dc },
252 { .name = "crimson", .code = 0xdc143c },
253 { .name = "cyan", .code = 0x00ffff },
254 { .name = "darkblue", .code = 0x00008b },
255 { .name = "darkcyan", .code = 0x008b8b },
256 { .name = "darkgoldenrod", .code = 0xb8860b },
257 { .name = "darkgray", .code = 0xa9a9a9 },
258 { .name = "darkgreen", .code = 0x006400 },
259 { .name = "darkgrey", .code = 0xa9a9a9 },
260 { .name = "darkkhaki", .code = 0xbdb76b },
261 { .name = "darkmagenta", .code = 0x8b008b },
262 { .name = "darkolivegreen", .code = 0x556b2f },
263 { .name = "darkorange", .code = 0xff8c00 },
264 { .name = "darkorchid", .code = 0x9932cc },
265 { .name = "darkred", .code = 0x8b0000 },
266 { .name = "darksalmon", .code = 0xe9967a },
267 { .name = "darkseagreen", .code = 0x8fbc8f },
268 { .name = "darkslateblue", .code = 0x483d8b },
269 { .name = "darkslategray", .code = 0x2f4f4f },
270 { .name = "darkslategrey", .code = 0x2f4f4f },
271 { .name = "darkturquoise", .code = 0x00ced1 },
272 { .name = "darkviolet", .code = 0x9400d3 },
273 { .name = "deeppink", .code = 0xff1493 },
274 { .name = "deepskyblue", .code = 0x00bfff },
275 { .name = "dimgray", .code = 0x696969 },
276 { .name = "dimgrey", .code = 0x696969 },
277 { .name = "dodgerblue", .code = 0x1e90ff },
278 { .name = "firebrick", .code = 0xb22222 },
279 { .name = "floralwhite", .code = 0xfffaf0 },
280 { .name = "forestgreen", .code = 0x228b22 },
281 { .name = "fuchsia", .code = 0xff00ff },
282 { .name = "gainsboro", .code = 0xdcdcdc },
283 { .name = "ghostwhite", .code = 0xf8f8ff },
284 { .name = "gold", .code = 0xffd700 },
285 { .name = "goldenrod", .code = 0xdaa520 },
286 { .name = "gray", .code = 0x808080 },
287 { .name = "green", .code = 0x008000 },
288 { .name = "greenyellow", .code = 0xadff2f },
289 { .name = "grey", .code = 0x808080 },
290 { .name = "honeydew", .code = 0xf0fff0 },
291 { .name = "hotpink", .code = 0xff69b4 },
292 { .name = "indianred", .code = 0xcd5c5c },
293 { .name = "indigo", .code = 0x4b0082 },
294 { .name = "ivory", .code = 0xfffff0 },
295 { .name = "khaki", .code = 0xf0e68c },
296 { .name = "lavender", .code = 0xe6e6fa },
297 { .name = "lavenderblush", .code = 0xfff0f5 },
298 { .name = "lawngreen", .code = 0x7cfc00 },
299 { .name = "lemonchiffon", .code = 0xfffacd },
300 { .name = "lightblue", .code = 0xadd8e6 },
301 { .name = "lightcoral", .code = 0xf08080 },
302 { .name = "lightcyan", .code = 0xe0ffff },
303 { .name = "lightgoldenrodyellow", .code = 0xfafad2 },
304 { .name = "lightgray", .code = 0xd3d3d3 },
305 { .name = "lightgreen", .code = 0x90ee90 },
306 { .name = "lightgrey", .code = 0xd3d3d3 },
307 { .name = "lightpink", .code = 0xffb6c1 },
308 { .name = "lightsalmon", .code = 0xffa07a },
309 { .name = "lightseagreen", .code = 0x20b2aa },
310 { .name = "lightskyblue", .code = 0x87cefa },
311 { .name = "lightslategray", .code = 0x778899 },
312 { .name = "lightslategrey", .code = 0x778899 },
313 { .name = "lightsteelblue", .code = 0xb0c4de },
314 { .name = "lightyellow", .code = 0xffffe0 },
315 { .name = "lime", .code = 0x00ff00 },
316 { .name = "limegreen", .code = 0x32cd32 },
317 { .name = "linen", .code = 0xfaf0e6 },
318 { .name = "magenta", .code = 0xff00ff },
319 { .name = "maroon", .code = 0x800000 },
320 { .name = "mediumaquamarine", .code = 0x66cdaa },
321 { .name = "mediumblue", .code = 0x0000cd },
322 { .name = "mediumorchid", .code = 0xba55d3 },
323 { .name = "mediumpurple", .code = 0x9370db },
324 { .name = "mediumseagreen", .code = 0x3cb371 },
325 { .name = "mediumslateblue", .code = 0x7b68ee },
326 { .name = "mediumspringgreen", .code = 0x00fa9a },
327 { .name = "mediumturquoise", .code = 0x48d1cc },
328 { .name = "mediumvioletred", .code = 0xc71585 },
329 { .name = "midnightblue", .code = 0x191970 },
330 { .name = "mintcream", .code = 0xf5fffa },
331 { .name = "mistyrose", .code = 0xffe4e1 },
332 { .name = "moccasin", .code = 0xffe4b5 },
333 { .name = "navajowhite", .code = 0xffdead },
334 { .name = "navy", .code = 0x000080 },
335 { .name = "oldlace", .code = 0xfdf5e6 },
336 { .name = "olive", .code = 0x808000 },
337 { .name = "olivedrab", .code = 0x6b8e23 },
338 { .name = "orange", .code = 0xffa500 },
339 { .name = "orangered", .code = 0xff4500 },
340 { .name = "orchid", .code = 0xda70d6 },
341 { .name = "palegoldenrod", .code = 0xeee8aa },
342 { .name = "palegreen", .code = 0x98fb98 },
343 { .name = "paleturquoise", .code = 0xafeeee },
344 { .name = "palevioletred", .code = 0xdb7093 },
345 { .name = "papayawhip", .code = 0xffefd5 },
346 { .name = "peachpuff", .code = 0xffdab9 },
347 { .name = "peru", .code = 0xcd853f },
348 { .name = "pink", .code = 0xffc0cb },
349 { .name = "plum", .code = 0xdda0dd },
350 { .name = "powderblue", .code = 0xb0e0e6 },
351 { .name = "purple", .code = 0x800080 },
352 { .name = "red", .code = 0xff0000 },
353 { .name = "rosybrown", .code = 0xbc8f8f },
354 { .name = "royalblue", .code = 0x4169e1 },
355 { .name = "saddlebrown", .code = 0x8b4513 },
356 { .name = "salmon", .code = 0xfa8072 },
357 { .name = "sandybrown", .code = 0xf4a460 },
358 { .name = "seagreen", .code = 0x2e8b57 },
359 { .name = "seashell", .code = 0xfff5ee },
360 { .name = "sienna", .code = 0xa0522d },
361 { .name = "silver", .code = 0xc0c0c0 },
362 { .name = "skyblue", .code = 0x87ceeb },
363 { .name = "slateblue", .code = 0x6a5acd },
364 { .name = "slategray", .code = 0x708090 },
365 { .name = "slategrey", .code = 0x708090 },
366 { .name = "snow", .code = 0xfffafa },
367 { .name = "springgreen", .code = 0x00ff7f },
368 { .name = "steelblue", .code = 0x4682b4 },
369 { .name = "tan", .code = 0xd2b48c },
370 { .name = "teal", .code = 0x008080 },
371 { .name = "thistle", .code = 0xd8bfd8 },
372 { .name = "tomato", .code = 0xff6347 },
373 { .name = "turquoise", .code = 0x40e0d0 },
374 { .name = "violet", .code = 0xee82ee },
375 { .name = "wheat", .code = 0xf5deb3 },
376 { .name = "white", .code = 0xffffff },
377 { .name = "whitesmoke", .code = 0xf5f5f5 },
378 { .name = "yellow", .code = 0xffff00 },
379 { .name = "yellowgreen", .code = 0x9acd32 },
382 static struct hmap color_table = HMAP_INITIALIZER (color_table);
384 if (hmap_is_empty (&color_table))
385 for (size_t i = 0; i < sizeof colors / sizeof *colors; i++)
386 hmap_insert (&color_table, &colors[i].hmap_node,
387 hash_string (colors[i].name, 0));
389 const struct color *color;
390 HMAP_FOR_EACH_WITH_HASH (color, struct color, hmap_node,
391 hash_string (s, 0), &color_table)
392 if (!strcmp (color->name, s))
398 parse_color__ (const char *s, struct cell_color *color)
401 uint16_t r16, g16, b16;
403 if (sscanf (s, "#%4"SCNx16"%4"SCNx16"%4"SCNx16"%n",
404 &r16, &g16, &b16, &len) == 3
416 if (sscanf (s, "#%2"SCNx8"%2"SCNx8"%2"SCNx8"%n", &r, &g, &b, &len) == 3
427 if (sscanf (s, "%2"SCNx8"%2"SCNx8"%2"SCNx8"%n", &r, &g, &b, &len) == 3
438 if (sscanf (s, "rgb (%"SCNi8" , %"SCNi8" , %"SCNi8") %n",
439 &r, &g, &b, &len) == 3
448 /* rgba(r,g,b,a), ignoring a. */
449 if (sscanf (s, "rgba (%"SCNi8" , %"SCNi8" , %"SCNi8", %*f) %n",
450 &r, &g, &b, &len) == 3
459 int code = lookup_color_name (s);
462 color->r = code >> 16;
463 color->g = code >> 8;
471 /* Parse color information specified by KEY into {RED,GREEN,BLUE}.
472 Currently, the input string must be of the form "#RRRRGGGGBBBB"
473 Future implementations might allow things like "yellow" and
474 "sky-blue-ultra-brown"
477 parse_color (struct output_driver *d, struct string_map *options,
478 const char *key, const char *default_value,
479 struct cell_color *color)
481 char *string = parse_string (opt (d, options, key, default_value));
482 if (!parse_color__ (string, color) && !parse_color__ (default_value, color))
483 *color = (struct cell_color) CELL_COLOR_BLACK;
487 static PangoFontDescription *
488 parse_font (const char *font, int default_size, bool bold, bool italic)
490 if (!c_strcasecmp (font, "Monospaced"))
493 PangoFontDescription *desc = pango_font_description_from_string (font);
497 /* If the font description didn't include an explicit font size, then set it
498 to DEFAULT_SIZE, which is in inch/72000 units. */
499 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
500 pango_font_description_set_size (desc,
501 (default_size / 1000.0) * PANGO_SCALE);
503 pango_font_description_set_weight (desc, (bold
505 : PANGO_WEIGHT_NORMAL));
506 pango_font_description_set_style (desc, (italic
508 : PANGO_STYLE_NORMAL));
513 static PangoFontDescription *
514 parse_font_option (struct output_driver *d, struct string_map *options,
515 const char *key, const char *default_value,
516 int default_size, bool bold, bool italic)
518 char *string = parse_string (opt (d, options, key, default_value));
519 PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
522 msg (MW, _("`%s': bad font specification"), string);
524 /* Fall back to DEFAULT_VALUE, which had better be a valid font
526 desc = parse_font (default_value, default_size, bold, italic);
527 assert (desc != NULL);
535 apply_options (struct xr_driver *xr, struct string_map *o)
537 struct output_driver *d = &xr->driver;
539 /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
540 int left_margin, right_margin;
541 int top_margin, bottom_margin;
542 int paper_width, paper_length;
544 int min_break[TABLE_N_AXES];
546 /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
547 const double scale = XR_POINT / 1000.;
551 for (i = 0; i < XR_N_FONTS; i++)
553 struct xr_font *font = &xr->fonts[i];
555 if (font->desc != NULL)
556 pango_font_description_free (font->desc);
559 font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
560 xr->fonts[XR_FONT_FIXED].desc = parse_font_option
561 (d, o, "fixed-font", "monospace", font_size, false, false);
562 xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font_option (
563 d, o, "prop-font", "sans serif", font_size, false, false);
564 xr->fonts[XR_FONT_EMPHASIS].desc = parse_font_option (
565 d, o, "emph-font", "sans serif", font_size, false, true);
567 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg);
568 parse_color (d, o, "foreground-color", "#000000000000", &xr->fg);
570 xr->transparent = parse_boolean (opt (d, o, "transparent", "false"));
571 xr->systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
573 /* Get dimensions. */
574 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
575 left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
576 right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
577 top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
578 bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
580 min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
581 min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
583 int object_spacing = (parse_dimension (opt (d, o, "object-spacing", NULL))
586 /* Convert to inch/(XR_POINT * 72). */
587 xr->left_margin = left_margin * scale;
588 xr->right_margin = right_margin * scale;
589 xr->top_margin = top_margin * scale;
590 xr->bottom_margin = bottom_margin * scale;
591 xr->width = (paper_width - left_margin - right_margin) * scale;
592 xr->length = (paper_length - top_margin - bottom_margin) * scale;
593 xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
594 xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
595 xr->object_spacing = object_spacing >= 0 ? object_spacing : XR_POINT * 12;
597 /* There are no headings so headings_height can stay 0. */
600 static struct xr_driver *
601 xr_allocate (const char *name, int device_type, struct string_map *o,
604 struct xr_driver *xr = xzalloc (sizeof *xr);
605 struct output_driver *d = &xr->driver;
607 output_driver_init (d, &cairo_driver_class, name, device_type);
609 string_map_init (&xr->heading_vars);
611 /* This is a nasty kluge for an issue that does not make sense. On any
612 surface other than a screen (e.g. for output to PDF or PS or SVG), the
613 fonts are way too big by default. A "9-point" font seems to appear about
614 16 points tall. We use a scale factor for these surfaces to help, but the
615 underlying issue is a mystery. */
616 xr->font_scale = font_scale;
618 apply_options (xr, o);
624 pango_to_xr (int pango)
626 return (XR_POINT != PANGO_SCALE
627 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
634 return (XR_POINT != PANGO_SCALE
635 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
640 xr_measure_fonts (cairo_t *cairo, const struct xr_font fonts[XR_N_FONTS],
641 int *char_width, int *char_height)
645 for (int i = 0; i < XR_N_FONTS; i++)
647 PangoLayout *layout = pango_cairo_create_layout (cairo);
648 pango_layout_set_font_description (layout, fonts[i].desc);
650 pango_layout_set_text (layout, "0", 1);
653 pango_layout_get_size (layout, &cw, &ch);
654 *char_width = MAX (*char_width, pango_to_xr (cw));
655 *char_height = MAX (*char_height, pango_to_xr (ch));
657 g_object_unref (G_OBJECT (layout));
662 get_layout_height (PangoLayout *layout)
665 pango_layout_get_size (layout, &w, &h);
670 xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
671 const struct page_heading *ph, int page_number,
672 int width, bool draw, int base_y)
674 PangoLayout *layout = pango_cairo_create_layout (cairo);
675 pango_layout_set_font_description (layout, font);
678 for (size_t i = 0; i < ph->n; i++)
680 const struct page_paragraph *pp = &ph->paragraphs[i];
682 char *markup = output_driver_substitute_heading_vars (pp->markup,
684 pango_layout_set_markup (layout, markup, -1);
687 pango_layout_set_alignment (
689 (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
690 : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER
691 : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
692 : PANGO_ALIGN_RIGHT));
693 pango_layout_set_width (layout, xr_to_pango (width));
697 cairo_translate (cairo, 0, xr_to_pt (y + base_y));
698 pango_cairo_show_layout (cairo, layout);
699 cairo_restore (cairo);
702 y += pango_to_xr (get_layout_height (layout));
705 g_object_unref (G_OBJECT (layout));
711 xr_measure_headings (cairo_surface_t *surface,
712 const PangoFontDescription *font,
713 const struct page_heading headings[2],
714 int width, int object_spacing, int height[2])
716 cairo_t *cairo = cairo_create (surface);
718 for (int i = 0; i < 2; i++)
720 int h = xr_render_page_heading (cairo, font, &headings[i], -1,
723 /* If the top heading is nonempty, add some space below it. */
731 cairo_destroy (cairo);
736 xr_check_fonts (cairo_surface_t *surface,
737 const struct xr_font fonts[XR_N_FONTS],
738 int usable_width, int usable_length)
740 cairo_t *cairo = cairo_create (surface);
741 int char_width, char_height;
742 xr_measure_fonts (cairo, fonts, &char_width, &char_height);
743 cairo_destroy (cairo);
746 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
747 if (usable_width / char_width < MIN_WIDTH)
749 msg (ME, _("The defined page is not wide enough to hold at least %d "
750 "characters in the default font. In fact, there's only "
751 "room for %d characters."),
752 MIN_WIDTH, usable_width / char_width);
755 if (usable_length / char_height < MIN_LENGTH)
757 msg (ME, _("The defined page is not long enough to hold at least %d "
758 "lines in the default font. In fact, there's only "
759 "room for %d lines."),
760 MIN_LENGTH, usable_length / char_height);
767 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
771 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
773 xr_measure_fonts (xr->cairo, xr->fonts, &xr->char_width, &xr->char_height);
775 for (int i = 0; i < XR_N_FONTS; i++)
777 struct xr_font *font = &xr->fonts[i];
778 font->layout = pango_cairo_create_layout (cairo);
779 pango_layout_set_font_description (font->layout, font->desc);
782 if (xr->params == NULL)
784 xr->params = xmalloc (sizeof *xr->params);
785 xr->params->draw_line = xr_draw_line;
786 xr->params->measure_cell_width = xr_measure_cell_width;
787 xr->params->measure_cell_height = xr_measure_cell_height;
788 xr->params->adjust_break = xr_adjust_break;
789 xr->params->draw_cell = xr_draw_cell;
790 xr->params->aux = xr;
791 xr->params->size[H] = xr->width;
792 xr->params->size[V] = xr->length;
793 xr->params->font_size[H] = xr->char_width;
794 xr->params->font_size[V] = xr->char_height;
796 int lw = XR_LINE_WIDTH;
797 int ls = XR_LINE_SPACE;
798 for (int i = 0; i < TABLE_N_AXES; i++)
800 xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
801 xr->params->line_widths[i][RENDER_LINE_SINGLE] = lw;
802 xr->params->line_widths[i][RENDER_LINE_DASHED] = lw;
803 xr->params->line_widths[i][RENDER_LINE_THICK] = lw * 2;
804 xr->params->line_widths[i][RENDER_LINE_THIN] = lw / 2;
805 xr->params->line_widths[i][RENDER_LINE_DOUBLE] = 2 * lw + ls;
808 for (int i = 0; i < TABLE_N_AXES; i++)
809 xr->params->min_break[i] = xr->min_break[i];
810 xr->params->supports_margins = true;
811 xr->params->rtl = render_direction_rtl ();
814 if (!xr->systemcolors)
815 cairo_set_source_rgb (xr->cairo,
816 xr->fg.r / 255.0, xr->fg.g / 255.0, xr->fg.b / 255.0);
819 static struct output_driver *
820 xr_create (const char *file_name, enum settings_output_devices device_type,
821 struct string_map *o, enum xr_output_type file_type)
823 struct xr_driver *xr;
824 cairo_status_t status;
825 double width_pt, length_pt;
827 xr = xr_allocate (file_name, device_type, o, 72.0 / 128.0);
829 width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
830 length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
831 if (file_type == XR_PDF)
832 xr->surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
833 else if (file_type == XR_PS)
834 xr->surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
835 else if (file_type == XR_SVG)
836 xr->surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
840 status = cairo_surface_status (xr->surface);
841 if (status != CAIRO_STATUS_SUCCESS)
843 msg (ME, _("error opening output file `%s': %s"),
844 file_name, cairo_status_to_string (status));
848 if (!xr_check_fonts (xr->surface, xr->fonts, xr->width, xr->length))
854 output_driver_destroy (&xr->driver);
858 static struct output_driver *
859 xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
860 struct string_map *o)
862 struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_PDF);
867 static struct output_driver *
868 xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
869 struct string_map *o)
871 struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_PS);
876 static struct output_driver *
877 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
878 struct string_map *o)
880 struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_SVG);
886 xr_destroy (struct output_driver *driver)
888 struct xr_driver *xr = xr_driver_cast (driver);
891 xr_driver_destroy_fsm (xr);
893 if (xr->cairo != NULL)
895 cairo_surface_finish (xr->surface);
896 cairo_status_t status = cairo_status (xr->cairo);
897 if (status != CAIRO_STATUS_SUCCESS)
898 fprintf (stderr, _("error drawing output for %s driver: %s"),
899 output_driver_get_name (driver),
900 cairo_status_to_string (status));
901 cairo_surface_destroy (xr->surface);
903 cairo_destroy (xr->cairo);
906 for (i = 0; i < XR_N_FONTS; i++)
908 struct xr_font *font = &xr->fonts[i];
910 if (font->desc != NULL)
911 pango_font_description_free (font->desc);
912 if (font->layout != NULL)
913 g_object_unref (font->layout);
921 xr_flush (struct output_driver *driver)
923 struct xr_driver *xr = xr_driver_cast (driver);
925 cairo_surface_flush (cairo_get_target (xr->cairo));
929 xr_update_page_setup (struct output_driver *driver,
930 const struct page_setup *ps)
932 struct xr_driver *xr = xr_driver_cast (driver);
934 xr->initial_page_number = ps->initial_page_number;
935 xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
940 int usable[TABLE_N_AXES];
941 for (int i = 0; i < 2; i++)
942 usable[i] = (ps->paper[i]
943 - (ps->margins[i][0] + ps->margins[i][1])) * 72 * XR_POINT;
945 int headings_height[2];
946 usable[V] -= xr_measure_headings (
947 xr->surface, xr->fonts[XR_FONT_PROPORTIONAL].desc, ps->headings,
948 usable[H], xr->object_spacing, headings_height);
950 enum table_axis h = ps->orientation == PAGE_LANDSCAPE;
951 enum table_axis v = !h;
952 if (!xr_check_fonts (xr->surface, xr->fonts, usable[h], usable[v]))
955 for (int i = 0; i < 2; i++)
957 page_heading_uninit (&xr->headings[i]);
958 page_heading_copy (&xr->headings[i], &ps->headings[i]);
959 xr->headings_height[i] = headings_height[i];
961 xr->width = usable[h];
962 xr->length = usable[v];
963 xr->left_margin = ps->margins[h][0] * 72 * XR_POINT;
964 xr->right_margin = ps->margins[h][1] * 72 * XR_POINT;
965 xr->top_margin = ps->margins[v][0] * 72 * XR_POINT;
966 xr->bottom_margin = ps->margins[v][1] * 72 * XR_POINT;
967 cairo_pdf_surface_set_size (xr->surface,
968 ps->paper[h] * 72.0, ps->paper[v] * 72.0);
972 xr_submit (struct output_driver *driver, const struct output_item *output_item)
974 struct xr_driver *xr = xr_driver_cast (driver);
976 if (is_page_setup_item (output_item))
978 xr_update_page_setup (driver,
979 to_page_setup_item (output_item)->page_setup);
985 xr->page_number = xr->initial_page_number - 1;
986 xr_set_cairo (xr, cairo_create (xr->surface));
987 cairo_save (xr->cairo);
988 xr_driver_next_page (xr, xr->cairo);
991 xr_driver_output_item (xr, output_item);
992 while (xr_driver_need_new_page (xr))
994 cairo_restore (xr->cairo);
995 cairo_show_page (xr->cairo);
996 cairo_save (xr->cairo);
997 xr_driver_next_page (xr, xr->cairo);
1001 /* Functions for rendering a series of output items to a series of Cairo
1002 contexts, with pagination.
1004 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
1005 its underlying implementation.
1007 See the big comment in cairo.h for intended usage. */
1009 /* Gives new page CAIRO to XR for output. */
1011 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
1013 if (!xr->transparent)
1016 cairo_set_source_rgb (cairo,
1017 xr->bg.r / 255.0, xr->bg.g / 255.0, xr->bg.b / 255.0);
1018 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
1020 cairo_restore (cairo);
1022 cairo_translate (cairo,
1023 xr_to_pt (xr->left_margin),
1024 xr_to_pt (xr->top_margin + xr->headings_height[0]));
1030 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
1031 &xr->headings[0], xr->page_number, xr->width, true,
1032 -xr->headings_height[0]);
1033 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
1034 &xr->headings[1], xr->page_number, xr->width, true,
1037 xr_driver_run_fsm (xr);
1040 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
1041 rendering a previous output item, that is, only if xr_driver_need_new_page()
1044 xr_driver_output_item (struct xr_driver *xr,
1045 const struct output_item *output_item)
1047 assert (xr->fsm == NULL);
1048 xr->fsm = xr_render_output_item (xr, output_item);
1049 xr_driver_run_fsm (xr);
1052 /* Returns true if XR is in the middle of rendering an output item and needs a
1053 new page to be appended using xr_driver_next_page() to make progress,
1056 xr_driver_need_new_page (const struct xr_driver *xr)
1058 return xr->fsm != NULL;
1061 /* Returns true if the current page doesn't have any content yet. */
1063 xr_driver_is_page_blank (const struct xr_driver *xr)
1069 xr_driver_destroy_fsm (struct xr_driver *xr)
1071 if (xr->fsm != NULL)
1073 xr->fsm->destroy (xr->fsm);
1079 xr_driver_run_fsm (struct xr_driver *xr)
1081 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
1082 xr_driver_destroy_fsm (xr);
1086 xr_layout_cell (struct xr_driver *, const struct table_cell *,
1087 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1088 int *width, int *height, int *brk);
1091 set_source_rgba (cairo_t *cairo, const struct cell_color *color)
1093 cairo_set_source_rgba (cairo,
1094 color->r / 255., color->g / 255., color->b / 255.,
1095 color->alpha / 255.);
1099 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
1100 const struct cell_color *color)
1102 cairo_new_path (xr->cairo);
1103 if (!xr->systemcolors)
1104 set_source_rgba (xr->cairo, color);
1105 cairo_set_line_width (
1107 xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
1108 : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
1110 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
1111 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
1112 cairo_stroke (xr->cairo);
1116 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
1118 cairo_new_path (xr->cairo);
1119 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
1120 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
1121 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y0 + xr->y));
1122 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
1123 cairo_line_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y1 + xr->y));
1124 cairo_close_path (xr->cairo);
1125 cairo_stroke (xr->cairo);
1129 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
1131 cairo_new_path (xr->cairo);
1132 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
1133 cairo_rectangle (xr->cairo,
1134 xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y),
1135 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
1136 cairo_fill (xr->cairo);
1139 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
1140 shortening it to X0...X1 if SHORTEN is true.
1141 Draws a horizontal line X1...X3 at Y if RIGHT says so,
1142 shortening it to X2...X3 if SHORTEN is true. */
1144 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
1145 enum render_line_style left, enum render_line_style right,
1146 const struct cell_color *left_color,
1147 const struct cell_color *right_color,
1150 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
1151 && cell_color_equal (left_color, right_color))
1152 dump_line (xr, x0, y, x3, y, left, left_color);
1155 if (left != RENDER_LINE_NONE)
1156 dump_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
1157 if (right != RENDER_LINE_NONE)
1158 dump_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
1162 /* Draws a vertical line Y0...Y2 at X if TOP says so,
1163 shortening it to Y0...Y1 if SHORTEN is true.
1164 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
1165 shortening it to Y2...Y3 if SHORTEN is true. */
1167 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
1168 enum render_line_style top, enum render_line_style bottom,
1169 const struct cell_color *top_color,
1170 const struct cell_color *bottom_color,
1173 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
1174 && cell_color_equal (top_color, bottom_color))
1175 dump_line (xr, x, y0, x, y3, top, top_color);
1178 if (top != RENDER_LINE_NONE)
1179 dump_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
1180 if (bottom != RENDER_LINE_NONE)
1181 dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
1186 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
1187 enum render_line_style styles[TABLE_N_AXES][2],
1188 struct cell_color colors[TABLE_N_AXES][2])
1190 const int x0 = bb[H][0];
1191 const int y0 = bb[V][0];
1192 const int x3 = bb[H][1];
1193 const int y3 = bb[V][1];
1194 const int top = styles[H][0];
1195 const int bottom = styles[H][1];
1197 int start_side = render_direction_rtl();
1198 int end_side = !start_side;
1199 const int start_of_line = styles[V][start_side];
1200 const int end_of_line = styles[V][end_side];
1201 const struct cell_color *top_color = &colors[H][0];
1202 const struct cell_color *bottom_color = &colors[H][1];
1203 const struct cell_color *start_color = &colors[V][start_side];
1204 const struct cell_color *end_color = &colors[V][end_side];
1206 /* The algorithm here is somewhat subtle, to allow it to handle
1207 all the kinds of intersections that we need.
1209 Three additional ordinates are assigned along the x axis. The
1210 first is xc, midway between x0 and x3. The others are x1 and
1211 x2; for a single vertical line these are equal to xc, and for
1212 a double vertical line they are the ordinates of the left and
1213 right half of the double line.
1215 yc, y1, and y2 are assigned similarly along the y axis.
1217 The following diagram shows the coordinate system and output
1218 for double top and bottom lines, single left line, and no
1222 y0 ________________________
1228 y1 = y2 = yc |######### # |
1233 y3 |________#_____#_______|
1235 struct xr_driver *xr = xr_;
1237 /* Offset from center of each line in a pair of double lines. */
1238 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
1240 /* Are the lines along each axis single or double?
1241 (It doesn't make sense to have different kinds of line on the
1242 same axis, so we don't try to gracefully handle that case.) */
1243 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
1244 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
1246 /* When horizontal lines are doubled,
1247 the left-side line along y1 normally runs from x0 to x2,
1248 and the right-side line along y1 from x3 to x1.
1249 If the top-side line is also doubled, we shorten the y1 lines,
1250 so that the left-side line runs only to x1,
1251 and the right-side line only to x2.
1252 Otherwise, the horizontal line at y = y1 below would cut off
1253 the intersection, which looks ugly:
1255 y0 ________________________
1260 y1 |######### ########|
1263 y2 |######################|
1266 y3 |______________________|
1267 It is more of a judgment call when the horizontal line is
1268 single. We actually choose to cut off the line anyhow, as
1269 shown in the first diagram above.
1271 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
1272 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
1273 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
1274 int horz_line_ofs = double_vert ? double_line_ofs : 0;
1275 int xc = (x0 + x3) / 2;
1276 int x1 = xc - horz_line_ofs;
1277 int x2 = xc + horz_line_ofs;
1279 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
1280 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
1281 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
1282 int vert_line_ofs = double_horz ? double_line_ofs : 0;
1283 int yc = (y0 + y3) / 2;
1284 int y1 = yc - vert_line_ofs;
1285 int y2 = yc + vert_line_ofs;
1288 horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
1289 start_color, end_color, shorten_yc_line);
1292 horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
1293 start_color, end_color, shorten_y1_lines);
1294 horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
1295 start_color, end_color, shorten_y2_lines);
1299 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
1303 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
1305 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
1311 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
1312 int *min_width, int *max_width)
1314 struct xr_driver *xr = xr_;
1315 int bb[TABLE_N_AXES][2];
1316 int clip[TABLE_N_AXES][2];
1323 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1324 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
1327 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
1330 *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
1331 + cell->style->cell_style.margin[H][1]);
1333 *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
1334 + cell->style->cell_style.margin[H][1]);
1338 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
1340 struct xr_driver *xr = xr_;
1341 int bb[TABLE_N_AXES][2];
1342 int clip[TABLE_N_AXES][2];
1346 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1347 + cell->style->cell_style.margin[H][1]);
1350 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1351 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
1352 h += px_to_xr (cell->style->cell_style.margin[V][0]
1353 + cell->style->cell_style.margin[V][1]);
1357 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
1360 xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
1361 int bb[TABLE_N_AXES][2], int valign_offset,
1362 int spill[TABLE_N_AXES][2],
1363 int clip[TABLE_N_AXES][2])
1365 struct xr_driver *xr = xr_;
1368 if (!xr->transparent)
1370 cairo_save (xr->cairo);
1371 int bg_clip[TABLE_N_AXES][2];
1372 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1374 bg_clip[axis][0] = clip[axis][0];
1375 if (bb[axis][0] == clip[axis][0])
1376 bg_clip[axis][0] -= spill[axis][0];
1378 bg_clip[axis][1] = clip[axis][1];
1379 if (bb[axis][1] == clip[axis][1])
1380 bg_clip[axis][1] += spill[axis][1];
1382 xr_clip (xr, bg_clip);
1383 set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]);
1385 bb[H][0] - spill[H][0],
1386 bb[V][0] - spill[V][0],
1387 bb[H][1] + spill[H][1],
1388 bb[V][1] + spill[V][1]);
1389 cairo_restore (xr->cairo);
1391 cairo_save (xr->cairo);
1392 if (!xr->systemcolors)
1393 set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]);
1395 bb[V][0] += valign_offset;
1397 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1399 bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
1400 bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
1402 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
1403 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1404 cairo_restore (xr->cairo);
1408 xr_adjust_break (void *xr_, const struct table_cell *cell,
1409 int width, int height)
1411 struct xr_driver *xr = xr_;
1412 int bb[TABLE_N_AXES][2];
1413 int clip[TABLE_N_AXES][2];
1416 if (xr_measure_cell_height (xr_, cell, width) < height)
1420 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1421 + cell->style->cell_style.margin[H][1]);
1425 bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
1426 + cell->style->cell_style.margin[V][1]);
1427 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1428 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1433 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
1435 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1437 double x0 = xr_to_pt (clip[H][0] + xr->x);
1438 double y0 = xr_to_pt (clip[V][0] + xr->y);
1439 double x1 = xr_to_pt (clip[H][1] + xr->x);
1440 double y1 = xr_to_pt (clip[V][1] + xr->y);
1442 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1443 cairo_clip (xr->cairo);
1448 add_attr (PangoAttrList *list, PangoAttribute *attr,
1449 guint start_index, guint end_index)
1451 attr->start_index = start_index;
1452 attr->end_index = end_index;
1453 pango_attr_list_insert (list, attr);
1457 markup_escape (struct string *out, unsigned int options,
1458 const char *in, size_t len)
1460 if (!(options & TAB_MARKUP))
1462 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
1474 ds_put_cstr (out, "&");
1477 ds_put_cstr (out, "<");
1480 ds_put_cstr (out, ">");
1483 ds_put_byte (out, c);
1490 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
1492 int size[TABLE_N_AXES];
1493 pango_layout_get_size (layout, &size[H], &size[V]);
1498 xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
1499 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1500 int *widthp, int *brk)
1502 const struct font_style *font_style = &cell->style->font_style;
1503 const struct cell_style *cell_style = &cell->style->cell_style;
1504 unsigned int options = cell->options;
1506 enum table_axis X = options & TAB_ROTATE ? V : H;
1507 enum table_axis Y = !X;
1508 int R = options & TAB_ROTATE ? 0 : 1;
1510 struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1511 : &xr->fonts[XR_FONT_PROPORTIONAL]);
1512 struct xr_font local_font;
1513 if (font_style->typeface)
1515 PangoFontDescription *desc = parse_font (
1516 font_style->typeface,
1517 font_style->size ? font_style->size * 1000 * xr->font_scale : 10000,
1518 font_style->bold, font_style->italic);
1521 PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1522 pango_layout_set_font_description (layout, desc);
1524 local_font.desc = desc;
1525 local_font.layout = layout;
1530 const char *text = cell->text;
1531 enum table_halign halign = table_halign_interpret (
1532 cell_style->halign, cell->options & TAB_NUMERIC);
1533 if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
1535 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
1537 const char *decimal = strrchr (text, cell_style->decimal_char);
1540 pango_layout_set_text (font->layout, decimal, strlen (decimal));
1541 pango_layout_set_width (font->layout, -1);
1542 margin_adjustment += get_layout_dimension (font->layout, H);
1545 if (margin_adjustment < 0)
1546 bb[H][1] += margin_adjustment;
1549 struct string tmp = DS_EMPTY_INITIALIZER;
1550 PangoAttrList *attrs = NULL;
1552 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
1553 Pango's implementation of it): it will break after a period or a comma
1554 that precedes a digit, e.g. in ".000" it will break after the period.
1555 This code looks for such a situation and inserts a U+2060 WORD JOINER
1556 to prevent the break.
1558 This isn't necessary when the decimal point is between two digits
1559 (e.g. "0.000" won't be broken) or when the display width is not limited so
1560 that word wrapping won't happen.
1562 It isn't necessary to look for more than one period or comma, as would
1563 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
1564 are present then there will always be a digit on both sides of every
1565 period and comma. */
1566 if (options & TAB_MARKUP)
1568 PangoAttrList *new_attrs;
1570 if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
1573 tmp.ss = ss_cstr (new_text);
1574 tmp.capacity = tmp.ss.length;
1578 /* XXX should we report the error? */
1579 ds_put_cstr (&tmp, text);
1582 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
1584 const char *decimal = text + strcspn (text, ".,");
1586 && c_isdigit (decimal[1])
1587 && (decimal == text || !c_isdigit (decimal[-1])))
1589 ds_extend (&tmp, strlen (text) + 16);
1590 markup_escape (&tmp, options, text, decimal - text + 1);
1591 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
1592 markup_escape (&tmp, options, decimal + 1, -1);
1596 if (font_style->underline)
1599 attrs = pango_attr_list_new ();
1600 pango_attr_list_insert (attrs, pango_attr_underline_new (
1601 PANGO_UNDERLINE_SINGLE));
1604 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
1606 /* If we haven't already put TEXT into tmp, do it now. */
1607 if (ds_is_empty (&tmp))
1609 ds_extend (&tmp, strlen (text) + 16);
1610 markup_escape (&tmp, options, text, -1);
1613 size_t subscript_ofs = ds_length (&tmp);
1614 for (size_t i = 0; i < cell->n_subscripts; i++)
1617 ds_put_byte (&tmp, ',');
1618 ds_put_cstr (&tmp, cell->subscripts[i]);
1621 size_t superscript_ofs = ds_length (&tmp);
1622 if (cell->superscript)
1623 ds_put_cstr (&tmp, cell->superscript);
1625 size_t footnote_ofs = ds_length (&tmp);
1626 for (size_t i = 0; i < cell->n_footnotes; i++)
1629 ds_put_byte (&tmp, ',');
1630 ds_put_cstr (&tmp, cell->footnotes[i]->marker);
1633 /* Allow footnote markers to occupy the right margin. That way, numbers
1634 in the column are still aligned. */
1635 if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
1637 /* Measure the width of the footnote marker, so we know how much we
1638 need to make room for. */
1639 pango_layout_set_text (font->layout, ds_cstr (&tmp) + footnote_ofs,
1640 ds_length (&tmp) - footnote_ofs);
1642 PangoAttrList *fn_attrs = pango_attr_list_new ();
1643 pango_attr_list_insert (
1644 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
1645 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
1646 pango_layout_set_attributes (font->layout, fn_attrs);
1647 pango_attr_list_unref (fn_attrs);
1648 int footnote_width = get_layout_dimension (font->layout, X);
1650 /* Bound the adjustment by the width of the right margin. */
1651 int right_margin = px_to_xr (cell_style->margin[X][R]);
1652 int footnote_adjustment = MIN (footnote_width, right_margin);
1654 /* Adjust the bounding box. */
1655 if (options & TAB_ROTATE)
1656 footnote_adjustment = -footnote_adjustment;
1657 bb[X][R] += footnote_adjustment;
1660 pango_layout_set_attributes (font->layout, NULL);
1663 /* Set attributes. */
1665 attrs = pango_attr_list_new ();
1666 add_attr (attrs, pango_attr_font_desc_new (font->desc), subscript_ofs,
1667 PANGO_ATTR_INDEX_TO_TEXT_END);
1668 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
1669 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
1670 if (cell->n_subscripts)
1671 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
1672 superscript_ofs - subscript_ofs);
1673 if (cell->superscript || cell->n_footnotes)
1674 add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
1675 PANGO_ATTR_INDEX_TO_TEXT_END);
1678 /* Set the attributes, if any. */
1681 pango_layout_set_attributes (font->layout, attrs);
1682 pango_attr_list_unref (attrs);
1686 if (ds_is_empty (&tmp))
1687 pango_layout_set_text (font->layout, text, -1);
1689 pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
1692 pango_layout_set_alignment (font->layout,
1693 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
1694 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
1695 : PANGO_ALIGN_CENTER));
1696 pango_layout_set_width (
1698 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
1699 pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1701 if (clip[H][0] != clip[H][1])
1703 cairo_save (xr->cairo);
1704 if (!(options & TAB_ROTATE))
1706 if (options & TAB_ROTATE)
1708 cairo_translate (xr->cairo,
1709 xr_to_pt (bb[H][0] + xr->x),
1710 xr_to_pt (bb[V][1] + xr->y));
1711 cairo_rotate (xr->cairo, -M_PI_2);
1714 cairo_translate (xr->cairo,
1715 xr_to_pt (bb[H][0] + xr->x),
1716 xr_to_pt (bb[V][0] + xr->y));
1717 pango_cairo_show_layout (xr->cairo, font->layout);
1719 /* If enabled, this draws a blue rectangle around the extents of each
1720 line of text, which can be rather useful for debugging layout
1724 PangoLayoutIter *iter;
1725 iter = pango_layout_get_iter (font->layout);
1728 PangoRectangle extents;
1730 pango_layout_iter_get_line_extents (iter, &extents, NULL);
1731 cairo_save (xr->cairo);
1732 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1734 pango_to_xr (extents.x) - xr->x,
1735 pango_to_xr (extents.y) - xr->y,
1736 pango_to_xr (extents.x + extents.width) - xr->x,
1737 pango_to_xr (extents.y + extents.height) - xr->y);
1738 cairo_restore (xr->cairo);
1740 while (pango_layout_iter_next_line (iter));
1741 pango_layout_iter_free (iter);
1744 cairo_restore (xr->cairo);
1747 int size[TABLE_N_AXES];
1748 pango_layout_get_size (font->layout, &size[H], &size[V]);
1749 int w = pango_to_xr (size[X]);
1750 int h = pango_to_xr (size[Y]);
1753 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
1755 PangoLayoutIter *iter;
1758 /* Choose a breakpoint between lines instead of in the middle of one. */
1759 iter = pango_layout_get_iter (font->layout);
1762 PangoRectangle extents;
1766 pango_layout_iter_get_line_extents (iter, NULL, &extents);
1767 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1768 extents.x = pango_to_xr (extents.x);
1769 extents.y = pango_to_xr (y0);
1770 extents.width = pango_to_xr (extents.width);
1771 extents.height = pango_to_xr (y1 - y0);
1772 bottom = bb[V][0] + extents.y + extents.height;
1773 if (bottom < bb[V][1])
1775 if (brk && clip[H][0] != clip[H][1])
1783 while (pango_layout_iter_next_line (iter));
1784 pango_layout_iter_free (iter);
1786 /* If enabled, draws a green line across the chosen breakpoint, which can
1787 be useful for debugging issues with breaking. */
1790 if (best && !xr->nest)
1791 dump_line (xr, -xr->left_margin, best,
1792 xr->width + xr->right_margin, best,
1794 &(struct cell_color) CELL_COLOR (0, 255, 0));
1798 pango_layout_set_attributes (font->layout, NULL);
1800 if (font == &local_font)
1802 g_object_unref (G_OBJECT (font->layout));
1803 pango_font_description_free (font->desc);
1810 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1811 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1812 int *width, int *height, int *brk)
1817 /* If enabled, draws a blue rectangle around the cell extents, which can be
1818 useful for debugging layout. */
1821 if (clip[H][0] != clip[H][1])
1823 int offset = (xr->nest) * XR_POINT;
1825 cairo_save (xr->cairo);
1826 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1828 bb[H][0] + offset, bb[V][0] + offset,
1829 bb[H][1] - offset, bb[V][1] - offset);
1830 cairo_restore (xr->cairo);
1836 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
1839 struct output_driver_factory pdf_driver_factory =
1840 { "pdf", "pspp.pdf", xr_pdf_create };
1841 struct output_driver_factory ps_driver_factory =
1842 { "ps", "pspp.ps", xr_ps_create };
1843 struct output_driver_factory svg_driver_factory =
1844 { "svg", "pspp.svg", xr_svg_create };
1846 static const struct output_driver_class cairo_driver_class =
1854 /* GUI rendering helpers. */
1858 struct output_item *item;
1861 struct render_pager *p;
1862 struct xr_driver *xr;
1865 #define CHART_WIDTH 500
1866 #define CHART_HEIGHT 375
1871 xr_driver_create (cairo_t *cairo, struct string_map *options)
1873 struct xr_driver *xr = xr_allocate ("cairo", 0, options, 1.0);
1874 xr_set_cairo (xr, cairo);
1878 /* Destroy XR, which should have been created with xr_driver_create(). Any
1879 cairo_t added to XR is not destroyed, because it is owned by the client. */
1881 xr_driver_destroy (struct xr_driver *xr)
1886 output_driver_destroy (&xr->driver);
1890 static struct xr_rendering *
1891 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1893 struct table_item *table_item;
1894 struct xr_rendering *r;
1896 table_item = table_item_create (table_from_string (text), NULL, NULL);
1897 r = xr_rendering_create (xr, &table_item->output_item, cr);
1898 table_item_unref (table_item);
1904 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1906 if (is_table_item (xr->item))
1907 apply_options (xr->xr, o);
1910 struct xr_rendering *
1911 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1914 struct xr_rendering *r = NULL;
1916 if (is_text_item (item))
1917 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1919 else if (is_message_item (item))
1921 const struct message_item *message_item = to_message_item (item);
1922 char *s = msg_to_string (message_item_get_msg (message_item));
1923 r = xr_rendering_create_text (xr, s, cr);
1926 else if (is_table_item (item))
1928 r = xzalloc (sizeof *r);
1929 r->item = output_item_ref (item);
1931 xr_set_cairo (xr, cr);
1932 r->p = render_pager_create (xr->params, to_table_item (item));
1934 else if (is_chart_item (item))
1936 r = xzalloc (sizeof *r);
1937 r->item = output_item_ref (item);
1939 else if (is_group_open_item (item))
1940 r = xr_rendering_create_text (xr, to_group_open_item (item)->command_name,
1947 xr_rendering_destroy (struct xr_rendering *r)
1951 output_item_unref (r->item);
1952 render_pager_destroy (r->p);
1958 xr_rendering_measure (const struct xr_rendering *r, int *wp, int *hp)
1962 if (is_table_item (r->item))
1964 w = render_pager_get_size (r->p, H) / XR_POINT;
1965 h = render_pager_get_size (r->p, V) / XR_POINT;
1979 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1980 double x, double y, double width, double height);
1984 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1985 int x0, int y0, int x1, int y1)
1987 if (is_table_item (r->item))
1989 struct xr_driver *xr = r->xr;
1991 xr_set_cairo (xr, cr);
1993 render_pager_draw_region (r->p, x0 * XR_POINT, y0 * XR_POINT,
1994 (x1 - x0) * XR_POINT, (y1 - y0) * XR_POINT);
1997 xr_draw_chart (to_chart_item (r->item), cr,
1998 0, 0, CHART_WIDTH, CHART_HEIGHT);
2002 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
2003 double x, double y, double width, double height)
2005 struct xrchart_geometry geom;
2008 cairo_translate (cr, x, y + height);
2009 cairo_scale (cr, 1.0, -1.0);
2010 xrchart_geometry_init (cr, &geom, width, height);
2011 if (is_boxplot (chart_item))
2012 xrchart_draw_boxplot (chart_item, cr, &geom);
2013 else if (is_histogram_chart (chart_item))
2014 xrchart_draw_histogram (chart_item, cr, &geom);
2015 else if (is_np_plot_chart (chart_item))
2016 xrchart_draw_np_plot (chart_item, cr, &geom);
2017 else if (is_piechart (chart_item))
2018 xrchart_draw_piechart (chart_item, cr, &geom);
2019 else if (is_barchart (chart_item))
2020 xrchart_draw_barchart (chart_item, cr, &geom);
2021 else if (is_roc_chart (chart_item))
2022 xrchart_draw_roc (chart_item, cr, &geom);
2023 else if (is_scree (chart_item))
2024 xrchart_draw_scree (chart_item, cr, &geom);
2025 else if (is_spreadlevel_plot_chart (chart_item))
2026 xrchart_draw_spreadlevel (chart_item, cr, &geom);
2027 else if (is_scatterplot_chart (chart_item))
2028 xrchart_draw_scatterplot (chart_item, cr, &geom);
2031 xrchart_geometry_free (cr, &geom);
2037 xr_draw_png_chart (const struct chart_item *item,
2038 const char *file_name_template, int number,
2039 const struct cell_color *fg,
2040 const struct cell_color *bg)
2042 const int width = 640;
2043 const int length = 480;
2045 cairo_surface_t *surface;
2046 cairo_status_t status;
2047 const char *number_pos;
2051 number_pos = strchr (file_name_template, '#');
2052 if (number_pos != NULL)
2053 file_name = xasprintf ("%.*s%d%s.png", (int) (number_pos - file_name_template),
2054 file_name_template, number, number_pos + 1);
2056 file_name = xasprintf ("%s.png", file_name_template);
2058 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
2059 cr = cairo_create (surface);
2061 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
2064 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
2066 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
2068 status = cairo_surface_write_to_png (surface, file_name);
2069 if (status != CAIRO_STATUS_SUCCESS)
2070 msg (ME, _("error writing output file `%s': %s"),
2071 file_name, cairo_status_to_string (status));
2074 cairo_surface_destroy (surface);
2081 xr_draw_eps_chart (const struct chart_item *item,
2082 const char *file_name_template, int number,
2083 const struct cell_color *fg,
2084 const struct cell_color *bg)
2086 const int width = 640;
2087 const int length = 480;
2089 cairo_surface_t *surface;
2090 const char *number_pos;
2094 number_pos = strchr (file_name_template, '#');
2095 if (number_pos != NULL)
2096 file_name = xasprintf ("%.*s%d%s.eps", (int) (number_pos - file_name_template),
2097 file_name_template, number, number_pos + 1);
2099 file_name = xasprintf ("%s.eps", file_name_template);
2101 surface = cairo_ps_surface_create (file_name, width, length);
2102 cairo_ps_surface_set_eps (surface, true);
2103 cr = cairo_create (surface);
2105 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
2108 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
2110 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
2113 cairo_surface_destroy (surface);
2120 struct xr_table_state
2122 struct xr_render_fsm fsm;
2123 struct render_pager *p;
2127 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
2129 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
2131 while (render_pager_has_next (ts->p))
2135 used = render_pager_draw_next (ts->p, xr->length - xr->y);
2148 xr_table_destroy (struct xr_render_fsm *fsm)
2150 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
2152 render_pager_destroy (ts->p);
2156 static struct xr_render_fsm *
2157 xr_render_table (struct xr_driver *xr, struct table_item *table_item)
2159 struct xr_table_state *ts;
2161 ts = xmalloc (sizeof *ts);
2162 ts->fsm.render = xr_table_render;
2163 ts->fsm.destroy = xr_table_destroy;
2166 xr->y += xr->char_height;
2168 ts->p = render_pager_create (xr->params, table_item);
2169 table_item_unref (table_item);
2174 struct xr_chart_state
2176 struct xr_render_fsm fsm;
2177 struct chart_item *chart_item;
2181 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
2183 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
2185 const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
2187 if (xr->y > xr->length - chart_height)
2190 if (xr->cairo != NULL)
2192 xr_draw_chart (cs->chart_item, xr->cairo,
2195 xr_to_pt (xr->width),
2196 xr_to_pt (chart_height));
2198 xr->y += chart_height;
2204 xr_chart_destroy (struct xr_render_fsm *fsm)
2206 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
2208 chart_item_unref (cs->chart_item);
2212 static struct xr_render_fsm *
2213 xr_render_chart (const struct chart_item *chart_item)
2215 struct xr_chart_state *cs;
2217 cs = xmalloc (sizeof *cs);
2218 cs->fsm.render = xr_chart_render;
2219 cs->fsm.destroy = xr_chart_destroy;
2220 cs->chart_item = chart_item_ref (chart_item);
2226 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
2232 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
2234 /* Nothing to do. */
2237 static struct xr_render_fsm *
2238 xr_render_eject (void)
2240 static struct xr_render_fsm eject_renderer =
2246 return &eject_renderer;
2249 static struct xr_render_fsm *
2250 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
2252 enum text_item_type type = text_item_get_type (text_item);
2253 const char *text = text_item_get_text (text_item);
2257 case TEXT_ITEM_PAGE_TITLE:
2258 string_map_replace (&xr->heading_vars, "PageTitle", text);
2261 case TEXT_ITEM_EJECT_PAGE:
2263 return xr_render_eject ();
2267 return xr_render_table (
2268 xr, text_item_to_table_item (text_item_ref (text_item)));
2274 static struct xr_render_fsm *
2275 xr_render_message (struct xr_driver *xr,
2276 const struct message_item *message_item)
2278 char *s = msg_to_string (message_item_get_msg (message_item));
2279 struct text_item *item = text_item_create (TEXT_ITEM_LOG, s);
2281 return xr_render_table (xr, text_item_to_table_item (item));
2284 static struct xr_render_fsm *
2285 xr_render_output_item (struct xr_driver *xr,
2286 const struct output_item *output_item)
2288 if (is_table_item (output_item))
2289 return xr_render_table (xr, table_item_ref (to_table_item (output_item)));
2290 else if (is_chart_item (output_item))
2291 return xr_render_chart (to_chart_item (output_item));
2292 else if (is_text_item (output_item))
2293 return xr_render_text (xr, to_text_item (output_item));
2294 else if (is_message_item (output_item))
2295 return xr_render_message (xr, to_message_item (output_item));
2301 xr_draw_svg_file (struct xr_rendering *r,
2302 const char *filename)
2306 xr_rendering_measure (r, &width, &height);
2307 cairo_surface_t *surface = cairo_svg_surface_create (filename, width, height);
2310 g_error ("Could not create cairo svg surface with file %s", filename);
2313 cairo_t *cr = cairo_create (surface);
2314 xr_rendering_draw (r, cr, 0, 0, width, height);
2316 cairo_surface_destroy (surface);