cairo: Remove 'x' from struct xr_driver.
[pspp] / src / output / cairo.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "output/cairo.h"
20
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"
51
52 #include <cairo/cairo-pdf.h>
53 #include <cairo/cairo-ps.h>
54 #include <cairo/cairo-svg.h>
55 #include <cairo/cairo.h>
56 #include <inttypes.h>
57 #include <math.h>
58 #include <pango/pango-font.h>
59 #include <pango/pango-layout.h>
60 #include <pango/pango.h>
61 #include <pango/pangocairo.h>
62 #include <stdlib.h>
63
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"
69
70 #include "gettext.h"
71 #define _(msgid) gettext (msgid)
72
73 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
74 #define H TABLE_HORZ
75 #define V TABLE_VERT
76
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
80
81 /* Conversions to and from points. */
82 static double
83 xr_to_pt (int x)
84 {
85   return x / (double) XR_POINT;
86 }
87
88 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
89 static int
90 px_to_xr (int x)
91 {
92   return x * (PANGO_SCALE * 72 / 96);
93 }
94
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. */
98
99 /* Output types. */
100 enum xr_output_type
101   {
102     XR_PDF,
103     XR_PS,
104     XR_SVG
105   };
106
107 /* Cairo fonts. */
108 enum xr_font_type
109   {
110     XR_FONT_PROPORTIONAL,
111     XR_FONT_EMPHASIS,
112     XR_FONT_FIXED,
113     XR_N_FONTS
114   };
115
116 /* A font for use with Cairo. */
117 struct xr_font
118   {
119     PangoFontDescription *desc;
120     PangoLayout *layout;
121   };
122
123 /* An output item whose rendering is in progress. */
124 struct xr_render_fsm
125   {
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
128        page. */
129     bool (*render) (struct xr_render_fsm *, struct xr_driver *);
130
131     /* Destroys the output item. */
132     void (*destroy) (struct xr_render_fsm *);
133   };
134
135 /* Cairo output driver. */
136 struct xr_driver
137   {
138     struct output_driver driver;
139
140     /* User parameters. */
141     struct xr_font fonts[XR_N_FONTS];
142
143     int width;                  /* Page width minus margins. */
144     int length;                 /* Page length minus margins and header. */
145
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). */
150
151     int line_space;             /* Space between lines. */
152     int line_width;             /* Width of lines. */
153
154     int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
155     int object_spacing;         /* Space between output objects. */
156
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     */
161
162     int initial_page_number;
163
164     struct page_heading headings[2]; /* Top and bottom headings. */
165     int headings_height[2];
166
167     /* Internal state. */
168     struct render_params *params;
169     double font_scale;
170     int char_width, char_height;
171     cairo_t *cairo;
172     cairo_surface_t *surface;
173     int page_number;            /* Current page number. */
174     int y;
175     struct xr_render_fsm *fsm;
176   };
177
178 static const struct output_driver_class cairo_driver_class;
179
180 static void xr_driver_destroy_fsm (struct xr_driver *);
181 static void xr_driver_run_fsm (struct xr_driver *);
182
183 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
184                           enum render_line_style styles[TABLE_N_AXES][2],
185                           struct cell_color colors[TABLE_N_AXES][2]);
186 static void xr_measure_cell_width (void *, const struct table_cell *,
187                                    int *min, int *max);
188 static int xr_measure_cell_height (void *, const struct table_cell *,
189                                    int width);
190 static void xr_draw_cell (void *, const struct table_cell *, int color_idx,
191                           int bb[TABLE_N_AXES][2], int valign_offset,
192                           int spill[TABLE_N_AXES][2],
193                           int clip[TABLE_N_AXES][2]);
194 static int xr_adjust_break (void *, const struct table_cell *,
195                             int width, int height);
196
197 static struct xr_render_fsm *xr_render_output_item (
198   struct xr_driver *, const struct output_item *);
199 \f
200 /* Output driver basics. */
201
202 static struct xr_driver *
203 xr_driver_cast (struct output_driver *driver)
204 {
205   assert (driver->class == &cairo_driver_class);
206   return UP_CAST (driver, struct xr_driver, driver);
207 }
208
209 static struct driver_option *
210 opt (struct output_driver *d, struct string_map *options, const char *key,
211      const char *default_value)
212 {
213   return driver_option_get (d, options, key, default_value);
214 }
215
216 static int
217 lookup_color_name (const char *s)
218 {
219   struct color
220     {
221       struct hmap_node hmap_node;
222       const char *name;
223       int code;
224     };
225
226   static struct color colors[] =
227     {
228       { .name = "aliceblue", .code = 0xf0f8ff },
229       { .name = "antiquewhite", .code = 0xfaebd7 },
230       { .name = "aqua", .code = 0x00ffff },
231       { .name = "aquamarine", .code = 0x7fffd4 },
232       { .name = "azure", .code = 0xf0ffff },
233       { .name = "beige", .code = 0xf5f5dc },
234       { .name = "bisque", .code = 0xffe4c4 },
235       { .name = "black", .code = 0x000000 },
236       { .name = "blanchedalmond", .code = 0xffebcd },
237       { .name = "blue", .code = 0x0000ff },
238       { .name = "blueviolet", .code = 0x8a2be2 },
239       { .name = "brown", .code = 0xa52a2a },
240       { .name = "burlywood", .code = 0xdeb887 },
241       { .name = "cadetblue", .code = 0x5f9ea0 },
242       { .name = "chartreuse", .code = 0x7fff00 },
243       { .name = "chocolate", .code = 0xd2691e },
244       { .name = "coral", .code = 0xff7f50 },
245       { .name = "cornflowerblue", .code = 0x6495ed },
246       { .name = "cornsilk", .code = 0xfff8dc },
247       { .name = "crimson", .code = 0xdc143c },
248       { .name = "cyan", .code = 0x00ffff },
249       { .name = "darkblue", .code = 0x00008b },
250       { .name = "darkcyan", .code = 0x008b8b },
251       { .name = "darkgoldenrod", .code = 0xb8860b },
252       { .name = "darkgray", .code = 0xa9a9a9 },
253       { .name = "darkgreen", .code = 0x006400 },
254       { .name = "darkgrey", .code = 0xa9a9a9 },
255       { .name = "darkkhaki", .code = 0xbdb76b },
256       { .name = "darkmagenta", .code = 0x8b008b },
257       { .name = "darkolivegreen", .code = 0x556b2f },
258       { .name = "darkorange", .code = 0xff8c00 },
259       { .name = "darkorchid", .code = 0x9932cc },
260       { .name = "darkred", .code = 0x8b0000 },
261       { .name = "darksalmon", .code = 0xe9967a },
262       { .name = "darkseagreen", .code = 0x8fbc8f },
263       { .name = "darkslateblue", .code = 0x483d8b },
264       { .name = "darkslategray", .code = 0x2f4f4f },
265       { .name = "darkslategrey", .code = 0x2f4f4f },
266       { .name = "darkturquoise", .code = 0x00ced1 },
267       { .name = "darkviolet", .code = 0x9400d3 },
268       { .name = "deeppink", .code = 0xff1493 },
269       { .name = "deepskyblue", .code = 0x00bfff },
270       { .name = "dimgray", .code = 0x696969 },
271       { .name = "dimgrey", .code = 0x696969 },
272       { .name = "dodgerblue", .code = 0x1e90ff },
273       { .name = "firebrick", .code = 0xb22222 },
274       { .name = "floralwhite", .code = 0xfffaf0 },
275       { .name = "forestgreen", .code = 0x228b22 },
276       { .name = "fuchsia", .code = 0xff00ff },
277       { .name = "gainsboro", .code = 0xdcdcdc },
278       { .name = "ghostwhite", .code = 0xf8f8ff },
279       { .name = "gold", .code = 0xffd700 },
280       { .name = "goldenrod", .code = 0xdaa520 },
281       { .name = "gray", .code = 0x808080 },
282       { .name = "green", .code = 0x008000 },
283       { .name = "greenyellow", .code = 0xadff2f },
284       { .name = "grey", .code = 0x808080 },
285       { .name = "honeydew", .code = 0xf0fff0 },
286       { .name = "hotpink", .code = 0xff69b4 },
287       { .name = "indianred", .code = 0xcd5c5c },
288       { .name = "indigo", .code = 0x4b0082 },
289       { .name = "ivory", .code = 0xfffff0 },
290       { .name = "khaki", .code = 0xf0e68c },
291       { .name = "lavender", .code = 0xe6e6fa },
292       { .name = "lavenderblush", .code = 0xfff0f5 },
293       { .name = "lawngreen", .code = 0x7cfc00 },
294       { .name = "lemonchiffon", .code = 0xfffacd },
295       { .name = "lightblue", .code = 0xadd8e6 },
296       { .name = "lightcoral", .code = 0xf08080 },
297       { .name = "lightcyan", .code = 0xe0ffff },
298       { .name = "lightgoldenrodyellow", .code = 0xfafad2 },
299       { .name = "lightgray", .code = 0xd3d3d3 },
300       { .name = "lightgreen", .code = 0x90ee90 },
301       { .name = "lightgrey", .code = 0xd3d3d3 },
302       { .name = "lightpink", .code = 0xffb6c1 },
303       { .name = "lightsalmon", .code = 0xffa07a },
304       { .name = "lightseagreen", .code = 0x20b2aa },
305       { .name = "lightskyblue", .code = 0x87cefa },
306       { .name = "lightslategray", .code = 0x778899 },
307       { .name = "lightslategrey", .code = 0x778899 },
308       { .name = "lightsteelblue", .code = 0xb0c4de },
309       { .name = "lightyellow", .code = 0xffffe0 },
310       { .name = "lime", .code = 0x00ff00 },
311       { .name = "limegreen", .code = 0x32cd32 },
312       { .name = "linen", .code = 0xfaf0e6 },
313       { .name = "magenta", .code = 0xff00ff },
314       { .name = "maroon", .code = 0x800000 },
315       { .name = "mediumaquamarine", .code = 0x66cdaa },
316       { .name = "mediumblue", .code = 0x0000cd },
317       { .name = "mediumorchid", .code = 0xba55d3 },
318       { .name = "mediumpurple", .code = 0x9370db },
319       { .name = "mediumseagreen", .code = 0x3cb371 },
320       { .name = "mediumslateblue", .code = 0x7b68ee },
321       { .name = "mediumspringgreen", .code = 0x00fa9a },
322       { .name = "mediumturquoise", .code = 0x48d1cc },
323       { .name = "mediumvioletred", .code = 0xc71585 },
324       { .name = "midnightblue", .code = 0x191970 },
325       { .name = "mintcream", .code = 0xf5fffa },
326       { .name = "mistyrose", .code = 0xffe4e1 },
327       { .name = "moccasin", .code = 0xffe4b5 },
328       { .name = "navajowhite", .code = 0xffdead },
329       { .name = "navy", .code = 0x000080 },
330       { .name = "oldlace", .code = 0xfdf5e6 },
331       { .name = "olive", .code = 0x808000 },
332       { .name = "olivedrab", .code = 0x6b8e23 },
333       { .name = "orange", .code = 0xffa500 },
334       { .name = "orangered", .code = 0xff4500 },
335       { .name = "orchid", .code = 0xda70d6 },
336       { .name = "palegoldenrod", .code = 0xeee8aa },
337       { .name = "palegreen", .code = 0x98fb98 },
338       { .name = "paleturquoise", .code = 0xafeeee },
339       { .name = "palevioletred", .code = 0xdb7093 },
340       { .name = "papayawhip", .code = 0xffefd5 },
341       { .name = "peachpuff", .code = 0xffdab9 },
342       { .name = "peru", .code = 0xcd853f },
343       { .name = "pink", .code = 0xffc0cb },
344       { .name = "plum", .code = 0xdda0dd },
345       { .name = "powderblue", .code = 0xb0e0e6 },
346       { .name = "purple", .code = 0x800080 },
347       { .name = "red", .code = 0xff0000 },
348       { .name = "rosybrown", .code = 0xbc8f8f },
349       { .name = "royalblue", .code = 0x4169e1 },
350       { .name = "saddlebrown", .code = 0x8b4513 },
351       { .name = "salmon", .code = 0xfa8072 },
352       { .name = "sandybrown", .code = 0xf4a460 },
353       { .name = "seagreen", .code = 0x2e8b57 },
354       { .name = "seashell", .code = 0xfff5ee },
355       { .name = "sienna", .code = 0xa0522d },
356       { .name = "silver", .code = 0xc0c0c0 },
357       { .name = "skyblue", .code = 0x87ceeb },
358       { .name = "slateblue", .code = 0x6a5acd },
359       { .name = "slategray", .code = 0x708090 },
360       { .name = "slategrey", .code = 0x708090 },
361       { .name = "snow", .code = 0xfffafa },
362       { .name = "springgreen", .code = 0x00ff7f },
363       { .name = "steelblue", .code = 0x4682b4 },
364       { .name = "tan", .code = 0xd2b48c },
365       { .name = "teal", .code = 0x008080 },
366       { .name = "thistle", .code = 0xd8bfd8 },
367       { .name = "tomato", .code = 0xff6347 },
368       { .name = "turquoise", .code = 0x40e0d0 },
369       { .name = "violet", .code = 0xee82ee },
370       { .name = "wheat", .code = 0xf5deb3 },
371       { .name = "white", .code = 0xffffff },
372       { .name = "whitesmoke", .code = 0xf5f5f5 },
373       { .name = "yellow", .code = 0xffff00 },
374       { .name = "yellowgreen", .code = 0x9acd32 },
375     };
376
377   static struct hmap color_table = HMAP_INITIALIZER (color_table);
378
379   if (hmap_is_empty (&color_table))
380     for (size_t i = 0; i < sizeof colors / sizeof *colors; i++)
381       hmap_insert (&color_table, &colors[i].hmap_node,
382                    hash_string (colors[i].name, 0));
383
384   const struct color *color;
385   HMAP_FOR_EACH_WITH_HASH (color, struct color, hmap_node,
386                            hash_string (s, 0), &color_table)
387     if (!strcmp (color->name, s))
388       return color->code;
389   return -1;
390 }
391
392 static bool
393 parse_color__ (const char *s, struct cell_color *color)
394 {
395   /* #rrrrggggbbbb */
396   uint16_t r16, g16, b16;
397   int len;
398   if (sscanf (s, "#%4"SCNx16"%4"SCNx16"%4"SCNx16"%n",
399               &r16, &g16, &b16, &len) == 3
400       && len == 13
401       && !s[len])
402     {
403       color->r = r16 >> 8;
404       color->g = g16 >> 8;
405       color->b = b16 >> 8;
406       return true;
407     }
408
409   /* #rrggbb */
410   uint8_t r, g, b;
411   if (sscanf (s, "#%2"SCNx8"%2"SCNx8"%2"SCNx8"%n", &r, &g, &b, &len) == 3
412       && len == 7
413       && !s[len])
414     {
415       color->r = r;
416       color->g = g;
417       color->b = b;
418       return true;
419     }
420
421   /* rrggbb */
422   if (sscanf (s, "%2"SCNx8"%2"SCNx8"%2"SCNx8"%n", &r, &g, &b, &len) == 3
423       && len == 6
424       && !s[len])
425     {
426       color->r = r;
427       color->g = g;
428       color->b = b;
429       return true;
430     }
431
432   /* rgb(r,g,b) */
433   if (sscanf (s, "rgb (%"SCNi8" , %"SCNi8" , %"SCNi8") %n",
434               &r, &g, &b, &len) == 3
435       && !s[len])
436     {
437       color->r = r;
438       color->g = g;
439       color->b = b;
440       return true;
441     }
442
443   /* rgba(r,g,b,a), ignoring a. */
444   if (sscanf (s, "rgba (%"SCNi8" , %"SCNi8" , %"SCNi8", %*f) %n",
445               &r, &g, &b, &len) == 3
446       && !s[len])
447     {
448       color->r = r;
449       color->g = g;
450       color->b = b;
451       return true;
452     }
453
454   int code = lookup_color_name (s);
455   if (code >= 0)
456     {
457       color->r = code >> 16;
458       color->g = code >> 8;
459       color->b = code;
460       return true;
461     }
462
463   return false;
464 }
465
466 /* Parse color information specified by KEY into {RED,GREEN,BLUE}.
467    Currently, the input string must be of the form "#RRRRGGGGBBBB"
468    Future implementations might allow things like "yellow" and
469    "sky-blue-ultra-brown"
470 */
471 void
472 parse_color (struct output_driver *d, struct string_map *options,
473              const char *key, const char *default_value,
474              struct cell_color *color)
475 {
476   char *string = parse_string (opt (d, options, key, default_value));
477   if (!parse_color__ (string, color) && !parse_color__ (default_value, color))
478     *color = (struct cell_color) CELL_COLOR_BLACK;
479   free (string);
480 }
481
482 static PangoFontDescription *
483 parse_font (const char *font, int default_size, bool bold, bool italic)
484 {
485   if (!c_strcasecmp (font, "Monospaced"))
486     font = "Monospace";
487
488   PangoFontDescription *desc = pango_font_description_from_string (font);
489   if (desc == NULL)
490     return NULL;
491
492   /* If the font description didn't include an explicit font size, then set it
493      to DEFAULT_SIZE, which is in inch/72000 units. */
494   if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
495     pango_font_description_set_size (desc,
496                                      (default_size / 1000.0) * PANGO_SCALE);
497
498   pango_font_description_set_weight (desc, (bold
499                                             ? PANGO_WEIGHT_BOLD
500                                             : PANGO_WEIGHT_NORMAL));
501   pango_font_description_set_style (desc, (italic
502                                            ? PANGO_STYLE_ITALIC
503                                            : PANGO_STYLE_NORMAL));
504
505   return desc;
506 }
507
508 static PangoFontDescription *
509 parse_font_option (struct output_driver *d, struct string_map *options,
510                    const char *key, const char *default_value,
511                    int default_size, bool bold, bool italic)
512 {
513   char *string = parse_string (opt (d, options, key, default_value));
514   PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
515   if (!desc)
516     {
517       msg (MW, _("`%s': bad font specification"), string);
518
519       /* Fall back to DEFAULT_VALUE, which had better be a valid font
520          description. */
521       desc = parse_font (default_value, default_size, bold, italic);
522       assert (desc != NULL);
523     }
524   free (string);
525
526   return desc;
527 }
528
529 static void
530 apply_options (struct xr_driver *xr, struct string_map *o)
531 {
532   struct output_driver *d = &xr->driver;
533
534   /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
535   int left_margin, right_margin;
536   int top_margin, bottom_margin;
537   int paper_width, paper_length;
538   int font_size;
539   int min_break[TABLE_N_AXES];
540
541   /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
542   const double scale = XR_POINT / 1000.;
543
544   int i;
545
546   for (i = 0; i < XR_N_FONTS; i++)
547     {
548       struct xr_font *font = &xr->fonts[i];
549
550       if (font->desc != NULL)
551         pango_font_description_free (font->desc);
552     }
553
554   font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
555   xr->fonts[XR_FONT_FIXED].desc = parse_font_option
556     (d, o, "fixed-font", "monospace", font_size, false, false);
557   xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font_option (
558     d, o, "prop-font", "sans serif", font_size, false, false);
559   xr->fonts[XR_FONT_EMPHASIS].desc = parse_font_option (
560     d, o, "emph-font", "sans serif", font_size, false, true);
561
562   parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg);
563   parse_color (d, o, "foreground-color", "#000000000000", &xr->fg);
564
565   xr->transparent = parse_boolean (opt (d, o, "transparent", "false"));
566   xr->systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
567
568   /* Get dimensions.  */
569   parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
570   left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
571   right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
572   top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
573   bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
574
575   min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
576   min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
577
578   int object_spacing = (parse_dimension (opt (d, o, "object-spacing", NULL))
579                         * scale);
580
581   /* Convert to inch/(XR_POINT * 72). */
582   xr->left_margin = left_margin * scale;
583   xr->right_margin = right_margin * scale;
584   xr->top_margin = top_margin * scale;
585   xr->bottom_margin = bottom_margin * scale;
586   xr->width = (paper_width - left_margin - right_margin) * scale;
587   xr->length = (paper_length - top_margin - bottom_margin) * scale;
588   xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
589   xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
590   xr->object_spacing = object_spacing >= 0 ? object_spacing : XR_POINT * 12;
591
592   /* There are no headings so headings_height can stay 0. */
593 }
594
595 static struct xr_driver *
596 xr_allocate (const char *name, int device_type, struct string_map *o,
597              double font_scale)
598 {
599   struct xr_driver *xr = xzalloc (sizeof *xr);
600   struct output_driver *d = &xr->driver;
601
602   output_driver_init (d, &cairo_driver_class, name, device_type);
603
604   /* This is a nasty kluge for an issue that does not make sense.  On any
605      surface other than a screen (e.g. for output to PDF or PS or SVG), the
606      fonts are way too big by default.  A "9-point" font seems to appear about
607      16 points tall.  We use a scale factor for these surfaces to help, but the
608      underlying issue is a mystery. */
609   xr->font_scale = font_scale;
610
611   apply_options (xr, o);
612
613   return xr;
614 }
615
616 static int
617 pango_to_xr (int pango)
618 {
619   return (XR_POINT != PANGO_SCALE
620           ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
621           : pango);
622 }
623
624 static int
625 xr_to_pango (int xr)
626 {
627   return (XR_POINT != PANGO_SCALE
628           ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
629           : xr);
630 }
631
632 static void
633 xr_measure_fonts (cairo_t *cairo, const struct xr_font fonts[XR_N_FONTS],
634                   int *char_width, int *char_height)
635 {
636   *char_width = 0;
637   *char_height = 0;
638   for (int i = 0; i < XR_N_FONTS; i++)
639     {
640       PangoLayout *layout = pango_cairo_create_layout (cairo);
641       pango_layout_set_font_description (layout, fonts[i].desc);
642
643       pango_layout_set_text (layout, "0", 1);
644
645       int cw, ch;
646       pango_layout_get_size (layout, &cw, &ch);
647       *char_width = MAX (*char_width, pango_to_xr (cw));
648       *char_height = MAX (*char_height, pango_to_xr (ch));
649
650       g_object_unref (G_OBJECT (layout));
651     }
652 }
653
654 static int
655 get_layout_height (PangoLayout *layout)
656 {
657   int w, h;
658   pango_layout_get_size (layout, &w, &h);
659   return h;
660 }
661
662 static int
663 xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
664                         const struct page_heading *ph, int page_number,
665                         int width, bool draw, int base_y)
666 {
667   PangoLayout *layout = pango_cairo_create_layout (cairo);
668   pango_layout_set_font_description (layout, font);
669
670   int y = 0;
671   for (size_t i = 0; i < ph->n; i++)
672     {
673       const struct page_paragraph *pp = &ph->paragraphs[i];
674
675       char *markup = output_driver_substitute_heading_vars (pp->markup,
676                                                             page_number);
677       pango_layout_set_markup (layout, markup, -1);
678       free (markup);
679
680       pango_layout_set_alignment (
681         layout,
682         (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
683          : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER
684          : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
685          : PANGO_ALIGN_RIGHT));
686       pango_layout_set_width (layout, xr_to_pango (width));
687       if (draw)
688         {
689           cairo_save (cairo);
690           cairo_translate (cairo, 0, xr_to_pt (y + base_y));
691           pango_cairo_show_layout (cairo, layout);
692           cairo_restore (cairo);
693         }
694
695       y += pango_to_xr (get_layout_height (layout));
696     }
697
698   g_object_unref (G_OBJECT (layout));
699
700   return y;
701 }
702
703 static int
704 xr_measure_headings (cairo_surface_t *surface,
705                      const PangoFontDescription *font,
706                      const struct page_heading headings[2],
707                      int width, int object_spacing, int height[2])
708 {
709   cairo_t *cairo = cairo_create (surface);
710   int total = 0;
711   for (int i = 0; i < 2; i++)
712     {
713       int h = xr_render_page_heading (cairo, font, &headings[i], -1,
714                                       width, false, 0);
715
716       /* If the top heading is nonempty, add some space below it. */
717       if (h && i == 0)
718         h += object_spacing;
719
720       if (height)
721         height[i] = h;
722       total += h;
723     }
724   cairo_destroy (cairo);
725   return total;
726 }
727
728 static bool
729 xr_check_fonts (cairo_surface_t *surface,
730                 const struct xr_font fonts[XR_N_FONTS],
731                 int usable_width, int usable_length)
732 {
733   cairo_t *cairo = cairo_create (surface);
734   int char_width, char_height;
735   xr_measure_fonts (cairo, fonts, &char_width, &char_height);
736   cairo_destroy (cairo);
737
738   bool ok = true;
739   enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
740   if (usable_width / char_width < MIN_WIDTH)
741     {
742       msg (ME, _("The defined page is not wide enough to hold at least %d "
743                  "characters in the default font.  In fact, there's only "
744                  "room for %d characters."),
745            MIN_WIDTH, usable_width / char_width);
746       ok = false;
747     }
748   if (usable_length / char_height < MIN_LENGTH)
749     {
750       msg (ME, _("The defined page is not long enough to hold at least %d "
751                  "lines in the default font.  In fact, there's only "
752                  "room for %d lines."),
753            MIN_LENGTH, usable_length / char_height);
754       ok = false;
755     }
756   return ok;
757 }
758
759 static void
760 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
761 {
762   xr->cairo = cairo;
763
764   cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
765
766   xr_measure_fonts (xr->cairo, xr->fonts, &xr->char_width, &xr->char_height);
767
768   for (int i = 0; i < XR_N_FONTS; i++)
769     {
770       struct xr_font *font = &xr->fonts[i];
771       font->layout = pango_cairo_create_layout (cairo);
772       pango_layout_set_font_description (font->layout, font->desc);
773     }
774
775   if (xr->params == NULL)
776     {
777       xr->params = xmalloc (sizeof *xr->params);
778       xr->params->draw_line = xr_draw_line;
779       xr->params->measure_cell_width = xr_measure_cell_width;
780       xr->params->measure_cell_height = xr_measure_cell_height;
781       xr->params->adjust_break = xr_adjust_break;
782       xr->params->draw_cell = xr_draw_cell;
783       xr->params->aux = xr;
784       xr->params->size[H] = xr->width;
785       xr->params->size[V] = xr->length;
786       xr->params->font_size[H] = xr->char_width;
787       xr->params->font_size[V] = xr->char_height;
788
789       int lw = XR_LINE_WIDTH;
790       int ls = XR_LINE_SPACE;
791       for (int i = 0; i < TABLE_N_AXES; i++)
792         {
793           xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
794           xr->params->line_widths[i][RENDER_LINE_SINGLE] = lw;
795           xr->params->line_widths[i][RENDER_LINE_DASHED] = lw;
796           xr->params->line_widths[i][RENDER_LINE_THICK] = lw * 2;
797           xr->params->line_widths[i][RENDER_LINE_THIN] = lw / 2;
798           xr->params->line_widths[i][RENDER_LINE_DOUBLE] = 2 * lw + ls;
799         }
800
801       for (int i = 0; i < TABLE_N_AXES; i++)
802         xr->params->min_break[i] = xr->min_break[i];
803       xr->params->supports_margins = true;
804       xr->params->rtl = render_direction_rtl ();
805     }
806
807   if (!xr->systemcolors)
808     cairo_set_source_rgb (xr->cairo,
809                           xr->fg.r / 255.0, xr->fg.g / 255.0, xr->fg.b / 255.0);
810 }
811
812 static struct output_driver *
813 xr_create (struct file_handle *fh, enum settings_output_devices device_type,
814            struct string_map *o, enum xr_output_type file_type)
815 {
816   const char *file_name = fh_get_file_name (fh);
817   struct xr_driver *xr = xr_allocate (file_name, device_type, o, 72.0 / 128.0);
818   double width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
819   double length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
820   if (file_type == XR_PDF)
821     xr->surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
822   else if (file_type == XR_PS)
823     xr->surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
824   else if (file_type == XR_SVG)
825     xr->surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
826   else
827     NOT_REACHED ();
828
829   cairo_status_t status = cairo_surface_status (xr->surface);
830   if (status != CAIRO_STATUS_SUCCESS)
831     {
832       msg (ME, _("error opening output file `%s': %s"),
833            file_name, cairo_status_to_string (status));
834       goto error;
835     }
836
837   if (!xr_check_fonts (xr->surface, xr->fonts, xr->width, xr->length))
838     goto error;
839
840   fh_unref (fh);
841   return &xr->driver;
842
843  error:
844   fh_unref (fh);
845   output_driver_destroy (&xr->driver);
846   return NULL;
847 }
848
849 static struct output_driver *
850 xr_pdf_create (struct  file_handle *fh, enum settings_output_devices device_type,
851                struct string_map *o)
852 {
853   return xr_create (fh, device_type, o, XR_PDF);
854 }
855
856 static struct output_driver *
857 xr_ps_create (struct  file_handle *fh, enum settings_output_devices device_type,
858                struct string_map *o)
859 {
860   return xr_create (fh, device_type, o, XR_PS);
861 }
862
863 static struct output_driver *
864 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
865                struct string_map *o)
866 {
867   return xr_create (fh, device_type, o, XR_SVG);
868 }
869
870 static void
871 xr_destroy (struct output_driver *driver)
872 {
873   struct xr_driver *xr = xr_driver_cast (driver);
874   size_t i;
875
876   xr_driver_destroy_fsm (xr);
877
878   if (xr->cairo != NULL)
879     {
880       cairo_surface_finish (xr->surface);
881       cairo_status_t status = cairo_status (xr->cairo);
882       if (status != CAIRO_STATUS_SUCCESS)
883         fprintf (stderr,  _("error drawing output for %s driver: %s"),
884                  output_driver_get_name (driver),
885                  cairo_status_to_string (status));
886       cairo_surface_destroy (xr->surface);
887
888       cairo_destroy (xr->cairo);
889     }
890
891   for (i = 0; i < XR_N_FONTS; i++)
892     {
893       struct xr_font *font = &xr->fonts[i];
894
895       if (font->desc != NULL)
896         pango_font_description_free (font->desc);
897       if (font->layout != NULL)
898         g_object_unref (font->layout);
899     }
900
901   free (xr->params);
902   free (xr);
903 }
904
905 static void
906 xr_flush (struct output_driver *driver)
907 {
908   struct xr_driver *xr = xr_driver_cast (driver);
909
910   cairo_surface_flush (cairo_get_target (xr->cairo));
911 }
912
913 static void
914 xr_update_page_setup (struct output_driver *driver,
915                       const struct page_setup *ps)
916 {
917   struct xr_driver *xr = xr_driver_cast (driver);
918
919   xr->initial_page_number = ps->initial_page_number;
920   xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
921
922   if (xr->cairo)
923     return;
924
925   int usable[TABLE_N_AXES];
926   for (int i = 0; i < 2; i++)
927     usable[i] = (ps->paper[i]
928                  - (ps->margins[i][0] + ps->margins[i][1])) * 72 * XR_POINT;
929
930   int headings_height[2];
931   usable[V] -= xr_measure_headings (
932     xr->surface, xr->fonts[XR_FONT_PROPORTIONAL].desc, ps->headings,
933     usable[H], xr->object_spacing, headings_height);
934
935   enum table_axis h = ps->orientation == PAGE_LANDSCAPE;
936   enum table_axis v = !h;
937   if (!xr_check_fonts (xr->surface, xr->fonts, usable[h], usable[v]))
938     return;
939
940   for (int i = 0; i < 2; i++)
941     {
942       page_heading_uninit (&xr->headings[i]);
943       page_heading_copy (&xr->headings[i], &ps->headings[i]);
944       xr->headings_height[i] = headings_height[i];
945     }
946   xr->width = usable[h];
947   xr->length = usable[v];
948   xr->left_margin = ps->margins[h][0] * 72 * XR_POINT;
949   xr->right_margin = ps->margins[h][1] * 72 * XR_POINT;
950   xr->top_margin = ps->margins[v][0] * 72 * XR_POINT;
951   xr->bottom_margin = ps->margins[v][1] * 72 * XR_POINT;
952   cairo_pdf_surface_set_size (xr->surface,
953                               ps->paper[h] * 72.0, ps->paper[v] * 72.0);
954 }
955
956 static void
957 xr_submit (struct output_driver *driver, const struct output_item *output_item)
958 {
959   struct xr_driver *xr = xr_driver_cast (driver);
960
961   if (is_page_setup_item (output_item))
962     {
963       xr_update_page_setup (driver,
964                             to_page_setup_item (output_item)->page_setup);
965       return;
966     }
967
968   if (!xr->cairo)
969     {
970       xr->page_number = xr->initial_page_number - 1;
971       xr_set_cairo (xr, cairo_create (xr->surface));
972       cairo_save (xr->cairo);
973       xr_driver_next_page (xr, xr->cairo);
974     }
975
976   xr_driver_output_item (xr, output_item);
977   while (xr_driver_need_new_page (xr))
978     {
979       cairo_restore (xr->cairo);
980       cairo_show_page (xr->cairo);
981       cairo_save (xr->cairo);
982       xr_driver_next_page (xr, xr->cairo);
983     }
984 }
985 \f
986 /* Functions for rendering a series of output items to a series of Cairo
987    contexts, with pagination.
988
989    Used by PSPPIRE for printing, and by the basic Cairo output driver above as
990    its underlying implementation.
991
992    See the big comment in cairo.h for intended usage. */
993
994 /* Gives new page CAIRO to XR for output. */
995 void
996 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
997 {
998   if (!xr->transparent)
999     {
1000       cairo_save (cairo);
1001       cairo_set_source_rgb (cairo,
1002                             xr->bg.r / 255.0, xr->bg.g / 255.0, xr->bg.b / 255.0);
1003       cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
1004       cairo_fill (cairo);
1005       cairo_restore (cairo);
1006     }
1007   cairo_translate (cairo,
1008                    xr_to_pt (xr->left_margin),
1009                    xr_to_pt (xr->top_margin + xr->headings_height[0]));
1010
1011   xr->page_number++;
1012   xr->cairo = cairo;
1013   xr->y = 0;
1014
1015   xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
1016                           &xr->headings[0], xr->page_number, xr->width, true,
1017                           -xr->headings_height[0]);
1018   xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
1019                           &xr->headings[1], xr->page_number, xr->width, true,
1020                           xr->length);
1021
1022   xr_driver_run_fsm (xr);
1023 }
1024
1025 /* Start rendering OUTPUT_ITEM to XR.  Only valid if XR is not in the middle of
1026    rendering a previous output item, that is, only if xr_driver_need_new_page()
1027    returns false. */
1028 void
1029 xr_driver_output_item (struct xr_driver *xr,
1030                        const struct output_item *output_item)
1031 {
1032   assert (xr->fsm == NULL);
1033   xr->fsm = xr_render_output_item (xr, output_item);
1034   xr_driver_run_fsm (xr);
1035 }
1036
1037 /* Returns true if XR is in the middle of rendering an output item and needs a
1038    new page to be appended using xr_driver_next_page() to make progress,
1039    otherwise false. */
1040 bool
1041 xr_driver_need_new_page (const struct xr_driver *xr)
1042 {
1043   return xr->fsm != NULL;
1044 }
1045
1046 /* Returns true if the current page doesn't have any content yet. */
1047 bool
1048 xr_driver_is_page_blank (const struct xr_driver *xr)
1049 {
1050   return xr->y == 0;
1051 }
1052
1053 static void
1054 xr_driver_destroy_fsm (struct xr_driver *xr)
1055 {
1056   if (xr->fsm != NULL)
1057     {
1058       xr->fsm->destroy (xr->fsm);
1059       xr->fsm = NULL;
1060     }
1061 }
1062
1063 static void
1064 xr_driver_run_fsm (struct xr_driver *xr)
1065 {
1066   if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
1067     xr_driver_destroy_fsm (xr);
1068 }
1069 \f
1070 static void
1071 xr_layout_cell (struct xr_driver *, const struct table_cell *,
1072                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1073                 int *width, int *height, int *brk);
1074
1075 static void
1076 set_source_rgba (cairo_t *cairo, const struct cell_color *color)
1077 {
1078   cairo_set_source_rgba (cairo,
1079                          color->r / 255., color->g / 255., color->b / 255.,
1080                          color->alpha / 255.);
1081 }
1082
1083 static void
1084 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
1085            const struct cell_color *color)
1086 {
1087   cairo_new_path (xr->cairo);
1088   if (!xr->systemcolors)
1089     set_source_rgba (xr->cairo, color);
1090   cairo_set_line_width (
1091     xr->cairo,
1092     xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
1093               : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
1094               : XR_LINE_WIDTH));
1095   cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
1096   cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
1097   cairo_stroke (xr->cairo);
1098 }
1099
1100 static void UNUSED
1101 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
1102 {
1103   cairo_new_path (xr->cairo);
1104   cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
1105   cairo_close_path (xr->cairo);
1106   cairo_stroke (xr->cairo);
1107   cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
1108   cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y0 + xr->y));
1109   cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
1110   cairo_line_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y1 + xr->y));
1111 }
1112
1113 static void
1114 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
1115 {
1116   cairo_new_path (xr->cairo);
1117   cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
1118   cairo_rectangle (xr->cairo,
1119                    xr_to_pt (x0), xr_to_pt (y0 + xr->y),
1120                    xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
1121   cairo_fill (xr->cairo);
1122 }
1123
1124 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
1125    shortening it to X0...X1 if SHORTEN is true.
1126    Draws a horizontal line X1...X3 at Y if RIGHT says so,
1127    shortening it to X2...X3 if SHORTEN is true. */
1128 static void
1129 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
1130            enum render_line_style left, enum render_line_style right,
1131            const struct cell_color *left_color,
1132            const struct cell_color *right_color,
1133            bool shorten)
1134 {
1135   if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
1136       && cell_color_equal (left_color, right_color))
1137     dump_line (xr, x0, y, x3, y, left, left_color);
1138   else
1139     {
1140       if (left != RENDER_LINE_NONE)
1141         dump_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
1142       if (right != RENDER_LINE_NONE)
1143         dump_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
1144     }
1145 }
1146
1147 /* Draws a vertical line Y0...Y2 at X if TOP says so,
1148    shortening it to Y0...Y1 if SHORTEN is true.
1149    Draws a vertical line Y1...Y3 at X if BOTTOM says so,
1150    shortening it to Y2...Y3 if SHORTEN is true. */
1151 static void
1152 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
1153            enum render_line_style top, enum render_line_style bottom,
1154            const struct cell_color *top_color,
1155            const struct cell_color *bottom_color,
1156            bool shorten)
1157 {
1158   if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
1159       && cell_color_equal (top_color, bottom_color))
1160     dump_line (xr, x, y0, x, y3, top, top_color);
1161   else
1162     {
1163       if (top != RENDER_LINE_NONE)
1164         dump_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
1165       if (bottom != RENDER_LINE_NONE)
1166         dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
1167     }
1168 }
1169
1170 static void
1171 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
1172               enum render_line_style styles[TABLE_N_AXES][2],
1173               struct cell_color colors[TABLE_N_AXES][2])
1174 {
1175   const int x0 = bb[H][0];
1176   const int y0 = bb[V][0];
1177   const int x3 = bb[H][1];
1178   const int y3 = bb[V][1];
1179   const int top = styles[H][0];
1180   const int bottom = styles[H][1];
1181
1182   int start_side = render_direction_rtl();
1183   int end_side = !start_side;
1184   const int start_of_line = styles[V][start_side];
1185   const int end_of_line   = styles[V][end_side];
1186   const struct cell_color *top_color = &colors[H][0];
1187   const struct cell_color *bottom_color = &colors[H][1];
1188   const struct cell_color *start_color = &colors[V][start_side];
1189   const struct cell_color *end_color = &colors[V][end_side];
1190
1191   /* The algorithm here is somewhat subtle, to allow it to handle
1192      all the kinds of intersections that we need.
1193
1194      Three additional ordinates are assigned along the x axis.  The
1195      first is xc, midway between x0 and x3.  The others are x1 and
1196      x2; for a single vertical line these are equal to xc, and for
1197      a double vertical line they are the ordinates of the left and
1198      right half of the double line.
1199
1200      yc, y1, and y2 are assigned similarly along the y axis.
1201
1202      The following diagram shows the coordinate system and output
1203      for double top and bottom lines, single left line, and no
1204      right line:
1205
1206                  x0       x1 xc  x2      x3
1207                y0 ________________________
1208                   |        #     #       |
1209                   |        #     #       |
1210                   |        #     #       |
1211                   |        #     #       |
1212                   |        #     #       |
1213      y1 = y2 = yc |#########     #       |
1214                   |        #     #       |
1215                   |        #     #       |
1216                   |        #     #       |
1217                   |        #     #       |
1218                y3 |________#_____#_______|
1219   */
1220   struct xr_driver *xr = xr_;
1221
1222   /* Offset from center of each line in a pair of double lines. */
1223   int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
1224
1225   /* Are the lines along each axis single or double?
1226      (It doesn't make sense to have different kinds of line on the
1227      same axis, so we don't try to gracefully handle that case.) */
1228   bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
1229   bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
1230
1231   /* When horizontal lines are doubled,
1232      the left-side line along y1 normally runs from x0 to x2,
1233      and the right-side line along y1 from x3 to x1.
1234      If the top-side line is also doubled, we shorten the y1 lines,
1235      so that the left-side line runs only to x1,
1236      and the right-side line only to x2.
1237      Otherwise, the horizontal line at y = y1 below would cut off
1238      the intersection, which looks ugly:
1239                x0       x1     x2      x3
1240              y0 ________________________
1241                 |        #     #       |
1242                 |        #     #       |
1243                 |        #     #       |
1244                 |        #     #       |
1245              y1 |#########     ########|
1246                 |                      |
1247                 |                      |
1248              y2 |######################|
1249                 |                      |
1250                 |                      |
1251              y3 |______________________|
1252      It is more of a judgment call when the horizontal line is
1253      single.  We actually choose to cut off the line anyhow, as
1254      shown in the first diagram above.
1255   */
1256   bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
1257   bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
1258   bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
1259   int horz_line_ofs = double_vert ? double_line_ofs : 0;
1260   int xc = (x0 + x3) / 2;
1261   int x1 = xc - horz_line_ofs;
1262   int x2 = xc + horz_line_ofs;
1263
1264   bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
1265   bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
1266   bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
1267   int vert_line_ofs = double_horz ? double_line_ofs : 0;
1268   int yc = (y0 + y3) / 2;
1269   int y1 = yc - vert_line_ofs;
1270   int y2 = yc + vert_line_ofs;
1271
1272   if (!double_horz)
1273     horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
1274                start_color, end_color, shorten_yc_line);
1275   else
1276     {
1277       horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
1278                  start_color, end_color, shorten_y1_lines);
1279       horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
1280                  start_color, end_color, shorten_y2_lines);
1281     }
1282
1283   if (!double_vert)
1284     vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
1285                shorten_xc_line);
1286   else
1287     {
1288       vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
1289                  shorten_x1_lines);
1290       vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
1291                  shorten_x2_lines);
1292     }
1293 }
1294
1295 static void
1296 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
1297                        int *min_width, int *max_width)
1298 {
1299   struct xr_driver *xr = xr_;
1300   int bb[TABLE_N_AXES][2];
1301   int clip[TABLE_N_AXES][2];
1302   int h;
1303
1304   bb[H][0] = 0;
1305   bb[H][1] = INT_MAX;
1306   bb[V][0] = 0;
1307   bb[V][1] = INT_MAX;
1308   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1309   xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
1310
1311   bb[H][1] = 1;
1312   xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
1313
1314   if (*min_width > 0)
1315     *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
1316                             + cell->style->cell_style.margin[H][1]);
1317   if (*max_width > 0)
1318     *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
1319                             + cell->style->cell_style.margin[H][1]);
1320 }
1321
1322 static int
1323 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
1324 {
1325   struct xr_driver *xr = xr_;
1326   int bb[TABLE_N_AXES][2];
1327   int clip[TABLE_N_AXES][2];
1328   int w, h;
1329
1330   bb[H][0] = 0;
1331   bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1332                                + cell->style->cell_style.margin[H][1]);
1333   bb[V][0] = 0;
1334   bb[V][1] = INT_MAX;
1335   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1336   xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
1337   h += px_to_xr (cell->style->cell_style.margin[V][0]
1338                  + cell->style->cell_style.margin[V][1]);
1339   return h;
1340 }
1341
1342 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
1343
1344 static void
1345 xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
1346               int bb[TABLE_N_AXES][2], int valign_offset,
1347               int spill[TABLE_N_AXES][2],
1348               int clip[TABLE_N_AXES][2])
1349 {
1350   struct xr_driver *xr = xr_;
1351   int w, h, brk;
1352
1353   if (!xr->transparent)
1354     {
1355       cairo_save (xr->cairo);
1356       int bg_clip[TABLE_N_AXES][2];
1357       for (int axis = 0; axis < TABLE_N_AXES; axis++)
1358         {
1359           bg_clip[axis][0] = clip[axis][0];
1360           if (bb[axis][0] == clip[axis][0])
1361             bg_clip[axis][0] -= spill[axis][0];
1362
1363           bg_clip[axis][1] = clip[axis][1];
1364           if (bb[axis][1] == clip[axis][1])
1365             bg_clip[axis][1] += spill[axis][1];
1366         }
1367       xr_clip (xr, bg_clip);
1368       set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]);
1369       fill_rectangle (xr,
1370                       bb[H][0] - spill[H][0],
1371                       bb[V][0] - spill[V][0],
1372                       bb[H][1] + spill[H][1],
1373                       bb[V][1] + spill[V][1]);
1374       cairo_restore (xr->cairo);
1375     }
1376   cairo_save (xr->cairo);
1377   if (!xr->systemcolors)
1378     set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]);
1379
1380   bb[V][0] += valign_offset;
1381
1382   for (int axis = 0; axis < TABLE_N_AXES; axis++)
1383     {
1384       bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
1385       bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
1386     }
1387   if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
1388     xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1389   cairo_restore (xr->cairo);
1390 }
1391
1392 static int
1393 xr_adjust_break (void *xr_, const struct table_cell *cell,
1394                  int width, int height)
1395 {
1396   struct xr_driver *xr = xr_;
1397   int bb[TABLE_N_AXES][2];
1398   int clip[TABLE_N_AXES][2];
1399   int w, h, brk;
1400
1401   if (xr_measure_cell_height (xr_, cell, width) < height)
1402     return -1;
1403
1404   bb[H][0] = 0;
1405   bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1406                                + cell->style->cell_style.margin[H][1]);
1407   if (bb[H][1] <= 0)
1408     return 0;
1409   bb[V][0] = 0;
1410   bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
1411                                 + cell->style->cell_style.margin[V][1]);
1412   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1413   xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1414   return brk;
1415 }
1416 \f
1417 static void
1418 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
1419 {
1420   if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1421     {
1422       double x0 = xr_to_pt (clip[H][0]);
1423       double y0 = xr_to_pt (clip[V][0] + xr->y);
1424       double x1 = xr_to_pt (clip[H][1]);
1425       double y1 = xr_to_pt (clip[V][1] + xr->y);
1426
1427       cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1428       cairo_clip (xr->cairo);
1429     }
1430 }
1431
1432 static void
1433 add_attr (PangoAttrList *list, PangoAttribute *attr,
1434           guint start_index, guint end_index)
1435 {
1436   attr->start_index = start_index;
1437   attr->end_index = end_index;
1438   pango_attr_list_insert (list, attr);
1439 }
1440
1441 static void
1442 markup_escape (struct string *out, unsigned int options,
1443                const char *in, size_t len)
1444 {
1445   if (!(options & TAB_MARKUP))
1446     {
1447       ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
1448       return;
1449     }
1450
1451   while (len-- > 0)
1452     {
1453       int c = *in++;
1454       switch (c)
1455         {
1456         case 0:
1457           return;
1458         case '&':
1459           ds_put_cstr (out, "&amp;");
1460           break;
1461         case '<':
1462           ds_put_cstr (out, "&lt;");
1463           break;
1464         case '>':
1465           ds_put_cstr (out, "&gt;");
1466           break;
1467         default:
1468           ds_put_byte (out, c);
1469           break;
1470         }
1471     }
1472 }
1473
1474 static int
1475 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
1476 {
1477   int size[TABLE_N_AXES];
1478   pango_layout_get_size (layout, &size[H], &size[V]);
1479   return size[axis];
1480 }
1481
1482 static int
1483 xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
1484                      int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1485                      int *widthp, int *brk)
1486 {
1487   const struct font_style *font_style = &cell->style->font_style;
1488   const struct cell_style *cell_style = &cell->style->cell_style;
1489   unsigned int options = cell->options;
1490
1491   enum table_axis X = options & TAB_ROTATE ? V : H;
1492   enum table_axis Y = !X;
1493   int R = options & TAB_ROTATE ? 0 : 1;
1494
1495   struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1496                           : &xr->fonts[XR_FONT_PROPORTIONAL]);
1497   struct xr_font local_font;
1498   if (font_style->typeface)
1499     {
1500       PangoFontDescription *desc = parse_font (
1501         font_style->typeface,
1502         font_style->size ? font_style->size * 1000 * xr->font_scale : 10000,
1503         font_style->bold, font_style->italic);
1504       if (desc)
1505         {
1506           PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1507           pango_layout_set_font_description (layout, desc);
1508
1509           local_font.desc = desc;
1510           local_font.layout = layout;
1511           font = &local_font;
1512         }
1513     }
1514
1515   const char *text = cell->text;
1516   enum table_halign halign = table_halign_interpret (
1517     cell_style->halign, cell->options & TAB_NUMERIC);
1518   if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
1519     {
1520       int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
1521
1522       const char *decimal = strrchr (text, cell_style->decimal_char);
1523       if (decimal)
1524         {
1525           pango_layout_set_text (font->layout, decimal, strlen (decimal));
1526           pango_layout_set_width (font->layout, -1);
1527           margin_adjustment += get_layout_dimension (font->layout, H);
1528         }
1529
1530       if (margin_adjustment < 0)
1531         bb[H][1] += margin_adjustment;
1532     }
1533
1534   struct string tmp = DS_EMPTY_INITIALIZER;
1535   PangoAttrList *attrs = NULL;
1536
1537   /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
1538      Pango's implementation of it): it will break after a period or a comma
1539      that precedes a digit, e.g. in ".000" it will break after the period.
1540      This code looks for such a situation and inserts a U+2060 WORD JOINER
1541      to prevent the break.
1542
1543      This isn't necessary when the decimal point is between two digits
1544      (e.g. "0.000" won't be broken) or when the display width is not limited so
1545      that word wrapping won't happen.
1546
1547      It isn't necessary to look for more than one period or comma, as would
1548      happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
1549      are present then there will always be a digit on both sides of every
1550      period and comma. */
1551   if (options & TAB_MARKUP)
1552     {
1553       PangoAttrList *new_attrs;
1554       char *new_text;
1555       if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
1556         {
1557           attrs = new_attrs;
1558           tmp.ss = ss_cstr (new_text);
1559           tmp.capacity = tmp.ss.length;
1560         }
1561       else
1562         {
1563           /* XXX should we report the error? */
1564           ds_put_cstr (&tmp, text);
1565         }
1566     }
1567   else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
1568     {
1569       const char *decimal = text + strcspn (text, ".,");
1570       if (decimal[0]
1571           && c_isdigit (decimal[1])
1572           && (decimal == text || !c_isdigit (decimal[-1])))
1573         {
1574           ds_extend (&tmp, strlen (text) + 16);
1575           markup_escape (&tmp, options, text, decimal - text + 1);
1576           ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
1577           markup_escape (&tmp, options, decimal + 1, -1);
1578         }
1579     }
1580
1581   if (font_style->underline)
1582     {
1583       if (!attrs)
1584         attrs = pango_attr_list_new ();
1585       pango_attr_list_insert (attrs, pango_attr_underline_new (
1586                                 PANGO_UNDERLINE_SINGLE));
1587     }
1588
1589   if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
1590     {
1591       /* If we haven't already put TEXT into tmp, do it now. */
1592       if (ds_is_empty (&tmp))
1593         {
1594           ds_extend (&tmp, strlen (text) + 16);
1595           markup_escape (&tmp, options, text, -1);
1596         }
1597
1598       size_t subscript_ofs = ds_length (&tmp);
1599       for (size_t i = 0; i < cell->n_subscripts; i++)
1600         {
1601           if (i)
1602             ds_put_byte (&tmp, ',');
1603           ds_put_cstr (&tmp, cell->subscripts[i]);
1604         }
1605
1606       size_t superscript_ofs = ds_length (&tmp);
1607       if (cell->superscript)
1608         ds_put_cstr (&tmp, cell->superscript);
1609
1610       size_t footnote_ofs = ds_length (&tmp);
1611       for (size_t i = 0; i < cell->n_footnotes; i++)
1612         {
1613           if (i)
1614             ds_put_byte (&tmp, ',');
1615           ds_put_cstr (&tmp, cell->footnotes[i]->marker);
1616         }
1617
1618       /* Allow footnote markers to occupy the right margin.  That way, numbers
1619          in the column are still aligned. */
1620       if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
1621         {
1622           /* Measure the width of the footnote marker, so we know how much we
1623              need to make room for. */
1624           pango_layout_set_text (font->layout, ds_cstr (&tmp) + footnote_ofs,
1625                                  ds_length (&tmp) - footnote_ofs);
1626
1627           PangoAttrList *fn_attrs = pango_attr_list_new ();
1628           pango_attr_list_insert (
1629             fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
1630           pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
1631           pango_layout_set_attributes (font->layout, fn_attrs);
1632           pango_attr_list_unref (fn_attrs);
1633           int footnote_width = get_layout_dimension (font->layout, X);
1634
1635           /* Bound the adjustment by the width of the right margin. */
1636           int right_margin = px_to_xr (cell_style->margin[X][R]);
1637           int footnote_adjustment = MIN (footnote_width, right_margin);
1638
1639           /* Adjust the bounding box. */
1640           if (options & TAB_ROTATE)
1641             footnote_adjustment = -footnote_adjustment;
1642           bb[X][R] += footnote_adjustment;
1643
1644           /* Clean up. */
1645           pango_layout_set_attributes (font->layout, NULL);
1646         }
1647
1648       /* Set attributes. */
1649       if (!attrs)
1650         attrs = pango_attr_list_new ();
1651       add_attr (attrs, pango_attr_font_desc_new (font->desc), subscript_ofs,
1652                 PANGO_ATTR_INDEX_TO_TEXT_END);
1653       add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
1654                 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
1655       if (cell->n_subscripts)
1656         add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
1657                   superscript_ofs - subscript_ofs);
1658       if (cell->superscript || cell->n_footnotes)
1659         add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
1660                   PANGO_ATTR_INDEX_TO_TEXT_END);
1661     }
1662
1663   /* Set the attributes, if any. */
1664   if (attrs)
1665     {
1666       pango_layout_set_attributes (font->layout, attrs);
1667       pango_attr_list_unref (attrs);
1668     }
1669
1670   /* Set the text. */
1671   if (ds_is_empty (&tmp))
1672     pango_layout_set_text (font->layout, text, -1);
1673   else
1674     pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
1675   ds_destroy (&tmp);
1676
1677   pango_layout_set_alignment (font->layout,
1678                               (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
1679                                : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
1680                                : PANGO_ALIGN_CENTER));
1681   pango_layout_set_width (
1682     font->layout,
1683     bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
1684   pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1685
1686   if (clip[H][0] != clip[H][1])
1687     {
1688       cairo_save (xr->cairo);
1689       if (!(options & TAB_ROTATE))
1690         xr_clip (xr, clip);
1691       if (options & TAB_ROTATE)
1692         {
1693           cairo_translate (xr->cairo,
1694                            xr_to_pt (bb[H][0]),
1695                            xr_to_pt (bb[V][1] + xr->y));
1696           cairo_rotate (xr->cairo, -M_PI_2);
1697         }
1698       else
1699         cairo_translate (xr->cairo,
1700                          xr_to_pt (bb[H][0]),
1701                          xr_to_pt (bb[V][0] + xr->y));
1702       pango_cairo_show_layout (xr->cairo, font->layout);
1703
1704       /* If enabled, this draws a blue rectangle around the extents of each
1705          line of text, which can be rather useful for debugging layout
1706          issues. */
1707       if (0)
1708         {
1709           PangoLayoutIter *iter;
1710           iter = pango_layout_get_iter (font->layout);
1711           do
1712             {
1713               PangoRectangle extents;
1714
1715               pango_layout_iter_get_line_extents (iter, &extents, NULL);
1716               cairo_save (xr->cairo);
1717               cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1718               dump_rectangle (xr,
1719                               pango_to_xr (extents.x),
1720                               pango_to_xr (extents.y) - xr->y,
1721                               pango_to_xr (extents.x + extents.width),
1722                               pango_to_xr (extents.y + extents.height) - xr->y);
1723               cairo_restore (xr->cairo);
1724             }
1725           while (pango_layout_iter_next_line (iter));
1726           pango_layout_iter_free (iter);
1727         }
1728
1729       cairo_restore (xr->cairo);
1730     }
1731
1732   int size[TABLE_N_AXES];
1733   pango_layout_get_size (font->layout, &size[H], &size[V]);
1734   int w = pango_to_xr (size[X]);
1735   int h = pango_to_xr (size[Y]);
1736   if (w > *widthp)
1737     *widthp = w;
1738   if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
1739     {
1740       PangoLayoutIter *iter;
1741       int best = 0;
1742
1743       /* Choose a breakpoint between lines instead of in the middle of one. */
1744       iter = pango_layout_get_iter (font->layout);
1745       do
1746         {
1747           PangoRectangle extents;
1748           int y0, y1;
1749           int bottom;
1750
1751           pango_layout_iter_get_line_extents (iter, NULL, &extents);
1752           pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1753           extents.x = pango_to_xr (extents.x);
1754           extents.y = pango_to_xr (y0);
1755           extents.width = pango_to_xr (extents.width);
1756           extents.height = pango_to_xr (y1 - y0);
1757           bottom = bb[V][0] + extents.y + extents.height;
1758           if (bottom < bb[V][1])
1759             {
1760               if (brk && clip[H][0] != clip[H][1])
1761                 best = bottom;
1762               if (brk)
1763                 *brk = bottom;
1764             }
1765           else
1766             break;
1767         }
1768       while (pango_layout_iter_next_line (iter));
1769       pango_layout_iter_free (iter);
1770
1771       /* If enabled, draws a green line across the chosen breakpoint, which can
1772          be useful for debugging issues with breaking.  */
1773       if (0)
1774         {
1775           if (best)
1776             dump_line (xr, -xr->left_margin, best,
1777                        xr->width + xr->right_margin, best,
1778                        RENDER_LINE_SINGLE,
1779                        &(struct cell_color) CELL_COLOR (0, 255, 0));
1780         }
1781     }
1782
1783   pango_layout_set_attributes (font->layout, NULL);
1784
1785   if (font == &local_font)
1786     {
1787       g_object_unref (G_OBJECT (font->layout));
1788       pango_font_description_free (font->desc);
1789     }
1790
1791   return h;
1792 }
1793
1794 static void
1795 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1796                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1797                 int *width, int *height, int *brk)
1798 {
1799   *width = 0;
1800   *height = 0;
1801
1802   /* If enabled, draws a blue rectangle around the cell extents, which can be
1803      useful for debugging layout. */
1804   if (0)
1805     {
1806       if (clip[H][0] != clip[H][1])
1807         {
1808           cairo_save (xr->cairo);
1809           cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1810           dump_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
1811           cairo_restore (xr->cairo);
1812         }
1813     }
1814
1815   if (brk)
1816     *brk = bb[V][0];
1817   *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
1818 }
1819 \f
1820 struct output_driver_factory pdf_driver_factory =
1821   { "pdf", "pspp.pdf", xr_pdf_create };
1822 struct output_driver_factory ps_driver_factory =
1823   { "ps", "pspp.ps", xr_ps_create };
1824 struct output_driver_factory svg_driver_factory =
1825   { "svg", "pspp.svg", xr_svg_create };
1826
1827 static const struct output_driver_class cairo_driver_class =
1828 {
1829   "cairo",
1830   xr_destroy,
1831   xr_submit,
1832   xr_flush,
1833 };
1834 \f
1835 /* GUI rendering helpers. */
1836
1837 struct xr_rendering
1838   {
1839     struct output_item *item;
1840
1841     /* Table items. */
1842     struct render_pager *p;
1843     struct xr_driver *xr;
1844   };
1845
1846 #define CHART_WIDTH 500
1847 #define CHART_HEIGHT 375
1848
1849
1850
1851 struct xr_driver *
1852 xr_driver_create (cairo_t *cairo, struct string_map *options)
1853 {
1854   struct xr_driver *xr = xr_allocate ("cairo", 0, options, 1.0);
1855   xr_set_cairo (xr, cairo);
1856   return xr;
1857 }
1858
1859 /* Destroy XR, which should have been created with xr_driver_create().  Any
1860    cairo_t added to XR is not destroyed, because it is owned by the client. */
1861 void
1862 xr_driver_destroy (struct xr_driver *xr)
1863 {
1864   if (xr != NULL)
1865     {
1866       xr->cairo = NULL;
1867       output_driver_destroy (&xr->driver);
1868     }
1869 }
1870
1871 static struct xr_rendering *
1872 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1873 {
1874   struct table_item *table_item;
1875   struct xr_rendering *r;
1876
1877   table_item = table_item_create (table_from_string (text), NULL, NULL);
1878   r = xr_rendering_create (xr, &table_item->output_item, cr);
1879   table_item_unref (table_item);
1880
1881   return r;
1882 }
1883
1884 void
1885 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1886 {
1887   if (is_table_item (xr->item))
1888     apply_options (xr->xr, o);
1889 }
1890
1891 struct xr_rendering *
1892 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1893                      cairo_t *cr)
1894 {
1895   struct xr_rendering *r = NULL;
1896
1897   if (is_text_item (item))
1898     r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1899                                   cr);
1900   else if (is_message_item (item))
1901     {
1902       const struct message_item *message_item = to_message_item (item);
1903       char *s = msg_to_string (message_item_get_msg (message_item));
1904       r = xr_rendering_create_text (xr, s, cr);
1905       free (s);
1906     }
1907   else if (is_table_item (item))
1908     {
1909       r = xzalloc (sizeof *r);
1910       r->item = output_item_ref (item);
1911       r->xr = xr;
1912       xr_set_cairo (xr, cr);
1913       r->p = render_pager_create (xr->params, to_table_item (item));
1914     }
1915   else if (is_chart_item (item))
1916     {
1917       r = xzalloc (sizeof *r);
1918       r->item = output_item_ref (item);
1919     }
1920   else if (is_group_open_item (item))
1921     r = xr_rendering_create_text (xr, to_group_open_item (item)->command_name,
1922                                   cr);
1923
1924   return r;
1925 }
1926
1927 void
1928 xr_rendering_destroy (struct xr_rendering *r)
1929 {
1930   if (r)
1931     {
1932       output_item_unref (r->item);
1933       render_pager_destroy (r->p);
1934       free (r);
1935     }
1936 }
1937
1938 void
1939 xr_rendering_measure (const struct xr_rendering *r, int *wp, int *hp)
1940 {
1941   int w, h;
1942
1943   if (is_table_item (r->item))
1944     {
1945       w = render_pager_get_size (r->p, H) / XR_POINT;
1946       h = render_pager_get_size (r->p, V) / XR_POINT;
1947     }
1948   else
1949     {
1950       w = CHART_WIDTH;
1951       h = CHART_HEIGHT;
1952     }
1953
1954   if (wp)
1955     *wp = w;
1956   if (hp)
1957     *hp = h;
1958 }
1959
1960 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1961                     double x, double y, double width, double height);
1962
1963 /* Draws onto CR */
1964 void
1965 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1966                    int x0, int y0, int x1, int y1)
1967 {
1968   if (is_table_item (r->item))
1969     {
1970       struct xr_driver *xr = r->xr;
1971
1972       xr_set_cairo (xr, cr);
1973
1974       render_pager_draw_region (r->p, x0 * XR_POINT, y0 * XR_POINT,
1975                                 (x1 - x0) * XR_POINT, (y1 - y0) * XR_POINT);
1976     }
1977   else
1978     xr_draw_chart (to_chart_item (r->item), cr,
1979                    0, 0, CHART_WIDTH, CHART_HEIGHT);
1980 }
1981
1982 static void
1983 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1984                double x, double y, double width, double height)
1985 {
1986   struct xrchart_geometry geom;
1987
1988   cairo_save (cr);
1989   cairo_translate (cr, x, y + height);
1990   cairo_scale (cr, 1.0, -1.0);
1991   xrchart_geometry_init (cr, &geom, width, height);
1992   if (is_boxplot (chart_item))
1993     xrchart_draw_boxplot (chart_item, cr, &geom);
1994   else if (is_histogram_chart (chart_item))
1995     xrchart_draw_histogram (chart_item, cr, &geom);
1996   else if (is_np_plot_chart (chart_item))
1997     xrchart_draw_np_plot (chart_item, cr, &geom);
1998   else if (is_piechart (chart_item))
1999     xrchart_draw_piechart (chart_item, cr, &geom);
2000   else if (is_barchart (chart_item))
2001     xrchart_draw_barchart (chart_item, cr, &geom);
2002   else if (is_roc_chart (chart_item))
2003     xrchart_draw_roc (chart_item, cr, &geom);
2004   else if (is_scree (chart_item))
2005     xrchart_draw_scree (chart_item, cr, &geom);
2006   else if (is_spreadlevel_plot_chart (chart_item))
2007     xrchart_draw_spreadlevel (chart_item, cr, &geom);
2008   else if (is_scatterplot_chart (chart_item))
2009     xrchart_draw_scatterplot (chart_item, cr, &geom);
2010   else
2011     NOT_REACHED ();
2012   xrchart_geometry_free (cr, &geom);
2013
2014   cairo_restore (cr);
2015 }
2016
2017 char *
2018 xr_draw_png_chart (const struct chart_item *item,
2019                    const char *file_name_template, int number,
2020                    const struct cell_color *fg,
2021                    const struct cell_color *bg)
2022 {
2023   const int width = 640;
2024   const int length = 480;
2025
2026   cairo_surface_t *surface;
2027   cairo_status_t status;
2028   const char *number_pos;
2029   char *file_name;
2030   cairo_t *cr;
2031
2032   number_pos = strchr (file_name_template, '#');
2033   if (number_pos != NULL)
2034     file_name = xasprintf ("%.*s%d%s.png", (int) (number_pos - file_name_template),
2035                            file_name_template, number, number_pos + 1);
2036   else
2037     file_name = xasprintf ("%s.png", file_name_template);
2038
2039   surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
2040   cr = cairo_create (surface);
2041
2042   cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
2043   cairo_paint (cr);
2044
2045   cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
2046
2047   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
2048
2049   status = cairo_surface_write_to_png (surface, file_name);
2050   if (status != CAIRO_STATUS_SUCCESS)
2051     msg (ME, _("error writing output file `%s': %s"),
2052            file_name, cairo_status_to_string (status));
2053
2054   cairo_destroy (cr);
2055   cairo_surface_destroy (surface);
2056
2057   return file_name;
2058 }
2059
2060
2061 char *
2062 xr_draw_eps_chart (const struct chart_item *item,
2063                    const char *file_name_template, int number,
2064                    const struct cell_color *fg,
2065                    const struct cell_color *bg)
2066 {
2067   const int width = 640;
2068   const int length = 480;
2069
2070   cairo_surface_t *surface;
2071   const char *number_pos;
2072   char *file_name;
2073   cairo_t *cr;
2074
2075   number_pos = strchr (file_name_template, '#');
2076   if (number_pos != NULL)
2077     file_name = xasprintf ("%.*s%d%s.eps", (int) (number_pos - file_name_template),
2078                            file_name_template, number, number_pos + 1);
2079   else
2080     file_name = xasprintf ("%s.eps", file_name_template);
2081
2082   surface = cairo_ps_surface_create (file_name, width, length);
2083   cairo_ps_surface_set_eps (surface, true);
2084   cr = cairo_create (surface);
2085
2086   cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
2087   cairo_paint (cr);
2088
2089   cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
2090
2091   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
2092
2093   cairo_destroy (cr);
2094   cairo_surface_destroy (surface);
2095
2096   return file_name;
2097 }
2098
2099 \f
2100
2101 struct xr_table_state
2102   {
2103     struct xr_render_fsm fsm;
2104     struct render_pager *p;
2105   };
2106
2107 static bool
2108 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
2109 {
2110   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
2111
2112   while (render_pager_has_next (ts->p))
2113     {
2114       int used;
2115
2116       used = render_pager_draw_next (ts->p, xr->length - xr->y);
2117       if (!used)
2118         {
2119           assert (xr->y > 0);
2120           return true;
2121         }
2122       else
2123         xr->y += used;
2124     }
2125   return false;
2126 }
2127
2128 static void
2129 xr_table_destroy (struct xr_render_fsm *fsm)
2130 {
2131   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
2132
2133   render_pager_destroy (ts->p);
2134   free (ts);
2135 }
2136
2137 static struct xr_render_fsm *
2138 xr_render_table (struct xr_driver *xr, struct table_item *table_item)
2139 {
2140   struct xr_table_state *ts;
2141
2142   ts = xmalloc (sizeof *ts);
2143   ts->fsm.render = xr_table_render;
2144   ts->fsm.destroy = xr_table_destroy;
2145
2146   if (xr->y > 0)
2147     xr->y += xr->char_height;
2148
2149   ts->p = render_pager_create (xr->params, table_item);
2150   table_item_unref (table_item);
2151
2152   return &ts->fsm;
2153 }
2154 \f
2155 struct xr_chart_state
2156   {
2157     struct xr_render_fsm fsm;
2158     struct chart_item *chart_item;
2159   };
2160
2161 static bool
2162 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
2163 {
2164   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
2165
2166   const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
2167
2168   if (xr->y > xr->length - chart_height)
2169     return true;
2170
2171   if (xr->cairo != NULL)
2172     {
2173       xr_draw_chart (cs->chart_item, xr->cairo,
2174                      0.0,
2175                      xr_to_pt (xr->y),
2176                      xr_to_pt (xr->width),
2177                      xr_to_pt (chart_height));
2178     }
2179   xr->y += chart_height;
2180
2181   return false;
2182 }
2183
2184 static void
2185 xr_chart_destroy (struct xr_render_fsm *fsm)
2186 {
2187   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
2188
2189   chart_item_unref (cs->chart_item);
2190   free (cs);
2191 }
2192
2193 static struct xr_render_fsm *
2194 xr_render_chart (const struct chart_item *chart_item)
2195 {
2196   struct xr_chart_state *cs;
2197
2198   cs = xmalloc (sizeof *cs);
2199   cs->fsm.render = xr_chart_render;
2200   cs->fsm.destroy = xr_chart_destroy;
2201   cs->chart_item = chart_item_ref (chart_item);
2202
2203   return &cs->fsm;
2204 }
2205 \f
2206 static bool
2207 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
2208 {
2209   return xr->y > 0;
2210 }
2211
2212 static void
2213 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
2214 {
2215   /* Nothing to do. */
2216 }
2217
2218 static struct xr_render_fsm *
2219 xr_render_eject (void)
2220 {
2221   static struct xr_render_fsm eject_renderer =
2222     {
2223       xr_eject_render,
2224       xr_eject_destroy
2225     };
2226
2227   return &eject_renderer;
2228 }
2229 \f
2230 static struct xr_render_fsm *
2231 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
2232 {
2233   enum text_item_type type = text_item_get_type (text_item);
2234
2235   switch (type)
2236     {
2237     case TEXT_ITEM_PAGE_TITLE:
2238       break;
2239
2240     case TEXT_ITEM_EJECT_PAGE:
2241       if (xr->y > 0)
2242         return xr_render_eject ();
2243       break;
2244
2245     default:
2246       return xr_render_table (
2247         xr, text_item_to_table_item (text_item_ref (text_item)));
2248     }
2249
2250   return NULL;
2251 }
2252
2253 static struct xr_render_fsm *
2254 xr_render_message (struct xr_driver *xr,
2255                    const struct message_item *message_item)
2256 {
2257   char *s = msg_to_string (message_item_get_msg (message_item));
2258   struct text_item *item = text_item_create (TEXT_ITEM_LOG, s);
2259   free (s);
2260   return xr_render_table (xr, text_item_to_table_item (item));
2261 }
2262
2263 static struct xr_render_fsm *
2264 xr_render_output_item (struct xr_driver *xr,
2265                        const struct output_item *output_item)
2266 {
2267   if (is_table_item (output_item))
2268     return xr_render_table (xr, table_item_ref (to_table_item (output_item)));
2269   else if (is_chart_item (output_item))
2270     return xr_render_chart (to_chart_item (output_item));
2271   else if (is_text_item (output_item))
2272     return xr_render_text (xr, to_text_item (output_item));
2273   else if (is_message_item (output_item))
2274     return xr_render_message (xr, to_message_item (output_item));
2275   else
2276     return NULL;
2277 }
2278
2279 bool
2280 xr_draw_svg_file (struct xr_rendering *r,
2281                   const char *filename)
2282 {
2283   int width, height;
2284   g_assert (r);
2285   xr_rendering_measure (r, &width, &height);
2286   cairo_surface_t *surface = cairo_svg_surface_create (filename, width, height);
2287   if (!surface)
2288     {
2289       g_error ("Could not create cairo svg surface with file %s", filename);
2290       return FALSE;
2291     }
2292   cairo_t *cr = cairo_create (surface);
2293   xr_rendering_draw (r, cr, 0, 0, width, height);
2294   cairo_destroy (cr);
2295   cairo_surface_destroy (surface);
2296   return TRUE;
2297 }