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],
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],
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 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1397 bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
1398 bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
1400 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
1401 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1402 cairo_restore (xr->cairo);
1406 xr_adjust_break (void *xr_, const struct table_cell *cell,
1407 int width, int height)
1409 struct xr_driver *xr = xr_;
1410 int bb[TABLE_N_AXES][2];
1411 int clip[TABLE_N_AXES][2];
1414 if (xr_measure_cell_height (xr_, cell, width) < height)
1418 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1419 + cell->style->cell_style.margin[H][1]);
1423 bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
1424 + cell->style->cell_style.margin[V][1]);
1425 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1426 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1431 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
1433 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1435 double x0 = xr_to_pt (clip[H][0] + xr->x);
1436 double y0 = xr_to_pt (clip[V][0] + xr->y);
1437 double x1 = xr_to_pt (clip[H][1] + xr->x);
1438 double y1 = xr_to_pt (clip[V][1] + xr->y);
1440 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1441 cairo_clip (xr->cairo);
1446 add_attr (PangoAttrList *list, PangoAttribute *attr,
1447 guint start_index, guint end_index)
1449 attr->start_index = start_index;
1450 attr->end_index = end_index;
1451 pango_attr_list_insert (list, attr);
1455 markup_escape (struct string *out, unsigned int options,
1456 const char *in, size_t len)
1458 if (!(options & TAB_MARKUP))
1460 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
1472 ds_put_cstr (out, "&");
1475 ds_put_cstr (out, "<");
1478 ds_put_cstr (out, ">");
1481 ds_put_byte (out, c);
1488 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
1490 int size[TABLE_N_AXES];
1491 pango_layout_get_size (layout, &size[H], &size[V]);
1496 xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
1497 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1498 int *widthp, int *brk)
1500 const struct font_style *font_style = &cell->style->font_style;
1501 const struct cell_style *cell_style = &cell->style->cell_style;
1502 unsigned int options = cell->options;
1504 enum table_axis X = options & TAB_ROTATE ? V : H;
1505 enum table_axis Y = !X;
1506 int R = options & TAB_ROTATE ? 0 : 1;
1508 struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1509 : &xr->fonts[XR_FONT_PROPORTIONAL]);
1510 struct xr_font local_font;
1511 if (font_style->typeface)
1513 PangoFontDescription *desc = parse_font (
1514 font_style->typeface,
1515 font_style->size ? font_style->size * 1000 * xr->font_scale : 10000,
1516 font_style->bold, font_style->italic);
1519 PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1520 pango_layout_set_font_description (layout, desc);
1522 local_font.desc = desc;
1523 local_font.layout = layout;
1528 const char *text = cell->text;
1529 enum table_halign halign = table_halign_interpret (
1530 cell_style->halign, cell->options & TAB_NUMERIC);
1531 if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
1533 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
1535 const char *decimal = strrchr (text, cell_style->decimal_char);
1538 pango_layout_set_text (font->layout, decimal, strlen (decimal));
1539 pango_layout_set_width (font->layout, -1);
1540 margin_adjustment += get_layout_dimension (font->layout, H);
1543 if (margin_adjustment < 0)
1544 bb[H][1] += margin_adjustment;
1547 struct string tmp = DS_EMPTY_INITIALIZER;
1548 PangoAttrList *attrs = NULL;
1550 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
1551 Pango's implementation of it): it will break after a period or a comma
1552 that precedes a digit, e.g. in ".000" it will break after the period.
1553 This code looks for such a situation and inserts a U+2060 WORD JOINER
1554 to prevent the break.
1556 This isn't necessary when the decimal point is between two digits
1557 (e.g. "0.000" won't be broken) or when the display width is not limited so
1558 that word wrapping won't happen.
1560 It isn't necessary to look for more than one period or comma, as would
1561 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
1562 are present then there will always be a digit on both sides of every
1563 period and comma. */
1564 if (options & TAB_MARKUP)
1566 PangoAttrList *new_attrs;
1568 if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
1571 tmp.ss = ss_cstr (new_text);
1572 tmp.capacity = tmp.ss.length;
1576 /* XXX should we report the error? */
1577 ds_put_cstr (&tmp, text);
1580 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
1582 const char *decimal = text + strcspn (text, ".,");
1584 && c_isdigit (decimal[1])
1585 && (decimal == text || !c_isdigit (decimal[-1])))
1587 ds_extend (&tmp, strlen (text) + 16);
1588 markup_escape (&tmp, options, text, decimal - text + 1);
1589 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
1590 markup_escape (&tmp, options, decimal + 1, -1);
1594 if (font_style->underline)
1597 attrs = pango_attr_list_new ();
1598 pango_attr_list_insert (attrs, pango_attr_underline_new (
1599 PANGO_UNDERLINE_SINGLE));
1602 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
1604 /* If we haven't already put TEXT into tmp, do it now. */
1605 if (ds_is_empty (&tmp))
1607 ds_extend (&tmp, strlen (text) + 16);
1608 markup_escape (&tmp, options, text, -1);
1611 size_t subscript_ofs = ds_length (&tmp);
1612 for (size_t i = 0; i < cell->n_subscripts; i++)
1615 ds_put_byte (&tmp, ',');
1616 ds_put_cstr (&tmp, cell->subscripts[i]);
1619 size_t superscript_ofs = ds_length (&tmp);
1620 if (cell->superscript)
1621 ds_put_cstr (&tmp, cell->superscript);
1623 size_t footnote_ofs = ds_length (&tmp);
1624 for (size_t i = 0; i < cell->n_footnotes; i++)
1627 ds_put_byte (&tmp, ',');
1628 ds_put_cstr (&tmp, cell->footnotes[i]->marker);
1631 /* Allow footnote markers to occupy the right margin. That way, numbers
1632 in the column are still aligned. */
1633 if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
1635 /* Measure the width of the footnote marker, so we know how much we
1636 need to make room for. */
1637 pango_layout_set_text (font->layout, ds_cstr (&tmp) + footnote_ofs,
1638 ds_length (&tmp) - footnote_ofs);
1640 PangoAttrList *fn_attrs = pango_attr_list_new ();
1641 pango_attr_list_insert (
1642 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
1643 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
1644 pango_layout_set_attributes (font->layout, fn_attrs);
1645 pango_attr_list_unref (fn_attrs);
1646 int footnote_width = get_layout_dimension (font->layout, X);
1648 /* Bound the adjustment by the width of the right margin. */
1649 int right_margin = px_to_xr (cell_style->margin[X][R]);
1650 int footnote_adjustment = MIN (footnote_width, right_margin);
1652 /* Adjust the bounding box. */
1653 if (options & TAB_ROTATE)
1654 footnote_adjustment = -footnote_adjustment;
1655 bb[X][R] += footnote_adjustment;
1658 pango_layout_set_attributes (font->layout, NULL);
1661 /* Set attributes. */
1663 attrs = pango_attr_list_new ();
1664 add_attr (attrs, pango_attr_font_desc_new (font->desc), subscript_ofs,
1665 PANGO_ATTR_INDEX_TO_TEXT_END);
1666 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
1667 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
1668 if (cell->n_subscripts)
1669 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
1670 superscript_ofs - subscript_ofs);
1671 if (cell->superscript || cell->n_footnotes)
1672 add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
1673 PANGO_ATTR_INDEX_TO_TEXT_END);
1676 /* Set the attributes, if any. */
1679 pango_layout_set_attributes (font->layout, attrs);
1680 pango_attr_list_unref (attrs);
1684 if (ds_is_empty (&tmp))
1685 pango_layout_set_text (font->layout, text, -1);
1687 pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
1690 pango_layout_set_alignment (font->layout,
1691 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
1692 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
1693 : PANGO_ALIGN_CENTER));
1694 pango_layout_set_width (
1696 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
1697 pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1699 if (clip[H][0] != clip[H][1])
1701 cairo_save (xr->cairo);
1702 if (!(options & TAB_ROTATE))
1704 if (options & TAB_ROTATE)
1706 cairo_translate (xr->cairo,
1707 xr_to_pt (bb[H][0] + xr->x),
1708 xr_to_pt (bb[V][1] + xr->y));
1709 cairo_rotate (xr->cairo, -M_PI_2);
1712 cairo_translate (xr->cairo,
1713 xr_to_pt (bb[H][0] + xr->x),
1714 xr_to_pt (bb[V][0] + xr->y));
1715 pango_cairo_show_layout (xr->cairo, font->layout);
1717 /* If enabled, this draws a blue rectangle around the extents of each
1718 line of text, which can be rather useful for debugging layout
1722 PangoLayoutIter *iter;
1723 iter = pango_layout_get_iter (font->layout);
1726 PangoRectangle extents;
1728 pango_layout_iter_get_line_extents (iter, &extents, NULL);
1729 cairo_save (xr->cairo);
1730 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1732 pango_to_xr (extents.x) - xr->x,
1733 pango_to_xr (extents.y) - xr->y,
1734 pango_to_xr (extents.x + extents.width) - xr->x,
1735 pango_to_xr (extents.y + extents.height) - xr->y);
1736 cairo_restore (xr->cairo);
1738 while (pango_layout_iter_next_line (iter));
1739 pango_layout_iter_free (iter);
1742 cairo_restore (xr->cairo);
1745 int size[TABLE_N_AXES];
1746 pango_layout_get_size (font->layout, &size[H], &size[V]);
1747 int w = pango_to_xr (size[X]);
1748 int h = pango_to_xr (size[Y]);
1751 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
1753 PangoLayoutIter *iter;
1756 /* Choose a breakpoint between lines instead of in the middle of one. */
1757 iter = pango_layout_get_iter (font->layout);
1760 PangoRectangle extents;
1764 pango_layout_iter_get_line_extents (iter, NULL, &extents);
1765 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1766 extents.x = pango_to_xr (extents.x);
1767 extents.y = pango_to_xr (y0);
1768 extents.width = pango_to_xr (extents.width);
1769 extents.height = pango_to_xr (y1 - y0);
1770 bottom = bb[V][0] + extents.y + extents.height;
1771 if (bottom < bb[V][1])
1773 if (brk && clip[H][0] != clip[H][1])
1781 while (pango_layout_iter_next_line (iter));
1782 pango_layout_iter_free (iter);
1784 /* If enabled, draws a green line across the chosen breakpoint, which can
1785 be useful for debugging issues with breaking. */
1788 if (best && !xr->nest)
1789 dump_line (xr, -xr->left_margin, best,
1790 xr->width + xr->right_margin, best,
1792 &(struct cell_color) CELL_COLOR (0, 255, 0));
1796 pango_layout_set_attributes (font->layout, NULL);
1798 if (font == &local_font)
1800 g_object_unref (G_OBJECT (font->layout));
1801 pango_font_description_free (font->desc);
1808 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1809 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1810 int *width, int *height, int *brk)
1815 /* If enabled, draws a blue rectangle around the cell extents, which can be
1816 useful for debugging layout. */
1819 if (clip[H][0] != clip[H][1])
1821 int offset = (xr->nest) * XR_POINT;
1823 cairo_save (xr->cairo);
1824 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1826 bb[H][0] + offset, bb[V][0] + offset,
1827 bb[H][1] - offset, bb[V][1] - offset);
1828 cairo_restore (xr->cairo);
1834 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
1837 struct output_driver_factory pdf_driver_factory =
1838 { "pdf", "pspp.pdf", xr_pdf_create };
1839 struct output_driver_factory ps_driver_factory =
1840 { "ps", "pspp.ps", xr_ps_create };
1841 struct output_driver_factory svg_driver_factory =
1842 { "svg", "pspp.svg", xr_svg_create };
1844 static const struct output_driver_class cairo_driver_class =
1852 /* GUI rendering helpers. */
1856 struct output_item *item;
1859 struct render_pager *p;
1860 struct xr_driver *xr;
1863 #define CHART_WIDTH 500
1864 #define CHART_HEIGHT 375
1869 xr_driver_create (cairo_t *cairo, struct string_map *options)
1871 struct xr_driver *xr = xr_allocate ("cairo", 0, options, 1.0);
1872 xr_set_cairo (xr, cairo);
1876 /* Destroy XR, which should have been created with xr_driver_create(). Any
1877 cairo_t added to XR is not destroyed, because it is owned by the client. */
1879 xr_driver_destroy (struct xr_driver *xr)
1884 output_driver_destroy (&xr->driver);
1888 static struct xr_rendering *
1889 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1891 struct table_item *table_item;
1892 struct xr_rendering *r;
1894 table_item = table_item_create (table_from_string (text), NULL, NULL);
1895 r = xr_rendering_create (xr, &table_item->output_item, cr);
1896 table_item_unref (table_item);
1902 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1904 if (is_table_item (xr->item))
1905 apply_options (xr->xr, o);
1908 struct xr_rendering *
1909 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1912 struct xr_rendering *r = NULL;
1914 if (is_text_item (item))
1915 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1917 else if (is_message_item (item))
1919 const struct message_item *message_item = to_message_item (item);
1920 char *s = msg_to_string (message_item_get_msg (message_item));
1921 r = xr_rendering_create_text (xr, s, cr);
1924 else if (is_table_item (item))
1926 r = xzalloc (sizeof *r);
1927 r->item = output_item_ref (item);
1929 xr_set_cairo (xr, cr);
1930 r->p = render_pager_create (xr->params, to_table_item (item));
1932 else if (is_chart_item (item))
1934 r = xzalloc (sizeof *r);
1935 r->item = output_item_ref (item);
1937 else if (is_group_open_item (item))
1938 r = xr_rendering_create_text (xr, to_group_open_item (item)->command_name,
1945 xr_rendering_destroy (struct xr_rendering *r)
1949 output_item_unref (r->item);
1950 render_pager_destroy (r->p);
1956 xr_rendering_measure (const struct xr_rendering *r, int *wp, int *hp)
1960 if (is_table_item (r->item))
1962 w = render_pager_get_size (r->p, H) / XR_POINT;
1963 h = render_pager_get_size (r->p, V) / XR_POINT;
1977 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1978 double x, double y, double width, double height);
1982 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1983 int x0, int y0, int x1, int y1)
1985 if (is_table_item (r->item))
1987 struct xr_driver *xr = r->xr;
1989 xr_set_cairo (xr, cr);
1991 render_pager_draw_region (r->p, x0 * XR_POINT, y0 * XR_POINT,
1992 (x1 - x0) * XR_POINT, (y1 - y0) * XR_POINT);
1995 xr_draw_chart (to_chart_item (r->item), cr,
1996 0, 0, CHART_WIDTH, CHART_HEIGHT);
2000 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
2001 double x, double y, double width, double height)
2003 struct xrchart_geometry geom;
2006 cairo_translate (cr, x, y + height);
2007 cairo_scale (cr, 1.0, -1.0);
2008 xrchart_geometry_init (cr, &geom, width, height);
2009 if (is_boxplot (chart_item))
2010 xrchart_draw_boxplot (chart_item, cr, &geom);
2011 else if (is_histogram_chart (chart_item))
2012 xrchart_draw_histogram (chart_item, cr, &geom);
2013 else if (is_np_plot_chart (chart_item))
2014 xrchart_draw_np_plot (chart_item, cr, &geom);
2015 else if (is_piechart (chart_item))
2016 xrchart_draw_piechart (chart_item, cr, &geom);
2017 else if (is_barchart (chart_item))
2018 xrchart_draw_barchart (chart_item, cr, &geom);
2019 else if (is_roc_chart (chart_item))
2020 xrchart_draw_roc (chart_item, cr, &geom);
2021 else if (is_scree (chart_item))
2022 xrchart_draw_scree (chart_item, cr, &geom);
2023 else if (is_spreadlevel_plot_chart (chart_item))
2024 xrchart_draw_spreadlevel (chart_item, cr, &geom);
2025 else if (is_scatterplot_chart (chart_item))
2026 xrchart_draw_scatterplot (chart_item, cr, &geom);
2029 xrchart_geometry_free (cr, &geom);
2035 xr_draw_png_chart (const struct chart_item *item,
2036 const char *file_name_template, int number,
2037 const struct cell_color *fg,
2038 const struct cell_color *bg)
2040 const int width = 640;
2041 const int length = 480;
2043 cairo_surface_t *surface;
2044 cairo_status_t status;
2045 const char *number_pos;
2049 number_pos = strchr (file_name_template, '#');
2050 if (number_pos != NULL)
2051 file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
2052 file_name_template, number, number_pos + 1);
2054 file_name = xstrdup (file_name_template);
2056 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
2057 cr = cairo_create (surface);
2059 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
2062 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
2064 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
2066 status = cairo_surface_write_to_png (surface, file_name);
2067 if (status != CAIRO_STATUS_SUCCESS)
2068 msg (ME, _("error writing output file `%s': %s"),
2069 file_name, cairo_status_to_string (status));
2072 cairo_surface_destroy (surface);
2077 struct xr_table_state
2079 struct xr_render_fsm fsm;
2080 struct render_pager *p;
2084 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
2086 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
2088 while (render_pager_has_next (ts->p))
2092 used = render_pager_draw_next (ts->p, xr->length - xr->y);
2105 xr_table_destroy (struct xr_render_fsm *fsm)
2107 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
2109 render_pager_destroy (ts->p);
2113 static struct xr_render_fsm *
2114 xr_render_table (struct xr_driver *xr, struct table_item *table_item)
2116 struct xr_table_state *ts;
2118 ts = xmalloc (sizeof *ts);
2119 ts->fsm.render = xr_table_render;
2120 ts->fsm.destroy = xr_table_destroy;
2123 xr->y += xr->char_height;
2125 ts->p = render_pager_create (xr->params, table_item);
2126 table_item_unref (table_item);
2131 struct xr_chart_state
2133 struct xr_render_fsm fsm;
2134 struct chart_item *chart_item;
2138 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
2140 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
2142 const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
2144 if (xr->y > xr->length - chart_height)
2147 if (xr->cairo != NULL)
2149 xr_draw_chart (cs->chart_item, xr->cairo,
2152 xr_to_pt (xr->width),
2153 xr_to_pt (chart_height));
2155 xr->y += chart_height;
2161 xr_chart_destroy (struct xr_render_fsm *fsm)
2163 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
2165 chart_item_unref (cs->chart_item);
2169 static struct xr_render_fsm *
2170 xr_render_chart (const struct chart_item *chart_item)
2172 struct xr_chart_state *cs;
2174 cs = xmalloc (sizeof *cs);
2175 cs->fsm.render = xr_chart_render;
2176 cs->fsm.destroy = xr_chart_destroy;
2177 cs->chart_item = chart_item_ref (chart_item);
2183 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
2189 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
2191 /* Nothing to do. */
2194 static struct xr_render_fsm *
2195 xr_render_eject (void)
2197 static struct xr_render_fsm eject_renderer =
2203 return &eject_renderer;
2206 static struct xr_render_fsm *
2207 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
2209 enum text_item_type type = text_item_get_type (text_item);
2210 const char *text = text_item_get_text (text_item);
2214 case TEXT_ITEM_PAGE_TITLE:
2215 string_map_replace (&xr->heading_vars, "PageTitle", text);
2218 case TEXT_ITEM_EJECT_PAGE:
2220 return xr_render_eject ();
2224 return xr_render_table (
2225 xr, text_item_to_table_item (text_item_ref (text_item)));
2231 static struct xr_render_fsm *
2232 xr_render_message (struct xr_driver *xr,
2233 const struct message_item *message_item)
2235 char *s = msg_to_string (message_item_get_msg (message_item));
2236 struct text_item *item = text_item_create (TEXT_ITEM_LOG, s);
2238 return xr_render_table (xr, text_item_to_table_item (item));
2241 static struct xr_render_fsm *
2242 xr_render_output_item (struct xr_driver *xr,
2243 const struct output_item *output_item)
2245 if (is_table_item (output_item))
2246 return xr_render_table (xr, table_item_ref (to_table_item (output_item)));
2247 else if (is_chart_item (output_item))
2248 return xr_render_chart (to_chart_item (output_item));
2249 else if (is_text_item (output_item))
2250 return xr_render_text (xr, to_text_item (output_item));
2251 else if (is_message_item (output_item))
2252 return xr_render_message (xr, to_message_item (output_item));