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 (struct file_handle *fh, enum settings_output_devices device_type,
821 struct string_map *o, enum xr_output_type file_type)
823 const char *file_name = fh_get_file_name (fh);
824 struct xr_driver *xr = xr_allocate (file_name, device_type, o, 72.0 / 128.0);
825 double width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
826 double length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
827 if (file_type == XR_PDF)
828 xr->surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
829 else if (file_type == XR_PS)
830 xr->surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
831 else if (file_type == XR_SVG)
832 xr->surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
836 cairo_status_t status = cairo_surface_status (xr->surface);
837 if (status != CAIRO_STATUS_SUCCESS)
839 msg (ME, _("error opening output file `%s': %s"),
840 file_name, cairo_status_to_string (status));
844 if (!xr_check_fonts (xr->surface, xr->fonts, xr->width, xr->length))
852 output_driver_destroy (&xr->driver);
856 static struct output_driver *
857 xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
858 struct string_map *o)
860 return xr_create (fh, device_type, o, XR_PDF);
863 static struct output_driver *
864 xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
865 struct string_map *o)
867 return xr_create (fh, device_type, o, XR_PS);
870 static struct output_driver *
871 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
872 struct string_map *o)
874 return xr_create (fh, device_type, o, XR_SVG);
878 xr_destroy (struct output_driver *driver)
880 struct xr_driver *xr = xr_driver_cast (driver);
883 xr_driver_destroy_fsm (xr);
885 if (xr->cairo != NULL)
887 cairo_surface_finish (xr->surface);
888 cairo_status_t status = cairo_status (xr->cairo);
889 if (status != CAIRO_STATUS_SUCCESS)
890 fprintf (stderr, _("error drawing output for %s driver: %s"),
891 output_driver_get_name (driver),
892 cairo_status_to_string (status));
893 cairo_surface_destroy (xr->surface);
895 cairo_destroy (xr->cairo);
898 for (i = 0; i < XR_N_FONTS; i++)
900 struct xr_font *font = &xr->fonts[i];
902 if (font->desc != NULL)
903 pango_font_description_free (font->desc);
904 if (font->layout != NULL)
905 g_object_unref (font->layout);
913 xr_flush (struct output_driver *driver)
915 struct xr_driver *xr = xr_driver_cast (driver);
917 cairo_surface_flush (cairo_get_target (xr->cairo));
921 xr_update_page_setup (struct output_driver *driver,
922 const struct page_setup *ps)
924 struct xr_driver *xr = xr_driver_cast (driver);
926 xr->initial_page_number = ps->initial_page_number;
927 xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
932 int usable[TABLE_N_AXES];
933 for (int i = 0; i < 2; i++)
934 usable[i] = (ps->paper[i]
935 - (ps->margins[i][0] + ps->margins[i][1])) * 72 * XR_POINT;
937 int headings_height[2];
938 usable[V] -= xr_measure_headings (
939 xr->surface, xr->fonts[XR_FONT_PROPORTIONAL].desc, ps->headings,
940 usable[H], xr->object_spacing, headings_height);
942 enum table_axis h = ps->orientation == PAGE_LANDSCAPE;
943 enum table_axis v = !h;
944 if (!xr_check_fonts (xr->surface, xr->fonts, usable[h], usable[v]))
947 for (int i = 0; i < 2; i++)
949 page_heading_uninit (&xr->headings[i]);
950 page_heading_copy (&xr->headings[i], &ps->headings[i]);
951 xr->headings_height[i] = headings_height[i];
953 xr->width = usable[h];
954 xr->length = usable[v];
955 xr->left_margin = ps->margins[h][0] * 72 * XR_POINT;
956 xr->right_margin = ps->margins[h][1] * 72 * XR_POINT;
957 xr->top_margin = ps->margins[v][0] * 72 * XR_POINT;
958 xr->bottom_margin = ps->margins[v][1] * 72 * XR_POINT;
959 cairo_pdf_surface_set_size (xr->surface,
960 ps->paper[h] * 72.0, ps->paper[v] * 72.0);
964 xr_submit (struct output_driver *driver, const struct output_item *output_item)
966 struct xr_driver *xr = xr_driver_cast (driver);
968 if (is_page_setup_item (output_item))
970 xr_update_page_setup (driver,
971 to_page_setup_item (output_item)->page_setup);
977 xr->page_number = xr->initial_page_number - 1;
978 xr_set_cairo (xr, cairo_create (xr->surface));
979 cairo_save (xr->cairo);
980 xr_driver_next_page (xr, xr->cairo);
983 xr_driver_output_item (xr, output_item);
984 while (xr_driver_need_new_page (xr))
986 cairo_restore (xr->cairo);
987 cairo_show_page (xr->cairo);
988 cairo_save (xr->cairo);
989 xr_driver_next_page (xr, xr->cairo);
993 /* Functions for rendering a series of output items to a series of Cairo
994 contexts, with pagination.
996 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
997 its underlying implementation.
999 See the big comment in cairo.h for intended usage. */
1001 /* Gives new page CAIRO to XR for output. */
1003 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
1005 if (!xr->transparent)
1008 cairo_set_source_rgb (cairo,
1009 xr->bg.r / 255.0, xr->bg.g / 255.0, xr->bg.b / 255.0);
1010 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
1012 cairo_restore (cairo);
1014 cairo_translate (cairo,
1015 xr_to_pt (xr->left_margin),
1016 xr_to_pt (xr->top_margin + xr->headings_height[0]));
1022 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
1023 &xr->headings[0], xr->page_number, xr->width, true,
1024 -xr->headings_height[0]);
1025 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
1026 &xr->headings[1], xr->page_number, xr->width, true,
1029 xr_driver_run_fsm (xr);
1032 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
1033 rendering a previous output item, that is, only if xr_driver_need_new_page()
1036 xr_driver_output_item (struct xr_driver *xr,
1037 const struct output_item *output_item)
1039 assert (xr->fsm == NULL);
1040 xr->fsm = xr_render_output_item (xr, output_item);
1041 xr_driver_run_fsm (xr);
1044 /* Returns true if XR is in the middle of rendering an output item and needs a
1045 new page to be appended using xr_driver_next_page() to make progress,
1048 xr_driver_need_new_page (const struct xr_driver *xr)
1050 return xr->fsm != NULL;
1053 /* Returns true if the current page doesn't have any content yet. */
1055 xr_driver_is_page_blank (const struct xr_driver *xr)
1061 xr_driver_destroy_fsm (struct xr_driver *xr)
1063 if (xr->fsm != NULL)
1065 xr->fsm->destroy (xr->fsm);
1071 xr_driver_run_fsm (struct xr_driver *xr)
1073 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
1074 xr_driver_destroy_fsm (xr);
1078 xr_layout_cell (struct xr_driver *, const struct table_cell *,
1079 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1080 int *width, int *height, int *brk);
1083 set_source_rgba (cairo_t *cairo, const struct cell_color *color)
1085 cairo_set_source_rgba (cairo,
1086 color->r / 255., color->g / 255., color->b / 255.,
1087 color->alpha / 255.);
1091 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
1092 const struct cell_color *color)
1094 cairo_new_path (xr->cairo);
1095 if (!xr->systemcolors)
1096 set_source_rgba (xr->cairo, color);
1097 cairo_set_line_width (
1099 xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
1100 : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
1102 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
1103 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
1104 cairo_stroke (xr->cairo);
1108 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
1110 cairo_new_path (xr->cairo);
1111 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
1112 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
1113 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y0 + xr->y));
1114 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
1115 cairo_line_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y1 + xr->y));
1116 cairo_close_path (xr->cairo);
1117 cairo_stroke (xr->cairo);
1121 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
1123 cairo_new_path (xr->cairo);
1124 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
1125 cairo_rectangle (xr->cairo,
1126 xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y),
1127 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
1128 cairo_fill (xr->cairo);
1131 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
1132 shortening it to X0...X1 if SHORTEN is true.
1133 Draws a horizontal line X1...X3 at Y if RIGHT says so,
1134 shortening it to X2...X3 if SHORTEN is true. */
1136 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
1137 enum render_line_style left, enum render_line_style right,
1138 const struct cell_color *left_color,
1139 const struct cell_color *right_color,
1142 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
1143 && cell_color_equal (left_color, right_color))
1144 dump_line (xr, x0, y, x3, y, left, left_color);
1147 if (left != RENDER_LINE_NONE)
1148 dump_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
1149 if (right != RENDER_LINE_NONE)
1150 dump_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
1154 /* Draws a vertical line Y0...Y2 at X if TOP says so,
1155 shortening it to Y0...Y1 if SHORTEN is true.
1156 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
1157 shortening it to Y2...Y3 if SHORTEN is true. */
1159 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
1160 enum render_line_style top, enum render_line_style bottom,
1161 const struct cell_color *top_color,
1162 const struct cell_color *bottom_color,
1165 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
1166 && cell_color_equal (top_color, bottom_color))
1167 dump_line (xr, x, y0, x, y3, top, top_color);
1170 if (top != RENDER_LINE_NONE)
1171 dump_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
1172 if (bottom != RENDER_LINE_NONE)
1173 dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
1178 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
1179 enum render_line_style styles[TABLE_N_AXES][2],
1180 struct cell_color colors[TABLE_N_AXES][2])
1182 const int x0 = bb[H][0];
1183 const int y0 = bb[V][0];
1184 const int x3 = bb[H][1];
1185 const int y3 = bb[V][1];
1186 const int top = styles[H][0];
1187 const int bottom = styles[H][1];
1189 int start_side = render_direction_rtl();
1190 int end_side = !start_side;
1191 const int start_of_line = styles[V][start_side];
1192 const int end_of_line = styles[V][end_side];
1193 const struct cell_color *top_color = &colors[H][0];
1194 const struct cell_color *bottom_color = &colors[H][1];
1195 const struct cell_color *start_color = &colors[V][start_side];
1196 const struct cell_color *end_color = &colors[V][end_side];
1198 /* The algorithm here is somewhat subtle, to allow it to handle
1199 all the kinds of intersections that we need.
1201 Three additional ordinates are assigned along the x axis. The
1202 first is xc, midway between x0 and x3. The others are x1 and
1203 x2; for a single vertical line these are equal to xc, and for
1204 a double vertical line they are the ordinates of the left and
1205 right half of the double line.
1207 yc, y1, and y2 are assigned similarly along the y axis.
1209 The following diagram shows the coordinate system and output
1210 for double top and bottom lines, single left line, and no
1214 y0 ________________________
1220 y1 = y2 = yc |######### # |
1225 y3 |________#_____#_______|
1227 struct xr_driver *xr = xr_;
1229 /* Offset from center of each line in a pair of double lines. */
1230 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
1232 /* Are the lines along each axis single or double?
1233 (It doesn't make sense to have different kinds of line on the
1234 same axis, so we don't try to gracefully handle that case.) */
1235 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
1236 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
1238 /* When horizontal lines are doubled,
1239 the left-side line along y1 normally runs from x0 to x2,
1240 and the right-side line along y1 from x3 to x1.
1241 If the top-side line is also doubled, we shorten the y1 lines,
1242 so that the left-side line runs only to x1,
1243 and the right-side line only to x2.
1244 Otherwise, the horizontal line at y = y1 below would cut off
1245 the intersection, which looks ugly:
1247 y0 ________________________
1252 y1 |######### ########|
1255 y2 |######################|
1258 y3 |______________________|
1259 It is more of a judgment call when the horizontal line is
1260 single. We actually choose to cut off the line anyhow, as
1261 shown in the first diagram above.
1263 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
1264 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
1265 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
1266 int horz_line_ofs = double_vert ? double_line_ofs : 0;
1267 int xc = (x0 + x3) / 2;
1268 int x1 = xc - horz_line_ofs;
1269 int x2 = xc + horz_line_ofs;
1271 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
1272 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
1273 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
1274 int vert_line_ofs = double_horz ? double_line_ofs : 0;
1275 int yc = (y0 + y3) / 2;
1276 int y1 = yc - vert_line_ofs;
1277 int y2 = yc + vert_line_ofs;
1280 horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
1281 start_color, end_color, shorten_yc_line);
1284 horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
1285 start_color, end_color, shorten_y1_lines);
1286 horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
1287 start_color, end_color, shorten_y2_lines);
1291 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
1295 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
1297 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
1303 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
1304 int *min_width, int *max_width)
1306 struct xr_driver *xr = xr_;
1307 int bb[TABLE_N_AXES][2];
1308 int clip[TABLE_N_AXES][2];
1315 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1316 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
1319 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
1322 *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
1323 + cell->style->cell_style.margin[H][1]);
1325 *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
1326 + cell->style->cell_style.margin[H][1]);
1330 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
1332 struct xr_driver *xr = xr_;
1333 int bb[TABLE_N_AXES][2];
1334 int clip[TABLE_N_AXES][2];
1338 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1339 + cell->style->cell_style.margin[H][1]);
1342 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1343 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
1344 h += px_to_xr (cell->style->cell_style.margin[V][0]
1345 + cell->style->cell_style.margin[V][1]);
1349 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
1352 xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
1353 int bb[TABLE_N_AXES][2], int valign_offset,
1354 int spill[TABLE_N_AXES][2],
1355 int clip[TABLE_N_AXES][2])
1357 struct xr_driver *xr = xr_;
1360 if (!xr->transparent)
1362 cairo_save (xr->cairo);
1363 int bg_clip[TABLE_N_AXES][2];
1364 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1366 bg_clip[axis][0] = clip[axis][0];
1367 if (bb[axis][0] == clip[axis][0])
1368 bg_clip[axis][0] -= spill[axis][0];
1370 bg_clip[axis][1] = clip[axis][1];
1371 if (bb[axis][1] == clip[axis][1])
1372 bg_clip[axis][1] += spill[axis][1];
1374 xr_clip (xr, bg_clip);
1375 set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]);
1377 bb[H][0] - spill[H][0],
1378 bb[V][0] - spill[V][0],
1379 bb[H][1] + spill[H][1],
1380 bb[V][1] + spill[V][1]);
1381 cairo_restore (xr->cairo);
1383 cairo_save (xr->cairo);
1384 if (!xr->systemcolors)
1385 set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]);
1387 bb[V][0] += valign_offset;
1389 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1391 bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
1392 bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
1394 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
1395 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1396 cairo_restore (xr->cairo);
1400 xr_adjust_break (void *xr_, const struct table_cell *cell,
1401 int width, int height)
1403 struct xr_driver *xr = xr_;
1404 int bb[TABLE_N_AXES][2];
1405 int clip[TABLE_N_AXES][2];
1408 if (xr_measure_cell_height (xr_, cell, width) < height)
1412 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1413 + cell->style->cell_style.margin[H][1]);
1417 bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
1418 + cell->style->cell_style.margin[V][1]);
1419 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1420 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1425 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
1427 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1429 double x0 = xr_to_pt (clip[H][0] + xr->x);
1430 double y0 = xr_to_pt (clip[V][0] + xr->y);
1431 double x1 = xr_to_pt (clip[H][1] + xr->x);
1432 double y1 = xr_to_pt (clip[V][1] + xr->y);
1434 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1435 cairo_clip (xr->cairo);
1440 add_attr (PangoAttrList *list, PangoAttribute *attr,
1441 guint start_index, guint end_index)
1443 attr->start_index = start_index;
1444 attr->end_index = end_index;
1445 pango_attr_list_insert (list, attr);
1449 markup_escape (struct string *out, unsigned int options,
1450 const char *in, size_t len)
1452 if (!(options & TAB_MARKUP))
1454 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
1466 ds_put_cstr (out, "&");
1469 ds_put_cstr (out, "<");
1472 ds_put_cstr (out, ">");
1475 ds_put_byte (out, c);
1482 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
1484 int size[TABLE_N_AXES];
1485 pango_layout_get_size (layout, &size[H], &size[V]);
1490 xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
1491 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1492 int *widthp, int *brk)
1494 const struct font_style *font_style = &cell->style->font_style;
1495 const struct cell_style *cell_style = &cell->style->cell_style;
1496 unsigned int options = cell->options;
1498 enum table_axis X = options & TAB_ROTATE ? V : H;
1499 enum table_axis Y = !X;
1500 int R = options & TAB_ROTATE ? 0 : 1;
1502 struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1503 : &xr->fonts[XR_FONT_PROPORTIONAL]);
1504 struct xr_font local_font;
1505 if (font_style->typeface)
1507 PangoFontDescription *desc = parse_font (
1508 font_style->typeface,
1509 font_style->size ? font_style->size * 1000 * xr->font_scale : 10000,
1510 font_style->bold, font_style->italic);
1513 PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1514 pango_layout_set_font_description (layout, desc);
1516 local_font.desc = desc;
1517 local_font.layout = layout;
1522 const char *text = cell->text;
1523 enum table_halign halign = table_halign_interpret (
1524 cell_style->halign, cell->options & TAB_NUMERIC);
1525 if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
1527 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
1529 const char *decimal = strrchr (text, cell_style->decimal_char);
1532 pango_layout_set_text (font->layout, decimal, strlen (decimal));
1533 pango_layout_set_width (font->layout, -1);
1534 margin_adjustment += get_layout_dimension (font->layout, H);
1537 if (margin_adjustment < 0)
1538 bb[H][1] += margin_adjustment;
1541 struct string tmp = DS_EMPTY_INITIALIZER;
1542 PangoAttrList *attrs = NULL;
1544 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
1545 Pango's implementation of it): it will break after a period or a comma
1546 that precedes a digit, e.g. in ".000" it will break after the period.
1547 This code looks for such a situation and inserts a U+2060 WORD JOINER
1548 to prevent the break.
1550 This isn't necessary when the decimal point is between two digits
1551 (e.g. "0.000" won't be broken) or when the display width is not limited so
1552 that word wrapping won't happen.
1554 It isn't necessary to look for more than one period or comma, as would
1555 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
1556 are present then there will always be a digit on both sides of every
1557 period and comma. */
1558 if (options & TAB_MARKUP)
1560 PangoAttrList *new_attrs;
1562 if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
1565 tmp.ss = ss_cstr (new_text);
1566 tmp.capacity = tmp.ss.length;
1570 /* XXX should we report the error? */
1571 ds_put_cstr (&tmp, text);
1574 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
1576 const char *decimal = text + strcspn (text, ".,");
1578 && c_isdigit (decimal[1])
1579 && (decimal == text || !c_isdigit (decimal[-1])))
1581 ds_extend (&tmp, strlen (text) + 16);
1582 markup_escape (&tmp, options, text, decimal - text + 1);
1583 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
1584 markup_escape (&tmp, options, decimal + 1, -1);
1588 if (font_style->underline)
1591 attrs = pango_attr_list_new ();
1592 pango_attr_list_insert (attrs, pango_attr_underline_new (
1593 PANGO_UNDERLINE_SINGLE));
1596 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
1598 /* If we haven't already put TEXT into tmp, do it now. */
1599 if (ds_is_empty (&tmp))
1601 ds_extend (&tmp, strlen (text) + 16);
1602 markup_escape (&tmp, options, text, -1);
1605 size_t subscript_ofs = ds_length (&tmp);
1606 for (size_t i = 0; i < cell->n_subscripts; i++)
1609 ds_put_byte (&tmp, ',');
1610 ds_put_cstr (&tmp, cell->subscripts[i]);
1613 size_t superscript_ofs = ds_length (&tmp);
1614 if (cell->superscript)
1615 ds_put_cstr (&tmp, cell->superscript);
1617 size_t footnote_ofs = ds_length (&tmp);
1618 for (size_t i = 0; i < cell->n_footnotes; i++)
1621 ds_put_byte (&tmp, ',');
1622 ds_put_cstr (&tmp, cell->footnotes[i]->marker);
1625 /* Allow footnote markers to occupy the right margin. That way, numbers
1626 in the column are still aligned. */
1627 if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
1629 /* Measure the width of the footnote marker, so we know how much we
1630 need to make room for. */
1631 pango_layout_set_text (font->layout, ds_cstr (&tmp) + footnote_ofs,
1632 ds_length (&tmp) - footnote_ofs);
1634 PangoAttrList *fn_attrs = pango_attr_list_new ();
1635 pango_attr_list_insert (
1636 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
1637 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
1638 pango_layout_set_attributes (font->layout, fn_attrs);
1639 pango_attr_list_unref (fn_attrs);
1640 int footnote_width = get_layout_dimension (font->layout, X);
1642 /* Bound the adjustment by the width of the right margin. */
1643 int right_margin = px_to_xr (cell_style->margin[X][R]);
1644 int footnote_adjustment = MIN (footnote_width, right_margin);
1646 /* Adjust the bounding box. */
1647 if (options & TAB_ROTATE)
1648 footnote_adjustment = -footnote_adjustment;
1649 bb[X][R] += footnote_adjustment;
1652 pango_layout_set_attributes (font->layout, NULL);
1655 /* Set attributes. */
1657 attrs = pango_attr_list_new ();
1658 add_attr (attrs, pango_attr_font_desc_new (font->desc), subscript_ofs,
1659 PANGO_ATTR_INDEX_TO_TEXT_END);
1660 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
1661 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
1662 if (cell->n_subscripts)
1663 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
1664 superscript_ofs - subscript_ofs);
1665 if (cell->superscript || cell->n_footnotes)
1666 add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
1667 PANGO_ATTR_INDEX_TO_TEXT_END);
1670 /* Set the attributes, if any. */
1673 pango_layout_set_attributes (font->layout, attrs);
1674 pango_attr_list_unref (attrs);
1678 if (ds_is_empty (&tmp))
1679 pango_layout_set_text (font->layout, text, -1);
1681 pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
1684 pango_layout_set_alignment (font->layout,
1685 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
1686 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
1687 : PANGO_ALIGN_CENTER));
1688 pango_layout_set_width (
1690 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
1691 pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1693 if (clip[H][0] != clip[H][1])
1695 cairo_save (xr->cairo);
1696 if (!(options & TAB_ROTATE))
1698 if (options & TAB_ROTATE)
1700 cairo_translate (xr->cairo,
1701 xr_to_pt (bb[H][0] + xr->x),
1702 xr_to_pt (bb[V][1] + xr->y));
1703 cairo_rotate (xr->cairo, -M_PI_2);
1706 cairo_translate (xr->cairo,
1707 xr_to_pt (bb[H][0] + xr->x),
1708 xr_to_pt (bb[V][0] + xr->y));
1709 pango_cairo_show_layout (xr->cairo, font->layout);
1711 /* If enabled, this draws a blue rectangle around the extents of each
1712 line of text, which can be rather useful for debugging layout
1716 PangoLayoutIter *iter;
1717 iter = pango_layout_get_iter (font->layout);
1720 PangoRectangle extents;
1722 pango_layout_iter_get_line_extents (iter, &extents, NULL);
1723 cairo_save (xr->cairo);
1724 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1726 pango_to_xr (extents.x) - xr->x,
1727 pango_to_xr (extents.y) - xr->y,
1728 pango_to_xr (extents.x + extents.width) - xr->x,
1729 pango_to_xr (extents.y + extents.height) - xr->y);
1730 cairo_restore (xr->cairo);
1732 while (pango_layout_iter_next_line (iter));
1733 pango_layout_iter_free (iter);
1736 cairo_restore (xr->cairo);
1739 int size[TABLE_N_AXES];
1740 pango_layout_get_size (font->layout, &size[H], &size[V]);
1741 int w = pango_to_xr (size[X]);
1742 int h = pango_to_xr (size[Y]);
1745 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
1747 PangoLayoutIter *iter;
1750 /* Choose a breakpoint between lines instead of in the middle of one. */
1751 iter = pango_layout_get_iter (font->layout);
1754 PangoRectangle extents;
1758 pango_layout_iter_get_line_extents (iter, NULL, &extents);
1759 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1760 extents.x = pango_to_xr (extents.x);
1761 extents.y = pango_to_xr (y0);
1762 extents.width = pango_to_xr (extents.width);
1763 extents.height = pango_to_xr (y1 - y0);
1764 bottom = bb[V][0] + extents.y + extents.height;
1765 if (bottom < bb[V][1])
1767 if (brk && clip[H][0] != clip[H][1])
1775 while (pango_layout_iter_next_line (iter));
1776 pango_layout_iter_free (iter);
1778 /* If enabled, draws a green line across the chosen breakpoint, which can
1779 be useful for debugging issues with breaking. */
1782 if (best && !xr->nest)
1783 dump_line (xr, -xr->left_margin, best,
1784 xr->width + xr->right_margin, best,
1786 &(struct cell_color) CELL_COLOR (0, 255, 0));
1790 pango_layout_set_attributes (font->layout, NULL);
1792 if (font == &local_font)
1794 g_object_unref (G_OBJECT (font->layout));
1795 pango_font_description_free (font->desc);
1802 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1803 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1804 int *width, int *height, int *brk)
1809 /* If enabled, draws a blue rectangle around the cell extents, which can be
1810 useful for debugging layout. */
1813 if (clip[H][0] != clip[H][1])
1815 int offset = (xr->nest) * XR_POINT;
1817 cairo_save (xr->cairo);
1818 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1820 bb[H][0] + offset, bb[V][0] + offset,
1821 bb[H][1] - offset, bb[V][1] - offset);
1822 cairo_restore (xr->cairo);
1828 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
1831 struct output_driver_factory pdf_driver_factory =
1832 { "pdf", "pspp.pdf", xr_pdf_create };
1833 struct output_driver_factory ps_driver_factory =
1834 { "ps", "pspp.ps", xr_ps_create };
1835 struct output_driver_factory svg_driver_factory =
1836 { "svg", "pspp.svg", xr_svg_create };
1838 static const struct output_driver_class cairo_driver_class =
1846 /* GUI rendering helpers. */
1850 struct output_item *item;
1853 struct render_pager *p;
1854 struct xr_driver *xr;
1857 #define CHART_WIDTH 500
1858 #define CHART_HEIGHT 375
1863 xr_driver_create (cairo_t *cairo, struct string_map *options)
1865 struct xr_driver *xr = xr_allocate ("cairo", 0, options, 1.0);
1866 xr_set_cairo (xr, cairo);
1870 /* Destroy XR, which should have been created with xr_driver_create(). Any
1871 cairo_t added to XR is not destroyed, because it is owned by the client. */
1873 xr_driver_destroy (struct xr_driver *xr)
1878 output_driver_destroy (&xr->driver);
1882 static struct xr_rendering *
1883 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1885 struct table_item *table_item;
1886 struct xr_rendering *r;
1888 table_item = table_item_create (table_from_string (text), NULL, NULL);
1889 r = xr_rendering_create (xr, &table_item->output_item, cr);
1890 table_item_unref (table_item);
1896 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1898 if (is_table_item (xr->item))
1899 apply_options (xr->xr, o);
1902 struct xr_rendering *
1903 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1906 struct xr_rendering *r = NULL;
1908 if (is_text_item (item))
1909 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1911 else if (is_message_item (item))
1913 const struct message_item *message_item = to_message_item (item);
1914 char *s = msg_to_string (message_item_get_msg (message_item));
1915 r = xr_rendering_create_text (xr, s, cr);
1918 else if (is_table_item (item))
1920 r = xzalloc (sizeof *r);
1921 r->item = output_item_ref (item);
1923 xr_set_cairo (xr, cr);
1924 r->p = render_pager_create (xr->params, to_table_item (item));
1926 else if (is_chart_item (item))
1928 r = xzalloc (sizeof *r);
1929 r->item = output_item_ref (item);
1931 else if (is_group_open_item (item))
1932 r = xr_rendering_create_text (xr, to_group_open_item (item)->command_name,
1939 xr_rendering_destroy (struct xr_rendering *r)
1943 output_item_unref (r->item);
1944 render_pager_destroy (r->p);
1950 xr_rendering_measure (const struct xr_rendering *r, int *wp, int *hp)
1954 if (is_table_item (r->item))
1956 w = render_pager_get_size (r->p, H) / XR_POINT;
1957 h = render_pager_get_size (r->p, V) / XR_POINT;
1971 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1972 double x, double y, double width, double height);
1976 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1977 int x0, int y0, int x1, int y1)
1979 if (is_table_item (r->item))
1981 struct xr_driver *xr = r->xr;
1983 xr_set_cairo (xr, cr);
1985 render_pager_draw_region (r->p, x0 * XR_POINT, y0 * XR_POINT,
1986 (x1 - x0) * XR_POINT, (y1 - y0) * XR_POINT);
1989 xr_draw_chart (to_chart_item (r->item), cr,
1990 0, 0, CHART_WIDTH, CHART_HEIGHT);
1994 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1995 double x, double y, double width, double height)
1997 struct xrchart_geometry geom;
2000 cairo_translate (cr, x, y + height);
2001 cairo_scale (cr, 1.0, -1.0);
2002 xrchart_geometry_init (cr, &geom, width, height);
2003 if (is_boxplot (chart_item))
2004 xrchart_draw_boxplot (chart_item, cr, &geom);
2005 else if (is_histogram_chart (chart_item))
2006 xrchart_draw_histogram (chart_item, cr, &geom);
2007 else if (is_np_plot_chart (chart_item))
2008 xrchart_draw_np_plot (chart_item, cr, &geom);
2009 else if (is_piechart (chart_item))
2010 xrchart_draw_piechart (chart_item, cr, &geom);
2011 else if (is_barchart (chart_item))
2012 xrchart_draw_barchart (chart_item, cr, &geom);
2013 else if (is_roc_chart (chart_item))
2014 xrchart_draw_roc (chart_item, cr, &geom);
2015 else if (is_scree (chart_item))
2016 xrchart_draw_scree (chart_item, cr, &geom);
2017 else if (is_spreadlevel_plot_chart (chart_item))
2018 xrchart_draw_spreadlevel (chart_item, cr, &geom);
2019 else if (is_scatterplot_chart (chart_item))
2020 xrchart_draw_scatterplot (chart_item, cr, &geom);
2023 xrchart_geometry_free (cr, &geom);
2029 xr_draw_png_chart (const struct chart_item *item,
2030 const char *file_name_template, int number,
2031 const struct cell_color *fg,
2032 const struct cell_color *bg)
2034 const int width = 640;
2035 const int length = 480;
2037 cairo_surface_t *surface;
2038 cairo_status_t status;
2039 const char *number_pos;
2043 number_pos = strchr (file_name_template, '#');
2044 if (number_pos != NULL)
2045 file_name = xasprintf ("%.*s%d%s.png", (int) (number_pos - file_name_template),
2046 file_name_template, number, number_pos + 1);
2048 file_name = xasprintf ("%s.png", file_name_template);
2050 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
2051 cr = cairo_create (surface);
2053 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
2056 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
2058 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
2060 status = cairo_surface_write_to_png (surface, file_name);
2061 if (status != CAIRO_STATUS_SUCCESS)
2062 msg (ME, _("error writing output file `%s': %s"),
2063 file_name, cairo_status_to_string (status));
2066 cairo_surface_destroy (surface);
2073 xr_draw_eps_chart (const struct chart_item *item,
2074 const char *file_name_template, int number,
2075 const struct cell_color *fg,
2076 const struct cell_color *bg)
2078 const int width = 640;
2079 const int length = 480;
2081 cairo_surface_t *surface;
2082 const char *number_pos;
2086 number_pos = strchr (file_name_template, '#');
2087 if (number_pos != NULL)
2088 file_name = xasprintf ("%.*s%d%s.eps", (int) (number_pos - file_name_template),
2089 file_name_template, number, number_pos + 1);
2091 file_name = xasprintf ("%s.eps", file_name_template);
2093 surface = cairo_ps_surface_create (file_name, width, length);
2094 cairo_ps_surface_set_eps (surface, true);
2095 cr = cairo_create (surface);
2097 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
2100 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
2102 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
2105 cairo_surface_destroy (surface);
2112 struct xr_table_state
2114 struct xr_render_fsm fsm;
2115 struct render_pager *p;
2119 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
2121 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
2123 while (render_pager_has_next (ts->p))
2127 used = render_pager_draw_next (ts->p, xr->length - xr->y);
2140 xr_table_destroy (struct xr_render_fsm *fsm)
2142 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
2144 render_pager_destroy (ts->p);
2148 static struct xr_render_fsm *
2149 xr_render_table (struct xr_driver *xr, struct table_item *table_item)
2151 struct xr_table_state *ts;
2153 ts = xmalloc (sizeof *ts);
2154 ts->fsm.render = xr_table_render;
2155 ts->fsm.destroy = xr_table_destroy;
2158 xr->y += xr->char_height;
2160 ts->p = render_pager_create (xr->params, table_item);
2161 table_item_unref (table_item);
2166 struct xr_chart_state
2168 struct xr_render_fsm fsm;
2169 struct chart_item *chart_item;
2173 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
2175 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
2177 const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
2179 if (xr->y > xr->length - chart_height)
2182 if (xr->cairo != NULL)
2184 xr_draw_chart (cs->chart_item, xr->cairo,
2187 xr_to_pt (xr->width),
2188 xr_to_pt (chart_height));
2190 xr->y += chart_height;
2196 xr_chart_destroy (struct xr_render_fsm *fsm)
2198 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
2200 chart_item_unref (cs->chart_item);
2204 static struct xr_render_fsm *
2205 xr_render_chart (const struct chart_item *chart_item)
2207 struct xr_chart_state *cs;
2209 cs = xmalloc (sizeof *cs);
2210 cs->fsm.render = xr_chart_render;
2211 cs->fsm.destroy = xr_chart_destroy;
2212 cs->chart_item = chart_item_ref (chart_item);
2218 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
2224 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
2226 /* Nothing to do. */
2229 static struct xr_render_fsm *
2230 xr_render_eject (void)
2232 static struct xr_render_fsm eject_renderer =
2238 return &eject_renderer;
2241 static struct xr_render_fsm *
2242 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
2244 enum text_item_type type = text_item_get_type (text_item);
2245 const char *text = text_item_get_text (text_item);
2249 case TEXT_ITEM_PAGE_TITLE:
2250 string_map_replace (&xr->heading_vars, "PageTitle", text);
2253 case TEXT_ITEM_EJECT_PAGE:
2255 return xr_render_eject ();
2259 return xr_render_table (
2260 xr, text_item_to_table_item (text_item_ref (text_item)));
2266 static struct xr_render_fsm *
2267 xr_render_message (struct xr_driver *xr,
2268 const struct message_item *message_item)
2270 char *s = msg_to_string (message_item_get_msg (message_item));
2271 struct text_item *item = text_item_create (TEXT_ITEM_LOG, s);
2273 return xr_render_table (xr, text_item_to_table_item (item));
2276 static struct xr_render_fsm *
2277 xr_render_output_item (struct xr_driver *xr,
2278 const struct output_item *output_item)
2280 if (is_table_item (output_item))
2281 return xr_render_table (xr, table_item_ref (to_table_item (output_item)));
2282 else if (is_chart_item (output_item))
2283 return xr_render_chart (to_chart_item (output_item));
2284 else if (is_text_item (output_item))
2285 return xr_render_text (xr, to_text_item (output_item));
2286 else if (is_message_item (output_item))
2287 return xr_render_message (xr, to_message_item (output_item));
2293 xr_draw_svg_file (struct xr_rendering *r,
2294 const char *filename)
2298 xr_rendering_measure (r, &width, &height);
2299 cairo_surface_t *surface = cairo_svg_surface_create (filename, width, height);
2302 g_error ("Could not create cairo svg surface with file %s", filename);
2305 cairo_t *cr = cairo_create (surface);
2306 xr_rendering_draw (r, cr, 0, 0, width, height);
2308 cairo_surface_destroy (surface);