New function: xr_draw_eps_chart
[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     char *command_name;
172     char *title;
173     char *subtitle;
174     cairo_t *cairo;
175     cairo_surface_t *surface;
176     int page_number;            /* Current page number. */
177     int x, y;
178     struct xr_render_fsm *fsm;
179     int nest;
180     struct string_map heading_vars;
181   };
182
183 static const struct output_driver_class cairo_driver_class;
184
185 static void xr_driver_destroy_fsm (struct xr_driver *);
186 static void xr_driver_run_fsm (struct xr_driver *);
187
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 *,
192                                    int *min, int *max);
193 static int xr_measure_cell_height (void *, const struct table_cell *,
194                                    int width);
195 static void xr_draw_cell (void *, const struct table_cell *, int color_idx,
196                           int bb[TABLE_N_AXES][2], int valign_offset,
197                           int spill[TABLE_N_AXES][2],
198                           int clip[TABLE_N_AXES][2]);
199 static int xr_adjust_break (void *, const struct table_cell *,
200                             int width, int height);
201
202 static struct xr_render_fsm *xr_render_output_item (
203   struct xr_driver *, const struct output_item *);
204 \f
205 /* Output driver basics. */
206
207 static struct xr_driver *
208 xr_driver_cast (struct output_driver *driver)
209 {
210   assert (driver->class == &cairo_driver_class);
211   return UP_CAST (driver, struct xr_driver, driver);
212 }
213
214 static struct driver_option *
215 opt (struct output_driver *d, struct string_map *options, const char *key,
216      const char *default_value)
217 {
218   return driver_option_get (d, options, key, default_value);
219 }
220
221 static int
222 lookup_color_name (const char *s)
223 {
224   struct color
225     {
226       struct hmap_node hmap_node;
227       const char *name;
228       int code;
229     };
230
231   static struct color colors[] =
232     {
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 },
380     };
381
382   static struct hmap color_table = HMAP_INITIALIZER (color_table);
383
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));
388
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))
393       return color->code;
394   return -1;
395 }
396
397 static bool
398 parse_color__ (const char *s, struct cell_color *color)
399 {
400   /* #rrrrggggbbbb */
401   uint16_t r16, g16, b16;
402   int len;
403   if (sscanf (s, "#%4"SCNx16"%4"SCNx16"%4"SCNx16"%n",
404               &r16, &g16, &b16, &len) == 3
405       && len == 13
406       && !s[len])
407     {
408       color->r = r16 >> 8;
409       color->g = g16 >> 8;
410       color->b = b16 >> 8;
411       return true;
412     }
413
414   /* #rrggbb */
415   uint8_t r, g, b;
416   if (sscanf (s, "#%2"SCNx8"%2"SCNx8"%2"SCNx8"%n", &r, &g, &b, &len) == 3
417       && len == 7
418       && !s[len])
419     {
420       color->r = r;
421       color->g = g;
422       color->b = b;
423       return true;
424     }
425
426   /* rrggbb */
427   if (sscanf (s, "%2"SCNx8"%2"SCNx8"%2"SCNx8"%n", &r, &g, &b, &len) == 3
428       && len == 6
429       && !s[len])
430     {
431       color->r = r;
432       color->g = g;
433       color->b = b;
434       return true;
435     }
436
437   /* rgb(r,g,b) */
438   if (sscanf (s, "rgb (%"SCNi8" , %"SCNi8" , %"SCNi8") %n",
439               &r, &g, &b, &len) == 3
440       && !s[len])
441     {
442       color->r = r;
443       color->g = g;
444       color->b = b;
445       return true;
446     }
447
448   /* rgba(r,g,b,a), ignoring a. */
449   if (sscanf (s, "rgba (%"SCNi8" , %"SCNi8" , %"SCNi8", %*f) %n",
450               &r, &g, &b, &len) == 3
451       && !s[len])
452     {
453       color->r = r;
454       color->g = g;
455       color->b = b;
456       return true;
457     }
458
459   int code = lookup_color_name (s);
460   if (code >= 0)
461     {
462       color->r = code >> 16;
463       color->g = code >> 8;
464       color->b = code;
465       return true;
466     }
467
468   return false;
469 }
470
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"
475 */
476 void
477 parse_color (struct output_driver *d, struct string_map *options,
478              const char *key, const char *default_value,
479              struct cell_color *color)
480 {
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;
484   free (string);
485 }
486
487 static PangoFontDescription *
488 parse_font (const char *font, int default_size, bool bold, bool italic)
489 {
490   if (!c_strcasecmp (font, "Monospaced"))
491     font = "Monospace";
492
493   PangoFontDescription *desc = pango_font_description_from_string (font);
494   if (desc == NULL)
495     return NULL;
496
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);
502
503   pango_font_description_set_weight (desc, (bold
504                                             ? PANGO_WEIGHT_BOLD
505                                             : PANGO_WEIGHT_NORMAL));
506   pango_font_description_set_style (desc, (italic
507                                            ? PANGO_STYLE_ITALIC
508                                            : PANGO_STYLE_NORMAL));
509
510   return desc;
511 }
512
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)
517 {
518   char *string = parse_string (opt (d, options, key, default_value));
519   PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
520   if (!desc)
521     {
522       msg (MW, _("`%s': bad font specification"), string);
523
524       /* Fall back to DEFAULT_VALUE, which had better be a valid font
525          description. */
526       desc = parse_font (default_value, default_size, bold, italic);
527       assert (desc != NULL);
528     }
529   free (string);
530
531   return desc;
532 }
533
534 static void
535 apply_options (struct xr_driver *xr, struct string_map *o)
536 {
537   struct output_driver *d = &xr->driver;
538
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;
543   int font_size;
544   int min_break[TABLE_N_AXES];
545
546   /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
547   const double scale = XR_POINT / 1000.;
548
549   int i;
550
551   for (i = 0; i < XR_N_FONTS; i++)
552     {
553       struct xr_font *font = &xr->fonts[i];
554
555       if (font->desc != NULL)
556         pango_font_description_free (font->desc);
557     }
558
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);
566
567   parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg);
568   parse_color (d, o, "foreground-color", "#000000000000", &xr->fg);
569
570   xr->transparent = parse_boolean (opt (d, o, "transparent", "false"));
571   xr->systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
572
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"));
579
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;
582
583   int object_spacing = (parse_dimension (opt (d, o, "object-spacing", NULL))
584                         * scale);
585
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;
596
597   /* There are no headings so headings_height can stay 0. */
598 }
599
600 static struct xr_driver *
601 xr_allocate (const char *name, int device_type, struct string_map *o,
602              double font_scale)
603 {
604   struct xr_driver *xr = xzalloc (sizeof *xr);
605   struct output_driver *d = &xr->driver;
606
607   output_driver_init (d, &cairo_driver_class, name, device_type);
608
609   string_map_init (&xr->heading_vars);
610
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;
617
618   apply_options (xr, o);
619
620   return xr;
621 }
622
623 static int
624 pango_to_xr (int pango)
625 {
626   return (XR_POINT != PANGO_SCALE
627           ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
628           : pango);
629 }
630
631 static int
632 xr_to_pango (int xr)
633 {
634   return (XR_POINT != PANGO_SCALE
635           ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
636           : xr);
637 }
638
639 static void
640 xr_measure_fonts (cairo_t *cairo, const struct xr_font fonts[XR_N_FONTS],
641                   int *char_width, int *char_height)
642 {
643   *char_width = 0;
644   *char_height = 0;
645   for (int i = 0; i < XR_N_FONTS; i++)
646     {
647       PangoLayout *layout = pango_cairo_create_layout (cairo);
648       pango_layout_set_font_description (layout, fonts[i].desc);
649
650       pango_layout_set_text (layout, "0", 1);
651
652       int cw, ch;
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));
656
657       g_object_unref (G_OBJECT (layout));
658     }
659 }
660
661 static int
662 get_layout_height (PangoLayout *layout)
663 {
664   int w, h;
665   pango_layout_get_size (layout, &w, &h);
666   return h;
667 }
668
669 static int
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)
673 {
674   PangoLayout *layout = pango_cairo_create_layout (cairo);
675   pango_layout_set_font_description (layout, font);
676
677   int y = 0;
678   for (size_t i = 0; i < ph->n; i++)
679     {
680       const struct page_paragraph *pp = &ph->paragraphs[i];
681
682       char *markup = output_driver_substitute_heading_vars (pp->markup,
683                                                             page_number);
684       pango_layout_set_markup (layout, markup, -1);
685       free (markup);
686
687       pango_layout_set_alignment (
688         layout,
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));
694       if (draw)
695         {
696           cairo_save (cairo);
697           cairo_translate (cairo, 0, xr_to_pt (y + base_y));
698           pango_cairo_show_layout (cairo, layout);
699           cairo_restore (cairo);
700         }
701
702       y += pango_to_xr (get_layout_height (layout));
703     }
704
705   g_object_unref (G_OBJECT (layout));
706
707   return y;
708 }
709
710 static int
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])
715 {
716   cairo_t *cairo = cairo_create (surface);
717   int total = 0;
718   for (int i = 0; i < 2; i++)
719     {
720       int h = xr_render_page_heading (cairo, font, &headings[i], -1,
721                                       width, false, 0);
722
723       /* If the top heading is nonempty, add some space below it. */
724       if (h && i == 0)
725         h += object_spacing;
726
727       if (height)
728         height[i] = h;
729       total += h;
730     }
731   cairo_destroy (cairo);
732   return total;
733 }
734
735 static bool
736 xr_check_fonts (cairo_surface_t *surface,
737                 const struct xr_font fonts[XR_N_FONTS],
738                 int usable_width, int usable_length)
739 {
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);
744
745   bool ok = true;
746   enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
747   if (usable_width / char_width < MIN_WIDTH)
748     {
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);
753       ok = false;
754     }
755   if (usable_length / char_height < MIN_LENGTH)
756     {
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);
761       ok = false;
762     }
763   return ok;
764 }
765
766 static void
767 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
768 {
769   xr->cairo = cairo;
770
771   cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
772
773   xr_measure_fonts (xr->cairo, xr->fonts, &xr->char_width, &xr->char_height);
774
775   for (int i = 0; i < XR_N_FONTS; i++)
776     {
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);
780     }
781
782   if (xr->params == NULL)
783     {
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;
795
796       int lw = XR_LINE_WIDTH;
797       int ls = XR_LINE_SPACE;
798       for (int i = 0; i < TABLE_N_AXES; i++)
799         {
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;
806         }
807
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 ();
812     }
813
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);
817 }
818
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)
822 {
823   struct xr_driver *xr;
824   cairo_status_t status;
825   double width_pt, length_pt;
826
827   xr = xr_allocate (file_name, device_type, o, 72.0 / 128.0);
828
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);
837   else
838     NOT_REACHED ();
839
840   status = cairo_surface_status (xr->surface);
841   if (status != CAIRO_STATUS_SUCCESS)
842     {
843       msg (ME, _("error opening output file `%s': %s"),
844              file_name, cairo_status_to_string (status));
845       goto error;
846     }
847
848   if (!xr_check_fonts (xr->surface, xr->fonts, xr->width, xr->length))
849     goto error;
850
851   return &xr->driver;
852
853  error:
854   output_driver_destroy (&xr->driver);
855   return NULL;
856 }
857
858 static struct output_driver *
859 xr_pdf_create (struct  file_handle *fh, enum settings_output_devices device_type,
860                struct string_map *o)
861 {
862   struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_PDF);
863   fh_unref (fh);
864   return od ;
865 }
866
867 static struct output_driver *
868 xr_ps_create (struct  file_handle *fh, enum settings_output_devices device_type,
869                struct string_map *o)
870 {
871   struct output_driver *od =  xr_create (fh_get_file_name (fh), device_type, o, XR_PS);
872   fh_unref (fh);
873   return od ;
874 }
875
876 static struct output_driver *
877 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
878                struct string_map *o)
879 {
880   struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_SVG);
881   fh_unref (fh);
882   return od ;
883 }
884
885 static void
886 xr_destroy (struct output_driver *driver)
887 {
888   struct xr_driver *xr = xr_driver_cast (driver);
889   size_t i;
890
891   xr_driver_destroy_fsm (xr);
892
893   if (xr->cairo != NULL)
894     {
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);
902
903       cairo_destroy (xr->cairo);
904     }
905
906   for (i = 0; i < XR_N_FONTS; i++)
907     {
908       struct xr_font *font = &xr->fonts[i];
909
910       if (font->desc != NULL)
911         pango_font_description_free (font->desc);
912       if (font->layout != NULL)
913         g_object_unref (font->layout);
914     }
915
916   free (xr->params);
917   free (xr);
918 }
919
920 static void
921 xr_flush (struct output_driver *driver)
922 {
923   struct xr_driver *xr = xr_driver_cast (driver);
924
925   cairo_surface_flush (cairo_get_target (xr->cairo));
926 }
927
928 static void
929 xr_update_page_setup (struct output_driver *driver,
930                       const struct page_setup *ps)
931 {
932   struct xr_driver *xr = xr_driver_cast (driver);
933
934   xr->initial_page_number = ps->initial_page_number;
935   xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
936
937   if (xr->cairo)
938     return;
939
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;
944
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);
949
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]))
953     return;
954
955   for (int i = 0; i < 2; i++)
956     {
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];
960     }
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);
969 }
970
971 static void
972 xr_submit (struct output_driver *driver, const struct output_item *output_item)
973 {
974   struct xr_driver *xr = xr_driver_cast (driver);
975
976   if (is_page_setup_item (output_item))
977     {
978       xr_update_page_setup (driver,
979                             to_page_setup_item (output_item)->page_setup);
980       return;
981     }
982
983   if (!xr->cairo)
984     {
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);
989     }
990
991   xr_driver_output_item (xr, output_item);
992   while (xr_driver_need_new_page (xr))
993     {
994       cairo_restore (xr->cairo);
995       cairo_show_page (xr->cairo);
996       cairo_save (xr->cairo);
997       xr_driver_next_page (xr, xr->cairo);
998     }
999 }
1000 \f
1001 /* Functions for rendering a series of output items to a series of Cairo
1002    contexts, with pagination.
1003
1004    Used by PSPPIRE for printing, and by the basic Cairo output driver above as
1005    its underlying implementation.
1006
1007    See the big comment in cairo.h for intended usage. */
1008
1009 /* Gives new page CAIRO to XR for output. */
1010 void
1011 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
1012 {
1013   if (!xr->transparent)
1014     {
1015       cairo_save (cairo);
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);
1019       cairo_fill (cairo);
1020       cairo_restore (cairo);
1021     }
1022   cairo_translate (cairo,
1023                    xr_to_pt (xr->left_margin),
1024                    xr_to_pt (xr->top_margin + xr->headings_height[0]));
1025
1026   xr->page_number++;
1027   xr->cairo = cairo;
1028   xr->x = xr->y = 0;
1029
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,
1035                           xr->length);
1036
1037   xr_driver_run_fsm (xr);
1038 }
1039
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()
1042    returns false. */
1043 void
1044 xr_driver_output_item (struct xr_driver *xr,
1045                        const struct output_item *output_item)
1046 {
1047   assert (xr->fsm == NULL);
1048   xr->fsm = xr_render_output_item (xr, output_item);
1049   xr_driver_run_fsm (xr);
1050 }
1051
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,
1054    otherwise false. */
1055 bool
1056 xr_driver_need_new_page (const struct xr_driver *xr)
1057 {
1058   return xr->fsm != NULL;
1059 }
1060
1061 /* Returns true if the current page doesn't have any content yet. */
1062 bool
1063 xr_driver_is_page_blank (const struct xr_driver *xr)
1064 {
1065   return xr->y == 0;
1066 }
1067
1068 static void
1069 xr_driver_destroy_fsm (struct xr_driver *xr)
1070 {
1071   if (xr->fsm != NULL)
1072     {
1073       xr->fsm->destroy (xr->fsm);
1074       xr->fsm = NULL;
1075     }
1076 }
1077
1078 static void
1079 xr_driver_run_fsm (struct xr_driver *xr)
1080 {
1081   if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
1082     xr_driver_destroy_fsm (xr);
1083 }
1084 \f
1085 static void
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);
1089
1090 static void
1091 set_source_rgba (cairo_t *cairo, const struct cell_color *color)
1092 {
1093   cairo_set_source_rgba (cairo,
1094                          color->r / 255., color->g / 255., color->b / 255.,
1095                          color->alpha / 255.);
1096 }
1097
1098 static void
1099 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
1100            const struct cell_color *color)
1101 {
1102   cairo_new_path (xr->cairo);
1103   if (!xr->systemcolors)
1104     set_source_rgba (xr->cairo, color);
1105   cairo_set_line_width (
1106     xr->cairo,
1107     xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
1108               : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
1109               : XR_LINE_WIDTH));
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);
1113 }
1114
1115 static void UNUSED
1116 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
1117 {
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);
1126 }
1127
1128 static void
1129 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
1130 {
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);
1137 }
1138
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. */
1143 static void
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,
1148            bool shorten)
1149 {
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);
1153   else
1154     {
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);
1159     }
1160 }
1161
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. */
1166 static void
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,
1171            bool shorten)
1172 {
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);
1176   else
1177     {
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);
1182     }
1183 }
1184
1185 static void
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])
1189 {
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];
1196
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];
1205
1206   /* The algorithm here is somewhat subtle, to allow it to handle
1207      all the kinds of intersections that we need.
1208
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.
1214
1215      yc, y1, and y2 are assigned similarly along the y axis.
1216
1217      The following diagram shows the coordinate system and output
1218      for double top and bottom lines, single left line, and no
1219      right line:
1220
1221                  x0       x1 xc  x2      x3
1222                y0 ________________________
1223                   |        #     #       |
1224                   |        #     #       |
1225                   |        #     #       |
1226                   |        #     #       |
1227                   |        #     #       |
1228      y1 = y2 = yc |#########     #       |
1229                   |        #     #       |
1230                   |        #     #       |
1231                   |        #     #       |
1232                   |        #     #       |
1233                y3 |________#_____#_______|
1234   */
1235   struct xr_driver *xr = xr_;
1236
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;
1239
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;
1245
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:
1254                x0       x1     x2      x3
1255              y0 ________________________
1256                 |        #     #       |
1257                 |        #     #       |
1258                 |        #     #       |
1259                 |        #     #       |
1260              y1 |#########     ########|
1261                 |                      |
1262                 |                      |
1263              y2 |######################|
1264                 |                      |
1265                 |                      |
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.
1270   */
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;
1278
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;
1286
1287   if (!double_horz)
1288     horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
1289                start_color, end_color, shorten_yc_line);
1290   else
1291     {
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);
1296     }
1297
1298   if (!double_vert)
1299     vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
1300                shorten_xc_line);
1301   else
1302     {
1303       vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
1304                  shorten_x1_lines);
1305       vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
1306                  shorten_x2_lines);
1307     }
1308 }
1309
1310 static void
1311 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
1312                        int *min_width, int *max_width)
1313 {
1314   struct xr_driver *xr = xr_;
1315   int bb[TABLE_N_AXES][2];
1316   int clip[TABLE_N_AXES][2];
1317   int h;
1318
1319   bb[H][0] = 0;
1320   bb[H][1] = INT_MAX;
1321   bb[V][0] = 0;
1322   bb[V][1] = INT_MAX;
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);
1325
1326   bb[H][1] = 1;
1327   xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
1328
1329   if (*min_width > 0)
1330     *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
1331                             + cell->style->cell_style.margin[H][1]);
1332   if (*max_width > 0)
1333     *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
1334                             + cell->style->cell_style.margin[H][1]);
1335 }
1336
1337 static int
1338 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
1339 {
1340   struct xr_driver *xr = xr_;
1341   int bb[TABLE_N_AXES][2];
1342   int clip[TABLE_N_AXES][2];
1343   int w, h;
1344
1345   bb[H][0] = 0;
1346   bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1347                                + cell->style->cell_style.margin[H][1]);
1348   bb[V][0] = 0;
1349   bb[V][1] = INT_MAX;
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]);
1354   return h;
1355 }
1356
1357 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
1358
1359 static void
1360 xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
1361               int bb[TABLE_N_AXES][2], int valign_offset,
1362               int spill[TABLE_N_AXES][2],
1363               int clip[TABLE_N_AXES][2])
1364 {
1365   struct xr_driver *xr = xr_;
1366   int w, h, brk;
1367
1368   if (!xr->transparent)
1369     {
1370       cairo_save (xr->cairo);
1371       int bg_clip[TABLE_N_AXES][2];
1372       for (int axis = 0; axis < TABLE_N_AXES; axis++)
1373         {
1374           bg_clip[axis][0] = clip[axis][0];
1375           if (bb[axis][0] == clip[axis][0])
1376             bg_clip[axis][0] -= spill[axis][0];
1377
1378           bg_clip[axis][1] = clip[axis][1];
1379           if (bb[axis][1] == clip[axis][1])
1380             bg_clip[axis][1] += spill[axis][1];
1381         }
1382       xr_clip (xr, bg_clip);
1383       set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]);
1384       fill_rectangle (xr,
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);
1390     }
1391   cairo_save (xr->cairo);
1392   if (!xr->systemcolors)
1393     set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]);
1394
1395   bb[V][0] += valign_offset;
1396
1397   for (int axis = 0; axis < TABLE_N_AXES; axis++)
1398     {
1399       bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
1400       bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
1401     }
1402   if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
1403     xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1404   cairo_restore (xr->cairo);
1405 }
1406
1407 static int
1408 xr_adjust_break (void *xr_, const struct table_cell *cell,
1409                  int width, int height)
1410 {
1411   struct xr_driver *xr = xr_;
1412   int bb[TABLE_N_AXES][2];
1413   int clip[TABLE_N_AXES][2];
1414   int w, h, brk;
1415
1416   if (xr_measure_cell_height (xr_, cell, width) < height)
1417     return -1;
1418
1419   bb[H][0] = 0;
1420   bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1421                                + cell->style->cell_style.margin[H][1]);
1422   if (bb[H][1] <= 0)
1423     return 0;
1424   bb[V][0] = 0;
1425   bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
1426                                 + cell->style->cell_style.margin[V][1]);
1427   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1428   xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1429   return brk;
1430 }
1431 \f
1432 static void
1433 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
1434 {
1435   if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1436     {
1437       double x0 = xr_to_pt (clip[H][0] + xr->x);
1438       double y0 = xr_to_pt (clip[V][0] + xr->y);
1439       double x1 = xr_to_pt (clip[H][1] + xr->x);
1440       double y1 = xr_to_pt (clip[V][1] + xr->y);
1441
1442       cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1443       cairo_clip (xr->cairo);
1444     }
1445 }
1446
1447 static void
1448 add_attr (PangoAttrList *list, PangoAttribute *attr,
1449           guint start_index, guint end_index)
1450 {
1451   attr->start_index = start_index;
1452   attr->end_index = end_index;
1453   pango_attr_list_insert (list, attr);
1454 }
1455
1456 static void
1457 markup_escape (struct string *out, unsigned int options,
1458                const char *in, size_t len)
1459 {
1460   if (!(options & TAB_MARKUP))
1461     {
1462       ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
1463       return;
1464     }
1465
1466   while (len-- > 0)
1467     {
1468       int c = *in++;
1469       switch (c)
1470         {
1471         case 0:
1472           return;
1473         case '&':
1474           ds_put_cstr (out, "&amp;");
1475           break;
1476         case '<':
1477           ds_put_cstr (out, "&lt;");
1478           break;
1479         case '>':
1480           ds_put_cstr (out, "&gt;");
1481           break;
1482         default:
1483           ds_put_byte (out, c);
1484           break;
1485         }
1486     }
1487 }
1488
1489 static int
1490 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
1491 {
1492   int size[TABLE_N_AXES];
1493   pango_layout_get_size (layout, &size[H], &size[V]);
1494   return size[axis];
1495 }
1496
1497 static int
1498 xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
1499                      int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1500                      int *widthp, int *brk)
1501 {
1502   const struct font_style *font_style = &cell->style->font_style;
1503   const struct cell_style *cell_style = &cell->style->cell_style;
1504   unsigned int options = cell->options;
1505
1506   enum table_axis X = options & TAB_ROTATE ? V : H;
1507   enum table_axis Y = !X;
1508   int R = options & TAB_ROTATE ? 0 : 1;
1509
1510   struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1511                           : &xr->fonts[XR_FONT_PROPORTIONAL]);
1512   struct xr_font local_font;
1513   if (font_style->typeface)
1514     {
1515       PangoFontDescription *desc = parse_font (
1516         font_style->typeface,
1517         font_style->size ? font_style->size * 1000 * xr->font_scale : 10000,
1518         font_style->bold, font_style->italic);
1519       if (desc)
1520         {
1521           PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1522           pango_layout_set_font_description (layout, desc);
1523
1524           local_font.desc = desc;
1525           local_font.layout = layout;
1526           font = &local_font;
1527         }
1528     }
1529
1530   const char *text = cell->text;
1531   enum table_halign halign = table_halign_interpret (
1532     cell_style->halign, cell->options & TAB_NUMERIC);
1533   if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
1534     {
1535       int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
1536
1537       const char *decimal = strrchr (text, cell_style->decimal_char);
1538       if (decimal)
1539         {
1540           pango_layout_set_text (font->layout, decimal, strlen (decimal));
1541           pango_layout_set_width (font->layout, -1);
1542           margin_adjustment += get_layout_dimension (font->layout, H);
1543         }
1544
1545       if (margin_adjustment < 0)
1546         bb[H][1] += margin_adjustment;
1547     }
1548
1549   struct string tmp = DS_EMPTY_INITIALIZER;
1550   PangoAttrList *attrs = NULL;
1551
1552   /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
1553      Pango's implementation of it): it will break after a period or a comma
1554      that precedes a digit, e.g. in ".000" it will break after the period.
1555      This code looks for such a situation and inserts a U+2060 WORD JOINER
1556      to prevent the break.
1557
1558      This isn't necessary when the decimal point is between two digits
1559      (e.g. "0.000" won't be broken) or when the display width is not limited so
1560      that word wrapping won't happen.
1561
1562      It isn't necessary to look for more than one period or comma, as would
1563      happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
1564      are present then there will always be a digit on both sides of every
1565      period and comma. */
1566   if (options & TAB_MARKUP)
1567     {
1568       PangoAttrList *new_attrs;
1569       char *new_text;
1570       if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
1571         {
1572           attrs = new_attrs;
1573           tmp.ss = ss_cstr (new_text);
1574           tmp.capacity = tmp.ss.length;
1575         }
1576       else
1577         {
1578           /* XXX should we report the error? */
1579           ds_put_cstr (&tmp, text);
1580         }
1581     }
1582   else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
1583     {
1584       const char *decimal = text + strcspn (text, ".,");
1585       if (decimal[0]
1586           && c_isdigit (decimal[1])
1587           && (decimal == text || !c_isdigit (decimal[-1])))
1588         {
1589           ds_extend (&tmp, strlen (text) + 16);
1590           markup_escape (&tmp, options, text, decimal - text + 1);
1591           ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
1592           markup_escape (&tmp, options, decimal + 1, -1);
1593         }
1594     }
1595
1596   if (font_style->underline)
1597     {
1598       if (!attrs)
1599         attrs = pango_attr_list_new ();
1600       pango_attr_list_insert (attrs, pango_attr_underline_new (
1601                                 PANGO_UNDERLINE_SINGLE));
1602     }
1603
1604   if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
1605     {
1606       /* If we haven't already put TEXT into tmp, do it now. */
1607       if (ds_is_empty (&tmp))
1608         {
1609           ds_extend (&tmp, strlen (text) + 16);
1610           markup_escape (&tmp, options, text, -1);
1611         }
1612
1613       size_t subscript_ofs = ds_length (&tmp);
1614       for (size_t i = 0; i < cell->n_subscripts; i++)
1615         {
1616           if (i)
1617             ds_put_byte (&tmp, ',');
1618           ds_put_cstr (&tmp, cell->subscripts[i]);
1619         }
1620
1621       size_t superscript_ofs = ds_length (&tmp);
1622       if (cell->superscript)
1623         ds_put_cstr (&tmp, cell->superscript);
1624
1625       size_t footnote_ofs = ds_length (&tmp);
1626       for (size_t i = 0; i < cell->n_footnotes; i++)
1627         {
1628           if (i)
1629             ds_put_byte (&tmp, ',');
1630           ds_put_cstr (&tmp, cell->footnotes[i]->marker);
1631         }
1632
1633       /* Allow footnote markers to occupy the right margin.  That way, numbers
1634          in the column are still aligned. */
1635       if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
1636         {
1637           /* Measure the width of the footnote marker, so we know how much we
1638              need to make room for. */
1639           pango_layout_set_text (font->layout, ds_cstr (&tmp) + footnote_ofs,
1640                                  ds_length (&tmp) - footnote_ofs);
1641
1642           PangoAttrList *fn_attrs = pango_attr_list_new ();
1643           pango_attr_list_insert (
1644             fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
1645           pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
1646           pango_layout_set_attributes (font->layout, fn_attrs);
1647           pango_attr_list_unref (fn_attrs);
1648           int footnote_width = get_layout_dimension (font->layout, X);
1649
1650           /* Bound the adjustment by the width of the right margin. */
1651           int right_margin = px_to_xr (cell_style->margin[X][R]);
1652           int footnote_adjustment = MIN (footnote_width, right_margin);
1653
1654           /* Adjust the bounding box. */
1655           if (options & TAB_ROTATE)
1656             footnote_adjustment = -footnote_adjustment;
1657           bb[X][R] += footnote_adjustment;
1658
1659           /* Clean up. */
1660           pango_layout_set_attributes (font->layout, NULL);
1661         }
1662
1663       /* Set attributes. */
1664       if (!attrs)
1665         attrs = pango_attr_list_new ();
1666       add_attr (attrs, pango_attr_font_desc_new (font->desc), subscript_ofs,
1667                 PANGO_ATTR_INDEX_TO_TEXT_END);
1668       add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
1669                 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
1670       if (cell->n_subscripts)
1671         add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
1672                   superscript_ofs - subscript_ofs);
1673       if (cell->superscript || cell->n_footnotes)
1674         add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
1675                   PANGO_ATTR_INDEX_TO_TEXT_END);
1676     }
1677
1678   /* Set the attributes, if any. */
1679   if (attrs)
1680     {
1681       pango_layout_set_attributes (font->layout, attrs);
1682       pango_attr_list_unref (attrs);
1683     }
1684
1685   /* Set the text. */
1686   if (ds_is_empty (&tmp))
1687     pango_layout_set_text (font->layout, text, -1);
1688   else
1689     pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
1690   ds_destroy (&tmp);
1691
1692   pango_layout_set_alignment (font->layout,
1693                               (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
1694                                : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
1695                                : PANGO_ALIGN_CENTER));
1696   pango_layout_set_width (
1697     font->layout,
1698     bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
1699   pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1700
1701   if (clip[H][0] != clip[H][1])
1702     {
1703       cairo_save (xr->cairo);
1704       if (!(options & TAB_ROTATE))
1705         xr_clip (xr, clip);
1706       if (options & TAB_ROTATE)
1707         {
1708           cairo_translate (xr->cairo,
1709                            xr_to_pt (bb[H][0] + xr->x),
1710                            xr_to_pt (bb[V][1] + xr->y));
1711           cairo_rotate (xr->cairo, -M_PI_2);
1712         }
1713       else
1714         cairo_translate (xr->cairo,
1715                          xr_to_pt (bb[H][0] + xr->x),
1716                          xr_to_pt (bb[V][0] + xr->y));
1717       pango_cairo_show_layout (xr->cairo, font->layout);
1718
1719       /* If enabled, this draws a blue rectangle around the extents of each
1720          line of text, which can be rather useful for debugging layout
1721          issues. */
1722       if (0)
1723         {
1724           PangoLayoutIter *iter;
1725           iter = pango_layout_get_iter (font->layout);
1726           do
1727             {
1728               PangoRectangle extents;
1729
1730               pango_layout_iter_get_line_extents (iter, &extents, NULL);
1731               cairo_save (xr->cairo);
1732               cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1733               dump_rectangle (xr,
1734                               pango_to_xr (extents.x) - xr->x,
1735                               pango_to_xr (extents.y) - xr->y,
1736                               pango_to_xr (extents.x + extents.width) - xr->x,
1737                               pango_to_xr (extents.y + extents.height) - xr->y);
1738               cairo_restore (xr->cairo);
1739             }
1740           while (pango_layout_iter_next_line (iter));
1741           pango_layout_iter_free (iter);
1742         }
1743
1744       cairo_restore (xr->cairo);
1745     }
1746
1747   int size[TABLE_N_AXES];
1748   pango_layout_get_size (font->layout, &size[H], &size[V]);
1749   int w = pango_to_xr (size[X]);
1750   int h = pango_to_xr (size[Y]);
1751   if (w > *widthp)
1752     *widthp = w;
1753   if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
1754     {
1755       PangoLayoutIter *iter;
1756       int best = 0;
1757
1758       /* Choose a breakpoint between lines instead of in the middle of one. */
1759       iter = pango_layout_get_iter (font->layout);
1760       do
1761         {
1762           PangoRectangle extents;
1763           int y0, y1;
1764           int bottom;
1765
1766           pango_layout_iter_get_line_extents (iter, NULL, &extents);
1767           pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1768           extents.x = pango_to_xr (extents.x);
1769           extents.y = pango_to_xr (y0);
1770           extents.width = pango_to_xr (extents.width);
1771           extents.height = pango_to_xr (y1 - y0);
1772           bottom = bb[V][0] + extents.y + extents.height;
1773           if (bottom < bb[V][1])
1774             {
1775               if (brk && clip[H][0] != clip[H][1])
1776                 best = bottom;
1777               if (brk)
1778                 *brk = bottom;
1779             }
1780           else
1781             break;
1782         }
1783       while (pango_layout_iter_next_line (iter));
1784       pango_layout_iter_free (iter);
1785
1786       /* If enabled, draws a green line across the chosen breakpoint, which can
1787          be useful for debugging issues with breaking.  */
1788       if (0)
1789         {
1790           if (best && !xr->nest)
1791             dump_line (xr, -xr->left_margin, best,
1792                        xr->width + xr->right_margin, best,
1793                        RENDER_LINE_SINGLE,
1794                        &(struct cell_color) CELL_COLOR (0, 255, 0));
1795         }
1796     }
1797
1798   pango_layout_set_attributes (font->layout, NULL);
1799
1800   if (font == &local_font)
1801     {
1802       g_object_unref (G_OBJECT (font->layout));
1803       pango_font_description_free (font->desc);
1804     }
1805
1806   return h;
1807 }
1808
1809 static void
1810 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1811                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1812                 int *width, int *height, int *brk)
1813 {
1814   *width = 0;
1815   *height = 0;
1816
1817   /* If enabled, draws a blue rectangle around the cell extents, which can be
1818      useful for debugging layout. */
1819   if (0)
1820     {
1821       if (clip[H][0] != clip[H][1])
1822         {
1823           int offset = (xr->nest) * XR_POINT;
1824
1825           cairo_save (xr->cairo);
1826           cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1827           dump_rectangle (xr,
1828                           bb[H][0] + offset, bb[V][0] + offset,
1829                           bb[H][1] - offset, bb[V][1] - offset);
1830           cairo_restore (xr->cairo);
1831         }
1832     }
1833
1834   if (brk)
1835     *brk = bb[V][0];
1836   *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
1837 }
1838 \f
1839 struct output_driver_factory pdf_driver_factory =
1840   { "pdf", "pspp.pdf", xr_pdf_create };
1841 struct output_driver_factory ps_driver_factory =
1842   { "ps", "pspp.ps", xr_ps_create };
1843 struct output_driver_factory svg_driver_factory =
1844   { "svg", "pspp.svg", xr_svg_create };
1845
1846 static const struct output_driver_class cairo_driver_class =
1847 {
1848   "cairo",
1849   xr_destroy,
1850   xr_submit,
1851   xr_flush,
1852 };
1853 \f
1854 /* GUI rendering helpers. */
1855
1856 struct xr_rendering
1857   {
1858     struct output_item *item;
1859
1860     /* Table items. */
1861     struct render_pager *p;
1862     struct xr_driver *xr;
1863   };
1864
1865 #define CHART_WIDTH 500
1866 #define CHART_HEIGHT 375
1867
1868
1869
1870 struct xr_driver *
1871 xr_driver_create (cairo_t *cairo, struct string_map *options)
1872 {
1873   struct xr_driver *xr = xr_allocate ("cairo", 0, options, 1.0);
1874   xr_set_cairo (xr, cairo);
1875   return xr;
1876 }
1877
1878 /* Destroy XR, which should have been created with xr_driver_create().  Any
1879    cairo_t added to XR is not destroyed, because it is owned by the client. */
1880 void
1881 xr_driver_destroy (struct xr_driver *xr)
1882 {
1883   if (xr != NULL)
1884     {
1885       xr->cairo = NULL;
1886       output_driver_destroy (&xr->driver);
1887     }
1888 }
1889
1890 static struct xr_rendering *
1891 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1892 {
1893   struct table_item *table_item;
1894   struct xr_rendering *r;
1895
1896   table_item = table_item_create (table_from_string (text), NULL, NULL);
1897   r = xr_rendering_create (xr, &table_item->output_item, cr);
1898   table_item_unref (table_item);
1899
1900   return r;
1901 }
1902
1903 void
1904 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1905 {
1906   if (is_table_item (xr->item))
1907     apply_options (xr->xr, o);
1908 }
1909
1910 struct xr_rendering *
1911 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1912                      cairo_t *cr)
1913 {
1914   struct xr_rendering *r = NULL;
1915
1916   if (is_text_item (item))
1917     r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1918                                   cr);
1919   else if (is_message_item (item))
1920     {
1921       const struct message_item *message_item = to_message_item (item);
1922       char *s = msg_to_string (message_item_get_msg (message_item));
1923       r = xr_rendering_create_text (xr, s, cr);
1924       free (s);
1925     }
1926   else if (is_table_item (item))
1927     {
1928       r = xzalloc (sizeof *r);
1929       r->item = output_item_ref (item);
1930       r->xr = xr;
1931       xr_set_cairo (xr, cr);
1932       r->p = render_pager_create (xr->params, to_table_item (item));
1933     }
1934   else if (is_chart_item (item))
1935     {
1936       r = xzalloc (sizeof *r);
1937       r->item = output_item_ref (item);
1938     }
1939   else if (is_group_open_item (item))
1940     r = xr_rendering_create_text (xr, to_group_open_item (item)->command_name,
1941                                   cr);
1942
1943   return r;
1944 }
1945
1946 void
1947 xr_rendering_destroy (struct xr_rendering *r)
1948 {
1949   if (r)
1950     {
1951       output_item_unref (r->item);
1952       render_pager_destroy (r->p);
1953       free (r);
1954     }
1955 }
1956
1957 void
1958 xr_rendering_measure (const struct xr_rendering *r, int *wp, int *hp)
1959 {
1960   int w, h;
1961
1962   if (is_table_item (r->item))
1963     {
1964       w = render_pager_get_size (r->p, H) / XR_POINT;
1965       h = render_pager_get_size (r->p, V) / XR_POINT;
1966     }
1967   else
1968     {
1969       w = CHART_WIDTH;
1970       h = CHART_HEIGHT;
1971     }
1972
1973   if (wp)
1974     *wp = w;
1975   if (hp)
1976     *hp = h;
1977 }
1978
1979 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1980                     double x, double y, double width, double height);
1981
1982 /* Draws onto CR */
1983 void
1984 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1985                    int x0, int y0, int x1, int y1)
1986 {
1987   if (is_table_item (r->item))
1988     {
1989       struct xr_driver *xr = r->xr;
1990
1991       xr_set_cairo (xr, cr);
1992
1993       render_pager_draw_region (r->p, x0 * XR_POINT, y0 * XR_POINT,
1994                                 (x1 - x0) * XR_POINT, (y1 - y0) * XR_POINT);
1995     }
1996   else
1997     xr_draw_chart (to_chart_item (r->item), cr,
1998                    0, 0, CHART_WIDTH, CHART_HEIGHT);
1999 }
2000
2001 static void
2002 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
2003                double x, double y, double width, double height)
2004 {
2005   struct xrchart_geometry geom;
2006
2007   cairo_save (cr);
2008   cairo_translate (cr, x, y + height);
2009   cairo_scale (cr, 1.0, -1.0);
2010   xrchart_geometry_init (cr, &geom, width, height);
2011   if (is_boxplot (chart_item))
2012     xrchart_draw_boxplot (chart_item, cr, &geom);
2013   else if (is_histogram_chart (chart_item))
2014     xrchart_draw_histogram (chart_item, cr, &geom);
2015   else if (is_np_plot_chart (chart_item))
2016     xrchart_draw_np_plot (chart_item, cr, &geom);
2017   else if (is_piechart (chart_item))
2018     xrchart_draw_piechart (chart_item, cr, &geom);
2019   else if (is_barchart (chart_item))
2020     xrchart_draw_barchart (chart_item, cr, &geom);
2021   else if (is_roc_chart (chart_item))
2022     xrchart_draw_roc (chart_item, cr, &geom);
2023   else if (is_scree (chart_item))
2024     xrchart_draw_scree (chart_item, cr, &geom);
2025   else if (is_spreadlevel_plot_chart (chart_item))
2026     xrchart_draw_spreadlevel (chart_item, cr, &geom);
2027   else if (is_scatterplot_chart (chart_item))
2028     xrchart_draw_scatterplot (chart_item, cr, &geom);
2029   else
2030     NOT_REACHED ();
2031   xrchart_geometry_free (cr, &geom);
2032
2033   cairo_restore (cr);
2034 }
2035
2036 char *
2037 xr_draw_png_chart (const struct chart_item *item,
2038                    const char *file_name_template, int number,
2039                    const struct cell_color *fg,
2040                    const struct cell_color *bg)
2041 {
2042   const int width = 640;
2043   const int length = 480;
2044
2045   cairo_surface_t *surface;
2046   cairo_status_t status;
2047   const char *number_pos;
2048   char *file_name;
2049   cairo_t *cr;
2050
2051   number_pos = strchr (file_name_template, '#');
2052   if (number_pos != NULL)
2053     file_name = xasprintf ("%.*s%d%s.png", (int) (number_pos - file_name_template),
2054                            file_name_template, number, number_pos + 1);
2055   else
2056     file_name = xasprintf ("%s.png", file_name_template);
2057
2058   surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
2059   cr = cairo_create (surface);
2060
2061   cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
2062   cairo_paint (cr);
2063
2064   cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
2065
2066   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
2067
2068   status = cairo_surface_write_to_png (surface, file_name);
2069   if (status != CAIRO_STATUS_SUCCESS)
2070     msg (ME, _("error writing output file `%s': %s"),
2071            file_name, cairo_status_to_string (status));
2072
2073   cairo_destroy (cr);
2074   cairo_surface_destroy (surface);
2075
2076   return file_name;
2077 }
2078
2079
2080 char *
2081 xr_draw_eps_chart (const struct chart_item *item,
2082                    const char *file_name_template, int number,
2083                    const struct cell_color *fg,
2084                    const struct cell_color *bg)
2085 {
2086   const int width = 640;
2087   const int length = 480;
2088
2089   cairo_surface_t *surface;
2090   const char *number_pos;
2091   char *file_name;
2092   cairo_t *cr;
2093
2094   number_pos = strchr (file_name_template, '#');
2095   if (number_pos != NULL)
2096     file_name = xasprintf ("%.*s%d%s.eps", (int) (number_pos - file_name_template),
2097                            file_name_template, number, number_pos + 1);
2098   else
2099     file_name = xasprintf ("%s.eps", file_name_template);
2100
2101   surface = cairo_ps_surface_create (file_name, width, length);
2102   cairo_ps_surface_set_eps (surface, true);
2103   cr = cairo_create (surface);
2104
2105   cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
2106   cairo_paint (cr);
2107
2108   cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
2109
2110   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
2111
2112   cairo_destroy (cr);
2113   cairo_surface_destroy (surface);
2114
2115   return file_name;
2116 }
2117
2118 \f
2119
2120 struct xr_table_state
2121   {
2122     struct xr_render_fsm fsm;
2123     struct render_pager *p;
2124   };
2125
2126 static bool
2127 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
2128 {
2129   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
2130
2131   while (render_pager_has_next (ts->p))
2132     {
2133       int used;
2134
2135       used = render_pager_draw_next (ts->p, xr->length - xr->y);
2136       if (!used)
2137         {
2138           assert (xr->y > 0);
2139           return true;
2140         }
2141       else
2142         xr->y += used;
2143     }
2144   return false;
2145 }
2146
2147 static void
2148 xr_table_destroy (struct xr_render_fsm *fsm)
2149 {
2150   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
2151
2152   render_pager_destroy (ts->p);
2153   free (ts);
2154 }
2155
2156 static struct xr_render_fsm *
2157 xr_render_table (struct xr_driver *xr, struct table_item *table_item)
2158 {
2159   struct xr_table_state *ts;
2160
2161   ts = xmalloc (sizeof *ts);
2162   ts->fsm.render = xr_table_render;
2163   ts->fsm.destroy = xr_table_destroy;
2164
2165   if (xr->y > 0)
2166     xr->y += xr->char_height;
2167
2168   ts->p = render_pager_create (xr->params, table_item);
2169   table_item_unref (table_item);
2170
2171   return &ts->fsm;
2172 }
2173 \f
2174 struct xr_chart_state
2175   {
2176     struct xr_render_fsm fsm;
2177     struct chart_item *chart_item;
2178   };
2179
2180 static bool
2181 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
2182 {
2183   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
2184
2185   const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
2186
2187   if (xr->y > xr->length - chart_height)
2188     return true;
2189
2190   if (xr->cairo != NULL)
2191     {
2192       xr_draw_chart (cs->chart_item, xr->cairo,
2193                      0.0,
2194                      xr_to_pt (xr->y),
2195                      xr_to_pt (xr->width),
2196                      xr_to_pt (chart_height));
2197     }
2198   xr->y += chart_height;
2199
2200   return false;
2201 }
2202
2203 static void
2204 xr_chart_destroy (struct xr_render_fsm *fsm)
2205 {
2206   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
2207
2208   chart_item_unref (cs->chart_item);
2209   free (cs);
2210 }
2211
2212 static struct xr_render_fsm *
2213 xr_render_chart (const struct chart_item *chart_item)
2214 {
2215   struct xr_chart_state *cs;
2216
2217   cs = xmalloc (sizeof *cs);
2218   cs->fsm.render = xr_chart_render;
2219   cs->fsm.destroy = xr_chart_destroy;
2220   cs->chart_item = chart_item_ref (chart_item);
2221
2222   return &cs->fsm;
2223 }
2224 \f
2225 static bool
2226 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
2227 {
2228   return xr->y > 0;
2229 }
2230
2231 static void
2232 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
2233 {
2234   /* Nothing to do. */
2235 }
2236
2237 static struct xr_render_fsm *
2238 xr_render_eject (void)
2239 {
2240   static struct xr_render_fsm eject_renderer =
2241     {
2242       xr_eject_render,
2243       xr_eject_destroy
2244     };
2245
2246   return &eject_renderer;
2247 }
2248 \f
2249 static struct xr_render_fsm *
2250 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
2251 {
2252   enum text_item_type type = text_item_get_type (text_item);
2253   const char *text = text_item_get_text (text_item);
2254
2255   switch (type)
2256     {
2257     case TEXT_ITEM_PAGE_TITLE:
2258       string_map_replace (&xr->heading_vars, "PageTitle", text);
2259       break;
2260
2261     case TEXT_ITEM_EJECT_PAGE:
2262       if (xr->y > 0)
2263         return xr_render_eject ();
2264       break;
2265
2266     default:
2267       return xr_render_table (
2268         xr, text_item_to_table_item (text_item_ref (text_item)));
2269     }
2270
2271   return NULL;
2272 }
2273
2274 static struct xr_render_fsm *
2275 xr_render_message (struct xr_driver *xr,
2276                    const struct message_item *message_item)
2277 {
2278   char *s = msg_to_string (message_item_get_msg (message_item));
2279   struct text_item *item = text_item_create (TEXT_ITEM_LOG, s);
2280   free (s);
2281   return xr_render_table (xr, text_item_to_table_item (item));
2282 }
2283
2284 static struct xr_render_fsm *
2285 xr_render_output_item (struct xr_driver *xr,
2286                        const struct output_item *output_item)
2287 {
2288   if (is_table_item (output_item))
2289     return xr_render_table (xr, table_item_ref (to_table_item (output_item)));
2290   else if (is_chart_item (output_item))
2291     return xr_render_chart (to_chart_item (output_item));
2292   else if (is_text_item (output_item))
2293     return xr_render_text (xr, to_text_item (output_item));
2294   else if (is_message_item (output_item))
2295     return xr_render_message (xr, to_message_item (output_item));
2296   else
2297     return NULL;
2298 }
2299
2300 bool
2301 xr_draw_svg_file (struct xr_rendering *r,
2302                   const char *filename)
2303 {
2304   int width, height;
2305   g_assert (r);
2306   xr_rendering_measure (r, &width, &height);
2307   cairo_surface_t *surface = cairo_svg_surface_create (filename, width, height);
2308   if (!surface)
2309     {
2310       g_error ("Could not create cairo svg surface with file %s", filename);
2311       return FALSE;
2312     }
2313   cairo_t *cr = cairo_create (surface);
2314   xr_rendering_draw (r, cr, 0, 0, width, height);
2315   cairo_destroy (cr);
2316   cairo_surface_destroy (surface);
2317   return TRUE;
2318 }