1dc8fe09668b1969ecf2a4b85f7b8dc1c20d7192
[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/message.h"
24 #include "libpspp/pool.h"
25 #include "libpspp/start-date.h"
26 #include "libpspp/str.h"
27 #include "libpspp/string-map.h"
28 #include "libpspp/version.h"
29 #include "data/file-handle-def.h"
30 #include "output/cairo-chart.h"
31 #include "output/chart-item-provider.h"
32 #include "output/charts/boxplot.h"
33 #include "output/charts/np-plot.h"
34 #include "output/charts/piechart.h"
35 #include "output/charts/barchart.h"
36 #include "output/charts/plot-hist.h"
37 #include "output/charts/roc-chart.h"
38 #include "output/charts/spreadlevel-plot.h"
39 #include "output/charts/scree.h"
40 #include "output/charts/scatterplot.h"
41 #include "output/driver-provider.h"
42 #include "output/group-item.h"
43 #include "output/message-item.h"
44 #include "output/options.h"
45 #include "output/page-setup-item.h"
46 #include "output/render.h"
47 #include "output/tab.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 <math.h>
57 #include <pango/pango-font.h>
58 #include <pango/pango-layout.h>
59 #include <pango/pango.h>
60 #include <pango/pangocairo.h>
61 #include <stdlib.h>
62
63 #include "gl/c-ctype.h"
64 #include "gl/c-strcase.h"
65 #include "gl/intprops.h"
66 #include "gl/minmax.h"
67 #include "gl/xalloc.h"
68
69 #include "gettext.h"
70 #define _(msgid) gettext (msgid)
71
72 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
73 #define H TABLE_HORZ
74 #define V TABLE_VERT
75
76 /* The unit used for internal measurements is inch/(72 * XR_POINT).
77    (Thus, XR_POINT units represent one point.) */
78 #define XR_POINT PANGO_SCALE
79
80 /* Conversions to and from points. */
81 static double
82 xr_to_pt (int x)
83 {
84   return x / (double) XR_POINT;
85 }
86
87 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
88 static int
89 px_to_xr (int x)
90 {
91   return x * (PANGO_SCALE * 72 / 96);
92 }
93
94 /* Dimensions for drawing lines in tables. */
95 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
96 #define XR_LINE_SPACE XR_POINT       /* Space between double lines. */
97
98 /* Output types. */
99 enum xr_output_type
100   {
101     XR_PDF,
102     XR_PS,
103     XR_SVG
104   };
105
106 /* Cairo fonts. */
107 enum xr_font_type
108   {
109     XR_FONT_PROPORTIONAL,
110     XR_FONT_EMPHASIS,
111     XR_FONT_FIXED,
112     XR_N_FONTS
113   };
114
115 /* A font for use with Cairo. */
116 struct xr_font
117   {
118     PangoFontDescription *desc;
119     PangoLayout *layout;
120   };
121
122 /* An output item whose rendering is in progress. */
123 struct xr_render_fsm
124   {
125     /* Renders as much of itself as it can on the current page.  Returns true
126        if rendering is complete, false if the output item needs another
127        page. */
128     bool (*render) (struct xr_render_fsm *, struct xr_driver *);
129
130     /* Destroys the output item. */
131     void (*destroy) (struct xr_render_fsm *);
132   };
133
134 /* Cairo output driver. */
135 struct xr_driver
136   {
137     struct output_driver driver;
138
139     /* User parameters. */
140     struct xr_font fonts[XR_N_FONTS];
141
142     int width;                  /* Page width minus margins. */
143     int length;                 /* Page length minus margins and header. */
144
145     int left_margin;            /* Left margin in inch/(72 * XR_POINT). */
146     int right_margin;           /* Right margin in inch/(72 * XR_POINT). */
147     int top_margin;             /* Top margin in inch/(72 * XR_POINT). */
148     int bottom_margin;          /* Bottom margin in inch/(72 * XR_POINT). */
149
150     int line_space;             /* Space between lines. */
151     int line_width;             /* Width of lines. */
152
153     int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
154     int object_spacing;         /* Space between output objects. */
155
156     struct xr_color bg;    /* Background color */
157     struct xr_color fg;    /* Foreground color */
158
159     int initial_page_number;
160
161     struct page_heading headings[2]; /* Top and bottom headings. */
162     int headings_height[2];
163
164     /* Internal state. */
165     struct render_params *params;
166     int char_width, char_height;
167     char *command_name;
168     char *title;
169     char *subtitle;
170     cairo_t *cairo;
171     cairo_surface_t *surface;
172     int page_number;            /* Current page number. */
173     int x, y;
174     struct xr_render_fsm *fsm;
175     int nest;
176     struct string_map heading_vars;
177   };
178
179 static const struct output_driver_class cairo_driver_class;
180
181 static void xr_driver_destroy_fsm (struct xr_driver *);
182 static void xr_driver_run_fsm (struct xr_driver *);
183
184 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
185                           enum render_line_style styles[TABLE_N_AXES][2],
186                           struct cell_color colors[TABLE_N_AXES][2]);
187 static void xr_measure_cell_width (void *, const struct table_cell *,
188                                    int *min, int *max);
189 static int xr_measure_cell_height (void *, const struct table_cell *,
190                                    int width);
191 static void xr_draw_cell (void *, const struct table_cell *, int color_idx,
192                           int bb[TABLE_N_AXES][2],
193                           int spill[TABLE_N_AXES][2],
194                           int clip[TABLE_N_AXES][2]);
195 static int xr_adjust_break (void *, const struct table_cell *,
196                             int width, int height);
197
198 static struct xr_render_fsm *xr_render_output_item (
199   struct xr_driver *, const struct output_item *);
200 \f
201 /* Output driver basics. */
202
203 static struct xr_driver *
204 xr_driver_cast (struct output_driver *driver)
205 {
206   assert (driver->class == &cairo_driver_class);
207   return UP_CAST (driver, struct xr_driver, driver);
208 }
209
210 static struct driver_option *
211 opt (struct output_driver *d, struct string_map *options, const char *key,
212      const char *default_value)
213 {
214   return driver_option_get (d, options, key, default_value);
215 }
216
217 /* Parse color information specified by KEY into {RED,GREEN,BLUE}.
218    Currently, the input string must be of the form "#RRRRGGGGBBBB"
219    Future implementations might allow things like "yellow" and
220    "sky-blue-ultra-brown"
221 */
222 void
223 parse_color (struct output_driver *d, struct string_map *options,
224              const char *key, const char *default_value,
225              struct xr_color *color)
226 {
227   int red, green, blue;
228   char *string = parse_string (opt (d, options, key, default_value));
229
230   if (3 != sscanf (string, "#%04x%04x%04x", &red, &green, &blue))
231     {
232       /* If the parsed option string fails, then try the default value */
233       if ( 3 != sscanf (default_value, "#%04x%04x%04x", &red, &green, &blue))
234         {
235           /* ... and if that fails set everything to zero */
236           red = green = blue = 0;
237         }
238     }
239
240   free (string);
241
242   /* Convert 16 bit ints to float */
243   color->red = red / (double) 0xFFFF;
244   color->green = green / (double) 0xFFFF;
245   color->blue = blue / (double) 0xFFFF;
246 }
247
248 static PangoFontDescription *
249 parse_font (const char *font, int default_size, bool bold, bool italic)
250 {
251   if (!c_strcasecmp (font, "Monospaced"))
252     font = "Monospace";
253
254   PangoFontDescription *desc = pango_font_description_from_string (font);
255   if (desc == NULL)
256     return NULL;
257
258   /* If the font description didn't include an explicit font size, then set it
259      to DEFAULT_SIZE, which is in inch/72000 units. */
260   if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
261     pango_font_description_set_size (desc,
262                                      (default_size / 1000.0) * PANGO_SCALE);
263
264   pango_font_description_set_weight (desc, (bold
265                                             ? PANGO_WEIGHT_BOLD
266                                             : PANGO_WEIGHT_NORMAL));
267   pango_font_description_set_style (desc, (italic
268                                            ? PANGO_STYLE_ITALIC
269                                            : PANGO_STYLE_NORMAL));
270
271   return desc;
272 }
273
274 static PangoFontDescription *
275 parse_font_option (struct output_driver *d, struct string_map *options,
276                    const char *key, const char *default_value,
277                    int default_size, bool bold, bool italic)
278 {
279   char *string = parse_string (opt (d, options, key, default_value));
280   PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
281   if (!desc)
282     {
283       msg (MW, _("`%s': bad font specification"), string);
284
285       /* Fall back to DEFAULT_VALUE, which had better be a valid font
286          description. */
287       desc = parse_font (default_value, default_size, bold, italic);
288       assert (desc != NULL);
289     }
290   free (string);
291
292   return desc;
293 }
294
295 static void
296 apply_options (struct xr_driver *xr, struct string_map *o)
297 {
298   struct output_driver *d = &xr->driver;
299
300   /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
301   int left_margin, right_margin;
302   int top_margin, bottom_margin;
303   int paper_width, paper_length;
304   int font_size;
305   int min_break[TABLE_N_AXES];
306
307   /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
308   const double scale = XR_POINT / 1000.;
309
310   int i;
311
312   for (i = 0; i < XR_N_FONTS; i++)
313     {
314       struct xr_font *font = &xr->fonts[i];
315
316       if (font->desc != NULL)
317         pango_font_description_free (font->desc);
318     }
319
320   font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
321   xr->fonts[XR_FONT_FIXED].desc = parse_font_option
322     (d, o, "fixed-font", "monospace", font_size, false, false);
323   xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font_option (
324     d, o, "prop-font", "sans serif", font_size, false, false);
325   xr->fonts[XR_FONT_EMPHASIS].desc = parse_font_option (
326     d, o, "emph-font", "sans serif", font_size, false, true);
327
328   parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg);
329   parse_color (d, o, "foreground-color", "#000000000000", &xr->fg);
330
331   /* Get dimensions.  */
332   parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
333   left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
334   right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
335   top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
336   bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
337
338   min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
339   min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
340
341   int object_spacing = (parse_dimension (opt (d, o, "object-spacing", NULL))
342                         * scale);
343
344   /* Convert to inch/(XR_POINT * 72). */
345   xr->left_margin = left_margin * scale;
346   xr->right_margin = right_margin * scale;
347   xr->top_margin = top_margin * scale;
348   xr->bottom_margin = bottom_margin * scale;
349   xr->width = (paper_width - left_margin - right_margin) * scale;
350   xr->length = (paper_length - top_margin - bottom_margin) * scale;
351   xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
352   xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
353   xr->object_spacing = object_spacing >= 0 ? object_spacing : XR_POINT * 12;
354
355   /* There are no headings so headings_height can stay 0. */
356 }
357
358 static struct xr_driver *
359 xr_allocate (const char *name, int device_type, struct string_map *o)
360 {
361   struct xr_driver *xr = xzalloc (sizeof *xr);
362   struct output_driver *d = &xr->driver;
363
364   output_driver_init (d, &cairo_driver_class, name, device_type);
365
366   string_map_init (&xr->heading_vars);
367
368   apply_options (xr, o);
369
370   return xr;
371 }
372
373 static int
374 pango_to_xr (int pango)
375 {
376   return (XR_POINT != PANGO_SCALE
377           ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
378           : pango);
379 }
380
381 static int
382 xr_to_pango (int xr)
383 {
384   return (XR_POINT != PANGO_SCALE
385           ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
386           : xr);
387 }
388
389 static void
390 xr_measure_fonts (cairo_t *cairo, const struct xr_font fonts[XR_N_FONTS],
391                   int *char_width, int *char_height)
392 {
393   *char_width = 0;
394   *char_height = 0;
395   for (int i = 0; i < XR_N_FONTS; i++)
396     {
397       PangoLayout *layout = pango_cairo_create_layout (cairo);
398       pango_layout_set_font_description (layout, fonts[i].desc);
399
400       pango_layout_set_text (layout, "0", 1);
401
402       int cw, ch;
403       pango_layout_get_size (layout, &cw, &ch);
404       *char_width = MAX (*char_width, pango_to_xr (cw));
405       *char_height = MAX (*char_height, pango_to_xr (ch));
406
407       g_object_unref (G_OBJECT (layout));
408     }
409 }
410
411 static int
412 get_layout_height (PangoLayout *layout)
413 {
414   int w, h;
415   pango_layout_get_size (layout, &w, &h);
416   return h;
417 }
418
419 static int
420 xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
421                         const struct page_heading *ph, int page_number,
422                         int width, bool draw, int base_y)
423 {
424   PangoLayout *layout = pango_cairo_create_layout (cairo);
425   pango_layout_set_font_description (layout, font);
426
427   int y = 0;
428   for (size_t i = 0; i < ph->n; i++)
429     {
430       const struct page_paragraph *pp = &ph->paragraphs[i];
431
432       char *markup = output_driver_substitute_heading_vars (pp->markup,
433                                                             page_number);
434       pango_layout_set_markup (layout, markup, -1);
435       free (markup);
436
437       pango_layout_set_alignment (
438         layout,
439         (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
440          : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER
441          : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
442          : PANGO_ALIGN_RIGHT));
443       pango_layout_set_width (layout, xr_to_pango (width));
444       if (draw)
445         {
446           cairo_save (cairo);
447           cairo_translate (cairo, 0, xr_to_pt (y + base_y));
448           pango_cairo_show_layout (cairo, layout);
449           cairo_restore (cairo);
450         }
451
452       y += pango_to_xr (get_layout_height (layout));
453     }
454
455   g_object_unref (G_OBJECT (layout));
456
457   return y;
458 }
459
460 static int
461 xr_measure_headings (cairo_surface_t *surface,
462                      const PangoFontDescription *font,
463                      const struct page_heading headings[2],
464                      int width, int object_spacing, int height[2])
465 {
466   cairo_t *cairo = cairo_create (surface);
467   int total = 0;
468   for (int i = 0; i < 2; i++)
469     {
470       int h = xr_render_page_heading (cairo, font, &headings[i], -1,
471                                       width, false, 0);
472
473       /* If the top heading is nonempty, add some space below it. */
474       if (h && i == 0)
475         h += object_spacing;
476
477       if (height)
478         height[i] = h;
479       total += h;
480     }
481   cairo_destroy (cairo);
482   return total;
483 }
484
485 static bool
486 xr_check_fonts (cairo_surface_t *surface,
487                 const struct xr_font fonts[XR_N_FONTS],
488                 int usable_width, int usable_length)
489 {
490   cairo_t *cairo = cairo_create (surface);
491   int char_width, char_height;
492   xr_measure_fonts (cairo, fonts, &char_width, &char_height);
493   cairo_destroy (cairo);
494
495   bool ok = true;
496   enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
497   if (usable_width / char_width < MIN_WIDTH)
498     {
499       msg (ME, _("The defined page is not wide enough to hold at least %d "
500                  "characters in the default font.  In fact, there's only "
501                  "room for %d characters."),
502            MIN_WIDTH, usable_width / char_width);
503       ok = false;
504     }
505   if (usable_length / char_height < MIN_LENGTH)
506     {
507       msg (ME, _("The defined page is not long enough to hold at least %d "
508                  "lines in the default font.  In fact, there's only "
509                  "room for %d lines."),
510            MIN_LENGTH, usable_length / char_height);
511       ok = false;
512     }
513   return ok;
514 }
515
516 static void
517 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
518 {
519   xr->cairo = cairo;
520
521   cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
522
523   xr_measure_fonts (xr->cairo, xr->fonts, &xr->char_width, &xr->char_height);
524
525   for (int i = 0; i < XR_N_FONTS; i++)
526     {
527       struct xr_font *font = &xr->fonts[i];
528       font->layout = pango_cairo_create_layout (cairo);
529       pango_layout_set_font_description (font->layout, font->desc);
530     }
531
532   if (xr->params == NULL)
533     {
534       xr->params = xmalloc (sizeof *xr->params);
535       xr->params->draw_line = xr_draw_line;
536       xr->params->measure_cell_width = xr_measure_cell_width;
537       xr->params->measure_cell_height = xr_measure_cell_height;
538       xr->params->adjust_break = xr_adjust_break;
539       xr->params->draw_cell = xr_draw_cell;
540       xr->params->aux = xr;
541       xr->params->size[H] = xr->width;
542       xr->params->size[V] = xr->length;
543       xr->params->font_size[H] = xr->char_width;
544       xr->params->font_size[V] = xr->char_height;
545
546       int lw = XR_LINE_WIDTH;
547       int ls = XR_LINE_SPACE;
548       for (int i = 0; i < TABLE_N_AXES; i++)
549         {
550           xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
551           xr->params->line_widths[i][RENDER_LINE_SINGLE] = lw;
552           xr->params->line_widths[i][RENDER_LINE_DASHED] = lw;
553           xr->params->line_widths[i][RENDER_LINE_THICK] = lw * 2;
554           xr->params->line_widths[i][RENDER_LINE_THIN] = lw / 2;
555           xr->params->line_widths[i][RENDER_LINE_DOUBLE] = 2 * lw + ls;
556         }
557
558       for (int i = 0; i < TABLE_N_AXES; i++)
559         xr->params->min_break[i] = xr->min_break[i];
560       xr->params->supports_margins = true;
561       xr->params->rtl = render_direction_rtl ();
562     }
563
564   cairo_set_source_rgb (xr->cairo, xr->fg.red, xr->fg.green, xr->fg.blue);
565 }
566
567 static struct output_driver *
568 xr_create (const char *file_name, enum settings_output_devices device_type,
569            struct string_map *o, enum xr_output_type file_type)
570 {
571   struct xr_driver *xr;
572   cairo_status_t status;
573   double width_pt, length_pt;
574
575   xr = xr_allocate (file_name, device_type, o);
576
577   width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
578   length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
579   if (file_type == XR_PDF)
580     xr->surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
581   else if (file_type == XR_PS)
582     xr->surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
583   else if (file_type == XR_SVG)
584     xr->surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
585   else
586     NOT_REACHED ();
587
588   status = cairo_surface_status (xr->surface);
589   if (status != CAIRO_STATUS_SUCCESS)
590     {
591       msg (ME, _("error opening output file `%s': %s"),
592              file_name, cairo_status_to_string (status));
593       goto error;
594     }
595
596   if (!xr_check_fonts (xr->surface, xr->fonts, xr->width, xr->length))
597     goto error;
598
599   return &xr->driver;
600
601  error:
602   output_driver_destroy (&xr->driver);
603   return NULL;
604 }
605
606 static struct output_driver *
607 xr_pdf_create (struct  file_handle *fh, enum settings_output_devices device_type,
608                struct string_map *o)
609 {
610   struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_PDF);
611   fh_unref (fh);
612   return od ;
613 }
614
615 static struct output_driver *
616 xr_ps_create (struct  file_handle *fh, enum settings_output_devices device_type,
617                struct string_map *o)
618 {
619   struct output_driver *od =  xr_create (fh_get_file_name (fh), device_type, o, XR_PS);
620   fh_unref (fh);
621   return od ;
622 }
623
624 static struct output_driver *
625 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
626                struct string_map *o)
627 {
628   struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_SVG);
629   fh_unref (fh);
630   return od ;
631 }
632
633 static void
634 xr_destroy (struct output_driver *driver)
635 {
636   struct xr_driver *xr = xr_driver_cast (driver);
637   size_t i;
638
639   xr_driver_destroy_fsm (xr);
640
641   if (xr->cairo != NULL)
642     {
643       cairo_surface_finish (xr->surface);
644       cairo_status_t status = cairo_status (xr->cairo);
645       if (status != CAIRO_STATUS_SUCCESS)
646         msg (ME, _("error drawing output for %s driver: %s"),
647                output_driver_get_name (driver),
648                cairo_status_to_string (status));
649       cairo_surface_destroy (xr->surface);
650
651       cairo_destroy (xr->cairo);
652     }
653
654   for (i = 0; i < XR_N_FONTS; i++)
655     {
656       struct xr_font *font = &xr->fonts[i];
657
658       if (font->desc != NULL)
659         pango_font_description_free (font->desc);
660       if (font->layout != NULL)
661         g_object_unref (font->layout);
662     }
663
664   free (xr->params);
665   free (xr);
666 }
667
668 static void
669 xr_flush (struct output_driver *driver)
670 {
671   struct xr_driver *xr = xr_driver_cast (driver);
672
673   cairo_surface_flush (cairo_get_target (xr->cairo));
674 }
675
676 static void
677 xr_update_page_setup (struct output_driver *driver,
678                       const struct page_setup *ps)
679 {
680   struct xr_driver *xr = xr_driver_cast (driver);
681
682   xr->initial_page_number = ps->initial_page_number;
683   xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
684
685   if (xr->cairo)
686     return;
687
688   int usable[TABLE_N_AXES];
689   for (int i = 0; i < 2; i++)
690     usable[i] = (ps->paper[i]
691                  - (ps->margins[i][0] + ps->margins[i][1])) * 72 * XR_POINT;
692
693   int headings_height[2];
694   usable[V] -= xr_measure_headings (
695     xr->surface, xr->fonts[XR_FONT_PROPORTIONAL].desc, ps->headings,
696     usable[H], xr->object_spacing, headings_height);
697
698   enum table_axis h = ps->orientation == PAGE_LANDSCAPE;
699   enum table_axis v = !h;
700   if (!xr_check_fonts (xr->surface, xr->fonts, usable[h], usable[v]))
701     return;
702
703   for (int i = 0; i < 2; i++)
704     {
705       page_heading_uninit (&xr->headings[i]);
706       page_heading_copy (&xr->headings[i], &ps->headings[i]);
707       xr->headings_height[i] = headings_height[i];
708     }
709   xr->width = usable[h];
710   xr->length = usable[v];
711   xr->left_margin = ps->margins[h][0] * 72 * XR_POINT;
712   xr->right_margin = ps->margins[h][1] * 72 * XR_POINT;
713   xr->top_margin = ps->margins[v][0] * 72 * XR_POINT;
714   xr->bottom_margin = ps->margins[v][1] * 72 * XR_POINT;
715   cairo_pdf_surface_set_size (xr->surface,
716                               ps->paper[h] * 72.0, ps->paper[v] * 72.0);
717 }
718
719 static void
720 xr_submit (struct output_driver *driver, const struct output_item *output_item)
721 {
722   struct xr_driver *xr = xr_driver_cast (driver);
723
724   if (is_page_setup_item (output_item))
725     {
726       xr_update_page_setup (driver,
727                             to_page_setup_item (output_item)->page_setup);
728       return;
729     }
730
731   if (!xr->cairo)
732     {
733       xr->page_number = xr->initial_page_number - 1;
734       xr_set_cairo (xr, cairo_create (xr->surface));
735       cairo_save (xr->cairo);
736       xr_driver_next_page (xr, xr->cairo);
737     }
738
739   xr_driver_output_item (xr, output_item);
740   while (xr_driver_need_new_page (xr))
741     {
742       cairo_restore (xr->cairo);
743       cairo_show_page (xr->cairo);
744       cairo_save (xr->cairo);
745       xr_driver_next_page (xr, xr->cairo);
746     }
747 }
748 \f
749 /* Functions for rendering a series of output items to a series of Cairo
750    contexts, with pagination.
751
752    Used by PSPPIRE for printing, and by the basic Cairo output driver above as
753    its underlying implementation.
754
755    See the big comment in cairo.h for intended usage. */
756
757 /* Gives new page CAIRO to XR for output. */
758 void
759 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
760 {
761   cairo_save (cairo);
762   cairo_set_source_rgb (cairo, xr->bg.red, xr->bg.green, xr->bg.blue);
763   cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
764   cairo_fill (cairo);
765   cairo_restore (cairo);
766
767   cairo_translate (cairo,
768                    xr_to_pt (xr->left_margin),
769                    xr_to_pt (xr->top_margin + xr->headings_height[0]));
770
771   xr->page_number++;
772   xr->cairo = cairo;
773   xr->x = xr->y = 0;
774
775   xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
776                           &xr->headings[0], xr->page_number, xr->width, true,
777                           -xr->headings_height[0]);
778   xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
779                           &xr->headings[1], xr->page_number, xr->width, true,
780                           xr->length);
781
782   xr_driver_run_fsm (xr);
783 }
784
785 /* Start rendering OUTPUT_ITEM to XR.  Only valid if XR is not in the middle of
786    rendering a previous output item, that is, only if xr_driver_need_new_page()
787    returns false. */
788 void
789 xr_driver_output_item (struct xr_driver *xr,
790                        const struct output_item *output_item)
791 {
792   assert (xr->fsm == NULL);
793   xr->fsm = xr_render_output_item (xr, output_item);
794   xr_driver_run_fsm (xr);
795 }
796
797 /* Returns true if XR is in the middle of rendering an output item and needs a
798    new page to be appended using xr_driver_next_page() to make progress,
799    otherwise false. */
800 bool
801 xr_driver_need_new_page (const struct xr_driver *xr)
802 {
803   return xr->fsm != NULL;
804 }
805
806 /* Returns true if the current page doesn't have any content yet. */
807 bool
808 xr_driver_is_page_blank (const struct xr_driver *xr)
809 {
810   return xr->y == 0;
811 }
812
813 static void
814 xr_driver_destroy_fsm (struct xr_driver *xr)
815 {
816   if (xr->fsm != NULL)
817     {
818       xr->fsm->destroy (xr->fsm);
819       xr->fsm = NULL;
820     }
821 }
822
823 static void
824 xr_driver_run_fsm (struct xr_driver *xr)
825 {
826   if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
827     xr_driver_destroy_fsm (xr);
828 }
829 \f
830 static void
831 xr_layout_cell (struct xr_driver *, const struct table_cell *,
832                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
833                 int *width, int *height, int *brk);
834
835 static void
836 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
837            const struct cell_color *color)
838 {
839   cairo_new_path (xr->cairo);
840   cairo_set_source_rgb (xr->cairo,
841                         color->r / 255.0, color->g / 255.0, color->b / 255.0);
842   cairo_set_line_width (
843     xr->cairo,
844     xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
845               : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
846               : XR_LINE_WIDTH));
847   cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
848   cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
849   cairo_stroke (xr->cairo);
850 }
851
852 static void UNUSED
853 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
854 {
855   cairo_new_path (xr->cairo);
856   cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
857   cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
858   cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y0 + xr->y));
859   cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
860   cairo_line_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y1 + xr->y));
861   cairo_close_path (xr->cairo);
862   cairo_stroke (xr->cairo);
863 }
864
865 static void
866 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
867 {
868   cairo_new_path (xr->cairo);
869   cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
870   cairo_rectangle (xr->cairo,
871                    xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y),
872                    xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
873   cairo_fill (xr->cairo);
874 }
875
876 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
877    shortening it to X0...X1 if SHORTEN is true.
878    Draws a horizontal line X1...X3 at Y if RIGHT says so,
879    shortening it to X2...X3 if SHORTEN is true. */
880 static void
881 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
882            enum render_line_style left, enum render_line_style right,
883            const struct cell_color *left_color,
884            const struct cell_color *right_color,
885            bool shorten)
886 {
887   if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
888       && cell_color_equal (left_color, right_color))
889     dump_line (xr, x0, y, x3, y, left, left_color);
890   else
891     {
892       if (left != RENDER_LINE_NONE)
893         dump_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
894       if (right != RENDER_LINE_NONE)
895         dump_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
896     }
897 }
898
899 /* Draws a vertical line Y0...Y2 at X if TOP says so,
900    shortening it to Y0...Y1 if SHORTEN is true.
901    Draws a vertical line Y1...Y3 at X if BOTTOM says so,
902    shortening it to Y2...Y3 if SHORTEN is true. */
903 static void
904 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
905            enum render_line_style top, enum render_line_style bottom,
906            const struct cell_color *top_color,
907            const struct cell_color *bottom_color,
908            bool shorten)
909 {
910   if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
911       && cell_color_equal (top_color, bottom_color))
912     dump_line (xr, x, y0, x, y3, top, top_color);
913   else
914     {
915       if (top != RENDER_LINE_NONE)
916         dump_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
917       if (bottom != RENDER_LINE_NONE)
918         dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
919     }
920 }
921
922 static void
923 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
924               enum render_line_style styles[TABLE_N_AXES][2],
925               struct cell_color colors[TABLE_N_AXES][2])
926 {
927   const int x0 = bb[H][0];
928   const int y0 = bb[V][0];
929   const int x3 = bb[H][1];
930   const int y3 = bb[V][1];
931   const int top = styles[H][0];
932   const int bottom = styles[H][1];
933
934   int start_side = render_direction_rtl();
935   int end_side = !start_side;
936   const int start_of_line = styles[V][start_side];
937   const int end_of_line   = styles[V][end_side];
938   const struct cell_color *top_color = &colors[H][0];
939   const struct cell_color *bottom_color = &colors[H][1];
940   const struct cell_color *start_color = &colors[V][start_side];
941   const struct cell_color *end_color = &colors[V][end_side];
942
943   /* The algorithm here is somewhat subtle, to allow it to handle
944      all the kinds of intersections that we need.
945
946      Three additional ordinates are assigned along the x axis.  The
947      first is xc, midway between x0 and x3.  The others are x1 and
948      x2; for a single vertical line these are equal to xc, and for
949      a double vertical line they are the ordinates of the left and
950      right half of the double line.
951
952      yc, y1, and y2 are assigned similarly along the y axis.
953
954      The following diagram shows the coordinate system and output
955      for double top and bottom lines, single left line, and no
956      right line:
957
958                  x0       x1 xc  x2      x3
959                y0 ________________________
960                   |        #     #       |
961                   |        #     #       |
962                   |        #     #       |
963                   |        #     #       |
964                   |        #     #       |
965      y1 = y2 = yc |#########     #       |
966                   |        #     #       |
967                   |        #     #       |
968                   |        #     #       |
969                   |        #     #       |
970                y3 |________#_____#_______|
971   */
972   struct xr_driver *xr = xr_;
973
974   /* Offset from center of each line in a pair of double lines. */
975   int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
976
977   /* Are the lines along each axis single or double?
978      (It doesn't make sense to have different kinds of line on the
979      same axis, so we don't try to gracefully handle that case.) */
980   bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
981   bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
982
983   /* When horizontal lines are doubled,
984      the left-side line along y1 normally runs from x0 to x2,
985      and the right-side line along y1 from x3 to x1.
986      If the top-side line is also doubled, we shorten the y1 lines,
987      so that the left-side line runs only to x1,
988      and the right-side line only to x2.
989      Otherwise, the horizontal line at y = y1 below would cut off
990      the intersection, which looks ugly:
991                x0       x1     x2      x3
992              y0 ________________________
993                 |        #     #       |
994                 |        #     #       |
995                 |        #     #       |
996                 |        #     #       |
997              y1 |#########     ########|
998                 |                      |
999                 |                      |
1000              y2 |######################|
1001                 |                      |
1002                 |                      |
1003              y3 |______________________|
1004      It is more of a judgment call when the horizontal line is
1005      single.  We actually choose to cut off the line anyhow, as
1006      shown in the first diagram above.
1007   */
1008   bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
1009   bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
1010   bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
1011   int horz_line_ofs = double_vert ? double_line_ofs : 0;
1012   int xc = (x0 + x3) / 2;
1013   int x1 = xc - horz_line_ofs;
1014   int x2 = xc + horz_line_ofs;
1015
1016   bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
1017   bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
1018   bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
1019   int vert_line_ofs = double_horz ? double_line_ofs : 0;
1020   int yc = (y0 + y3) / 2;
1021   int y1 = yc - vert_line_ofs;
1022   int y2 = yc + vert_line_ofs;
1023
1024   if (!double_horz)
1025     horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
1026                start_color, end_color, shorten_yc_line);
1027   else
1028     {
1029       horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
1030                  start_color, end_color, shorten_y1_lines);
1031       horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
1032                  start_color, end_color, shorten_y2_lines);
1033     }
1034
1035   if (!double_vert)
1036     vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
1037                shorten_xc_line);
1038   else
1039     {
1040       vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
1041                  shorten_x1_lines);
1042       vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
1043                  shorten_x2_lines);
1044     }
1045 }
1046
1047 static void
1048 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
1049                        int *min_width, int *max_width)
1050 {
1051   struct xr_driver *xr = xr_;
1052   int bb[TABLE_N_AXES][2];
1053   int clip[TABLE_N_AXES][2];
1054   int h;
1055
1056   bb[H][0] = 0;
1057   bb[H][1] = INT_MAX;
1058   bb[V][0] = 0;
1059   bb[V][1] = INT_MAX;
1060   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1061   xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
1062
1063   bb[H][1] = 1;
1064   xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
1065
1066   if (*min_width > 0)
1067     *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
1068                             + cell->style->cell_style.margin[H][1]);
1069   if (*max_width > 0)
1070     *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
1071                             + cell->style->cell_style.margin[H][1]);
1072 }
1073
1074 static int
1075 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
1076 {
1077   struct xr_driver *xr = xr_;
1078   int bb[TABLE_N_AXES][2];
1079   int clip[TABLE_N_AXES][2];
1080   int w, h;
1081
1082   bb[H][0] = 0;
1083   bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1084                                + cell->style->cell_style.margin[H][1]);
1085   bb[V][0] = 0;
1086   bb[V][1] = INT_MAX;
1087   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1088   xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
1089   h += px_to_xr (cell->style->cell_style.margin[V][0]
1090                  + cell->style->cell_style.margin[V][1]);
1091   return h;
1092 }
1093
1094 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
1095
1096 static void
1097 xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
1098               int bb[TABLE_N_AXES][2],
1099               int spill[TABLE_N_AXES][2],
1100               int clip[TABLE_N_AXES][2])
1101 {
1102   struct xr_driver *xr = xr_;
1103   int w, h, brk;
1104
1105   cairo_save (xr->cairo);
1106   int bg_clip[TABLE_N_AXES][2];
1107   for (int axis = 0; axis < TABLE_N_AXES; axis++)
1108     {
1109       bg_clip[axis][0] = clip[axis][0];
1110       if (bb[axis][0] == clip[axis][0])
1111         bg_clip[axis][0] -= spill[axis][0];
1112
1113       bg_clip[axis][1] = clip[axis][1];
1114       if (bb[axis][1] == clip[axis][1])
1115         bg_clip[axis][1] += spill[axis][1];
1116     }
1117   xr_clip (xr, bg_clip);
1118   cairo_set_source_rgb (xr->cairo,
1119                         cell->style->font_style.bg[color_idx].r / 255.,
1120                         cell->style->font_style.bg[color_idx].g / 255.,
1121                         cell->style->font_style.bg[color_idx].b / 255.);
1122   fill_rectangle (xr,
1123                   bb[H][0] - spill[H][0],
1124                   bb[V][0] - spill[V][0],
1125                   bb[H][1] + spill[H][1],
1126                   bb[V][1] + spill[V][1]);
1127   cairo_restore (xr->cairo);
1128
1129   cairo_save (xr->cairo);
1130   cairo_set_source_rgb (xr->cairo,
1131                         cell->style->font_style.fg[color_idx].r / 255.,
1132                         cell->style->font_style.fg[color_idx].g / 255.,
1133                         cell->style->font_style.fg[color_idx].b / 255.);
1134
1135   for (int axis = 0; axis < TABLE_N_AXES; axis++)
1136     {
1137       bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
1138       bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
1139     }
1140   if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
1141     xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1142   cairo_restore (xr->cairo);
1143 }
1144
1145 static int
1146 xr_adjust_break (void *xr_, const struct table_cell *cell,
1147                  int width, int height)
1148 {
1149   struct xr_driver *xr = xr_;
1150   int bb[TABLE_N_AXES][2];
1151   int clip[TABLE_N_AXES][2];
1152   int w, h, brk;
1153
1154   if (xr_measure_cell_height (xr_, cell, width) < height)
1155     return -1;
1156
1157   bb[H][0] = 0;
1158   bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1159                                + cell->style->cell_style.margin[H][1]);
1160   if (bb[H][1] <= 0)
1161     return 0;
1162   bb[V][0] = 0;
1163   bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
1164                                 + cell->style->cell_style.margin[V][1]);
1165   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1166   xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1167   return brk;
1168 }
1169 \f
1170 static void
1171 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
1172 {
1173   if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1174     {
1175       double x0 = xr_to_pt (clip[H][0] + xr->x);
1176       double y0 = xr_to_pt (clip[V][0] + xr->y);
1177       double x1 = xr_to_pt (clip[H][1] + xr->x);
1178       double y1 = xr_to_pt (clip[V][1] + xr->y);
1179
1180       cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1181       cairo_clip (xr->cairo);
1182     }
1183 }
1184
1185 static void
1186 add_attr_with_start (PangoAttrList *list, PangoAttribute *attr, guint start_index)
1187 {
1188   attr->start_index = start_index;
1189   pango_attr_list_insert (list, attr);
1190 }
1191
1192 static void
1193 markup_escape (const char *in, struct string *out)
1194 {
1195   for (int c = *in++; c; c = *in++)
1196     switch (c)
1197       {
1198       case '&':
1199         ds_put_cstr (out, "&amp;");
1200         break;
1201       case '<':
1202         ds_put_cstr (out, "&lt;");
1203         break;
1204       case '>':
1205         ds_put_cstr (out, "&gt;");
1206         break;
1207       default:
1208         ds_put_byte (out, c);
1209         break;
1210       }
1211 }
1212
1213 static int
1214 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
1215 {
1216   int size[TABLE_N_AXES];
1217   pango_layout_get_size (layout, &size[H], &size[V]);
1218   return size[axis];
1219 }
1220
1221 static int
1222 xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
1223                      int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1224                      int *widthp, int *brk)
1225 {
1226   const struct font_style *font_style = &cell->style->font_style;
1227   const struct cell_style *cell_style = &cell->style->cell_style;
1228   unsigned int options = cell->options;
1229
1230   enum table_axis X = options & TAB_ROTATE ? V : H;
1231   enum table_axis Y = !X;
1232   int R = options & TAB_ROTATE ? 0 : 1;
1233
1234   struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1235                           : options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
1236                           : &xr->fonts[XR_FONT_PROPORTIONAL]);
1237   struct xr_font local_font;
1238   if (font_style->typeface)
1239     {
1240       PangoFontDescription *desc = parse_font (
1241         font_style->typeface,
1242         font_style->size ? font_style->size * 1000 * 72 / 128 : 10000,
1243         font_style->bold, font_style->italic);
1244       if (desc)
1245         {
1246           PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1247           pango_layout_set_font_description (layout, desc);
1248
1249           local_font.desc = desc;
1250           local_font.layout = layout;
1251           font = &local_font;
1252         }
1253     }
1254
1255   const char *text = cell->text;
1256   enum table_halign halign = table_halign_interpret (
1257     cell_style->halign, cell->options & TAB_NUMERIC);
1258   if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
1259     {
1260       int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
1261
1262       const char *decimal = strrchr (text, cell_style->decimal_char);
1263       if (decimal)
1264         {
1265           pango_layout_set_text (font->layout, decimal, strlen (decimal));
1266           pango_layout_set_width (font->layout, -1);
1267           margin_adjustment += get_layout_dimension (font->layout, H);
1268         }
1269
1270       if (margin_adjustment < 0)
1271         bb[H][1] += margin_adjustment;
1272     }
1273
1274   struct string tmp = DS_EMPTY_INITIALIZER;
1275
1276   /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
1277      Pango's implementation of it): it will break after a period or a comma
1278      that precedes a digit, e.g. in ".000" it will break after the period.
1279      This code looks for such a situation and inserts a U+2060 WORD JOINER
1280      to prevent the break.
1281
1282      This isn't necessary when the decimal point is between two digits
1283      (e.g. "0.000" won't be broken) or when the display width is not limited so
1284      that word wrapping won't happen.
1285
1286      It isn't necessary to look for more than one period or comma, as would
1287      happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
1288      are present then there will always be a digit on both sides of every
1289      period and comma. */
1290   if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
1291     {
1292       const char *decimal = text + strcspn (text, ".,");
1293       if (decimal[0]
1294           && c_isdigit (decimal[1])
1295           && (decimal == text || !c_isdigit (decimal[-1])))
1296         {
1297           ds_extend (&tmp, strlen (text) + 16);
1298           ds_put_substring (&tmp, ss_buffer (text, decimal - text + 1));
1299           ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
1300           ds_put_cstr (&tmp, decimal + 1);
1301         }
1302     }
1303
1304   if (cell->n_footnotes)
1305     {
1306       int footnote_adjustment;
1307       if (cell->n_footnotes == 1 && halign == TABLE_HALIGN_RIGHT)
1308         {
1309           const char *marker = cell->footnotes[0]->marker;
1310           pango_layout_set_text (font->layout, marker, strlen (marker));
1311
1312           PangoAttrList *attrs = pango_attr_list_new ();
1313           pango_attr_list_insert (attrs, pango_attr_rise_new (7000));
1314           pango_layout_set_attributes (font->layout, attrs);
1315           pango_attr_list_unref (attrs);
1316
1317           int w = get_layout_dimension (font->layout, X);
1318           int right_margin = px_to_xr (cell_style->margin[X][R]);
1319           footnote_adjustment = MIN (w, right_margin);
1320
1321           pango_layout_set_attributes (font->layout, NULL);
1322         }
1323       else
1324         footnote_adjustment = px_to_xr (cell_style->margin[X][R]);
1325
1326       if (R)
1327         bb[X][R] += footnote_adjustment;
1328       else
1329         bb[X][R] -= footnote_adjustment;
1330
1331       if (ds_is_empty (&tmp))
1332         {
1333           ds_extend (&tmp, strlen (text) + 16);
1334           ds_put_cstr (&tmp, text);
1335         }
1336       size_t initial_length = ds_length (&tmp);
1337
1338       for (size_t i = 0; i < cell->n_footnotes; i++)
1339         {
1340           if (i)
1341             ds_put_byte (&tmp, ',');
1342
1343           const char *marker = cell->footnotes[i]->marker;
1344           if (options & TAB_MARKUP)
1345             markup_escape (marker, &tmp);
1346           else
1347             ds_put_cstr (&tmp, marker);
1348         }
1349
1350       if (options & TAB_MARKUP)
1351         pango_layout_set_markup (font->layout,
1352                                  ds_cstr (&tmp), ds_length (&tmp));
1353       else
1354         pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
1355
1356       PangoAttrList *attrs = pango_attr_list_new ();
1357       if (font_style->underline)
1358         pango_attr_list_insert (attrs, pango_attr_underline_new (
1359                                PANGO_UNDERLINE_SINGLE));
1360       add_attr_with_start (attrs, pango_attr_rise_new (7000), initial_length);
1361       add_attr_with_start (
1362         attrs, pango_attr_font_desc_new (font->desc), initial_length);
1363       pango_layout_set_attributes (font->layout, attrs);
1364       pango_attr_list_unref (attrs);
1365     }
1366   else
1367     {
1368       const char *content = ds_is_empty (&tmp) ? text : ds_cstr (&tmp);
1369       if (options & TAB_MARKUP)
1370         pango_layout_set_markup (font->layout, content, -1);
1371       else
1372         pango_layout_set_text (font->layout, content, -1);
1373
1374       if (font_style->underline)
1375         {
1376           PangoAttrList *attrs = pango_attr_list_new ();
1377           pango_attr_list_insert (attrs, pango_attr_underline_new (
1378                                     PANGO_UNDERLINE_SINGLE));
1379           pango_layout_set_attributes (font->layout, attrs);
1380           pango_attr_list_unref (attrs);
1381         }
1382     }
1383   ds_destroy (&tmp);
1384
1385   pango_layout_set_alignment (font->layout,
1386                               (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
1387                                : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
1388                                : PANGO_ALIGN_CENTER));
1389   pango_layout_set_width (
1390     font->layout,
1391     bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
1392   pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1393
1394   if (clip[H][0] != clip[H][1])
1395     {
1396       cairo_save (xr->cairo);
1397       if (!(options & TAB_ROTATE))
1398         xr_clip (xr, clip);
1399       if (options & TAB_ROTATE)
1400         {
1401           cairo_translate (xr->cairo,
1402                            xr_to_pt (bb[H][0] + xr->x),
1403                            xr_to_pt (bb[V][1] + xr->y));
1404           cairo_rotate (xr->cairo, -M_PI_2);
1405         }
1406       else
1407         cairo_translate (xr->cairo,
1408                          xr_to_pt (bb[H][0] + xr->x),
1409                          xr_to_pt (bb[V][0] + xr->y));
1410       pango_cairo_show_layout (xr->cairo, font->layout);
1411
1412       /* If enabled, this draws a blue rectangle around the extents of each
1413          line of text, which can be rather useful for debugging layout
1414          issues. */
1415       if (0)
1416         {
1417           PangoLayoutIter *iter;
1418           iter = pango_layout_get_iter (font->layout);
1419           do
1420             {
1421               PangoRectangle extents;
1422
1423               pango_layout_iter_get_line_extents (iter, &extents, NULL);
1424               cairo_save (xr->cairo);
1425               cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1426               dump_rectangle (xr,
1427                               pango_to_xr (extents.x) - xr->x,
1428                               pango_to_xr (extents.y) - xr->y,
1429                               pango_to_xr (extents.x + extents.width) - xr->x,
1430                               pango_to_xr (extents.y + extents.height) - xr->y);
1431               cairo_restore (xr->cairo);
1432             }
1433           while (pango_layout_iter_next_line (iter));
1434           pango_layout_iter_free (iter);
1435         }
1436
1437       cairo_restore (xr->cairo);
1438     }
1439
1440   int size[TABLE_N_AXES];
1441   pango_layout_get_size (font->layout, &size[H], &size[V]);
1442   int w = pango_to_xr (size[X]);
1443   int h = pango_to_xr (size[Y]);
1444   if (w > *widthp)
1445     *widthp = w;
1446   if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
1447     {
1448       PangoLayoutIter *iter;
1449       int best UNUSED = 0;
1450
1451       /* Choose a breakpoint between lines instead of in the middle of one. */
1452       iter = pango_layout_get_iter (font->layout);
1453       do
1454         {
1455           PangoRectangle extents;
1456           int y0, y1;
1457           int bottom;
1458
1459           pango_layout_iter_get_line_extents (iter, NULL, &extents);
1460           pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1461           extents.x = pango_to_xr (extents.x);
1462           extents.y = pango_to_xr (y0);
1463           extents.width = pango_to_xr (extents.width);
1464           extents.height = pango_to_xr (y1 - y0);
1465           bottom = bb[V][0] + extents.y + extents.height;
1466           if (bottom < bb[V][1])
1467             {
1468               if (brk && clip[H][0] != clip[H][1])
1469                 best = bottom;
1470               if (brk)
1471                 *brk = bottom;
1472             }
1473           else
1474             break;
1475         }
1476       while (pango_layout_iter_next_line (iter));
1477       pango_layout_iter_free (iter);
1478
1479       /* If enabled, draws a green line across the chosen breakpoint, which can
1480          be useful for debugging issues with breaking.  */
1481       if (0)
1482         {
1483           if (best && !xr->nest)
1484             dump_line (xr, -xr->left_margin, best,
1485                        xr->width + xr->right_margin, best,
1486                        RENDER_LINE_SINGLE,
1487                        &(struct cell_color) CELL_COLOR (0, 255, 0));
1488         }
1489     }
1490
1491   pango_layout_set_attributes (font->layout, NULL);
1492
1493   if (font == &local_font)
1494     {
1495       g_object_unref (G_OBJECT (font->layout));
1496       pango_font_description_free (font->desc);
1497     }
1498
1499   return h;
1500 }
1501
1502 static void
1503 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1504                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1505                 int *width, int *height, int *brk)
1506 {
1507   *width = 0;
1508   *height = 0;
1509
1510   /* If enabled, draws a blue rectangle around the cell extents, which can be
1511      useful for debugging layout. */
1512   if (0)
1513     {
1514       if (clip[H][0] != clip[H][1])
1515         {
1516           int offset = (xr->nest) * XR_POINT;
1517
1518           cairo_save (xr->cairo);
1519           cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1520           dump_rectangle (xr,
1521                           bb[H][0] + offset, bb[V][0] + offset,
1522                           bb[H][1] - offset, bb[V][1] - offset);
1523           cairo_restore (xr->cairo);
1524         }
1525     }
1526
1527   if (brk)
1528     *brk = bb[V][0];
1529   *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
1530 }
1531 \f
1532 struct output_driver_factory pdf_driver_factory =
1533   { "pdf", "pspp.pdf", xr_pdf_create };
1534 struct output_driver_factory ps_driver_factory =
1535   { "ps", "pspp.ps", xr_ps_create };
1536 struct output_driver_factory svg_driver_factory =
1537   { "svg", "pspp.svg", xr_svg_create };
1538
1539 static const struct output_driver_class cairo_driver_class =
1540 {
1541   "cairo",
1542   xr_destroy,
1543   xr_submit,
1544   xr_flush,
1545 };
1546 \f
1547 /* GUI rendering helpers. */
1548
1549 struct xr_rendering
1550   {
1551     struct output_item *item;
1552
1553     /* Table items. */
1554     struct render_pager *p;
1555     struct xr_driver *xr;
1556   };
1557
1558 #define CHART_WIDTH 500
1559 #define CHART_HEIGHT 375
1560
1561
1562
1563 struct xr_driver *
1564 xr_driver_create (cairo_t *cairo, struct string_map *options)
1565 {
1566   struct xr_driver *xr = xr_allocate ("cairo", 0, options);
1567   xr_set_cairo (xr, cairo);
1568   return xr;
1569 }
1570
1571 /* Destroy XR, which should have been created with xr_driver_create().  Any
1572    cairo_t added to XR is not destroyed, because it is owned by the client. */
1573 void
1574 xr_driver_destroy (struct xr_driver *xr)
1575 {
1576   if (xr != NULL)
1577     {
1578       xr->cairo = NULL;
1579       output_driver_destroy (&xr->driver);
1580     }
1581 }
1582
1583 static struct xr_rendering *
1584 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1585 {
1586   struct table_item *table_item;
1587   struct xr_rendering *r;
1588
1589   table_item = table_item_create (table_from_string (TABLE_HALIGN_LEFT, text),
1590                                   NULL, NULL);
1591   r = xr_rendering_create (xr, &table_item->output_item, cr);
1592   table_item_unref (table_item);
1593
1594   return r;
1595 }
1596
1597 void
1598 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1599 {
1600   if (is_table_item (xr->item))
1601     apply_options (xr->xr, o);
1602 }
1603
1604 struct xr_rendering *
1605 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1606                      cairo_t *cr)
1607 {
1608   struct xr_rendering *r = NULL;
1609
1610   if (is_text_item (item))
1611     r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1612                                   cr);
1613   else if (is_message_item (item))
1614     {
1615       const struct message_item *message_item = to_message_item (item);
1616       char *s = msg_to_string (message_item_get_msg (message_item));
1617       r = xr_rendering_create_text (xr, s, cr);
1618       free (s);
1619     }
1620   else if (is_table_item (item))
1621     {
1622       r = xzalloc (sizeof *r);
1623       r->item = output_item_ref (item);
1624       r->xr = xr;
1625       xr_set_cairo (xr, cr);
1626       r->p = render_pager_create (xr->params, to_table_item (item));
1627     }
1628   else if (is_chart_item (item))
1629     {
1630       r = xzalloc (sizeof *r);
1631       r->item = output_item_ref (item);
1632     }
1633   else if (is_group_open_item (item))
1634     r = xr_rendering_create_text (xr, to_group_open_item (item)->command_name,
1635                                   cr);
1636
1637   return r;
1638 }
1639
1640 void
1641 xr_rendering_destroy (struct xr_rendering *r)
1642 {
1643   if (r)
1644     {
1645       output_item_unref (r->item);
1646       render_pager_destroy (r->p);
1647       free (r);
1648     }
1649 }
1650
1651 void
1652 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
1653 {
1654   if (is_table_item (r->item))
1655     {
1656       *w = render_pager_get_size (r->p, H) / XR_POINT;
1657       *h = render_pager_get_size (r->p, V) / XR_POINT;
1658     }
1659   else
1660     {
1661       *w = CHART_WIDTH;
1662       *h = CHART_HEIGHT;
1663     }
1664 }
1665
1666 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1667                     double x, double y, double width, double height);
1668
1669 /* Draws onto CR */
1670 void
1671 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1672                    int x0, int y0, int x1, int y1)
1673 {
1674   if (is_table_item (r->item))
1675     {
1676       struct xr_driver *xr = r->xr;
1677
1678       xr_set_cairo (xr, cr);
1679
1680       render_pager_draw_region (r->p, x0 * XR_POINT, y0 * XR_POINT,
1681                                 (x1 - x0) * XR_POINT, (y1 - y0) * XR_POINT);
1682     }
1683   else
1684     xr_draw_chart (to_chart_item (r->item), cr,
1685                    0, 0, CHART_WIDTH, CHART_HEIGHT);
1686 }
1687
1688 static void
1689 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1690                double x, double y, double width, double height)
1691 {
1692   struct xrchart_geometry geom;
1693
1694   cairo_save (cr);
1695   cairo_translate (cr, x, y + height);
1696   cairo_scale (cr, 1.0, -1.0);
1697   xrchart_geometry_init (cr, &geom, width, height);
1698   if (is_boxplot (chart_item))
1699     xrchart_draw_boxplot (chart_item, cr, &geom);
1700   else if (is_histogram_chart (chart_item))
1701     xrchart_draw_histogram (chart_item, cr, &geom);
1702   else if (is_np_plot_chart (chart_item))
1703     xrchart_draw_np_plot (chart_item, cr, &geom);
1704   else if (is_piechart (chart_item))
1705     xrchart_draw_piechart (chart_item, cr, &geom);
1706   else if (is_barchart (chart_item))
1707     xrchart_draw_barchart (chart_item, cr, &geom);
1708   else if (is_roc_chart (chart_item))
1709     xrchart_draw_roc (chart_item, cr, &geom);
1710   else if (is_scree (chart_item))
1711     xrchart_draw_scree (chart_item, cr, &geom);
1712   else if (is_spreadlevel_plot_chart (chart_item))
1713     xrchart_draw_spreadlevel (chart_item, cr, &geom);
1714   else if (is_scatterplot_chart (chart_item))
1715     xrchart_draw_scatterplot (chart_item, cr, &geom);
1716   else
1717     NOT_REACHED ();
1718   xrchart_geometry_free (cr, &geom);
1719
1720   cairo_restore (cr);
1721 }
1722
1723 char *
1724 xr_draw_png_chart (const struct chart_item *item,
1725                    const char *file_name_template, int number,
1726                    const struct xr_color *fg,
1727                    const struct xr_color *bg
1728                    )
1729 {
1730   const int width = 640;
1731   const int length = 480;
1732
1733   cairo_surface_t *surface;
1734   cairo_status_t status;
1735   const char *number_pos;
1736   char *file_name;
1737   cairo_t *cr;
1738
1739   number_pos = strchr (file_name_template, '#');
1740   if (number_pos != NULL)
1741     file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1742                            file_name_template, number, number_pos + 1);
1743   else
1744     file_name = xstrdup (file_name_template);
1745
1746   surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1747   cr = cairo_create (surface);
1748
1749   cairo_set_source_rgb (cr, bg->red, bg->green, bg->blue);
1750   cairo_paint (cr);
1751
1752   cairo_set_source_rgb (cr, fg->red, fg->green, fg->blue);
1753
1754   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1755
1756   status = cairo_surface_write_to_png (surface, file_name);
1757   if (status != CAIRO_STATUS_SUCCESS)
1758     msg (ME, _("error writing output file `%s': %s"),
1759            file_name, cairo_status_to_string (status));
1760
1761   cairo_destroy (cr);
1762   cairo_surface_destroy (surface);
1763
1764   return file_name;
1765 }
1766 \f
1767 struct xr_table_state
1768   {
1769     struct xr_render_fsm fsm;
1770     struct render_pager *p;
1771   };
1772
1773 static bool
1774 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1775 {
1776   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1777
1778   while (render_pager_has_next (ts->p))
1779     {
1780       int used;
1781
1782       used = render_pager_draw_next (ts->p, xr->length - xr->y);
1783       if (!used)
1784         {
1785           assert (xr->y > 0);
1786           return true;
1787         }
1788       else
1789         xr->y += used;
1790     }
1791   return false;
1792 }
1793
1794 static void
1795 xr_table_destroy (struct xr_render_fsm *fsm)
1796 {
1797   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1798
1799   render_pager_destroy (ts->p);
1800   free (ts);
1801 }
1802
1803 static struct xr_render_fsm *
1804 xr_render_table (struct xr_driver *xr, struct table_item *table_item)
1805 {
1806   struct xr_table_state *ts;
1807
1808   ts = xmalloc (sizeof *ts);
1809   ts->fsm.render = xr_table_render;
1810   ts->fsm.destroy = xr_table_destroy;
1811
1812   if (xr->y > 0)
1813     xr->y += xr->char_height;
1814
1815   ts->p = render_pager_create (xr->params, table_item);
1816   table_item_unref (table_item);
1817
1818   return &ts->fsm;
1819 }
1820 \f
1821 struct xr_chart_state
1822   {
1823     struct xr_render_fsm fsm;
1824     struct chart_item *chart_item;
1825   };
1826
1827 static bool
1828 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1829 {
1830   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1831
1832   const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
1833
1834   if (xr->y > xr->length - chart_height)
1835     return true;
1836
1837   if (xr->cairo != NULL)
1838     {
1839       xr_draw_chart (cs->chart_item, xr->cairo,
1840                      0.0,
1841                      xr_to_pt (xr->y),
1842                      xr_to_pt (xr->width),
1843                      xr_to_pt (chart_height));
1844     }
1845   xr->y += chart_height;
1846
1847   return false;
1848 }
1849
1850 static void
1851 xr_chart_destroy (struct xr_render_fsm *fsm)
1852 {
1853   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1854
1855   chart_item_unref (cs->chart_item);
1856   free (cs);
1857 }
1858
1859 static struct xr_render_fsm *
1860 xr_render_chart (const struct chart_item *chart_item)
1861 {
1862   struct xr_chart_state *cs;
1863
1864   cs = xmalloc (sizeof *cs);
1865   cs->fsm.render = xr_chart_render;
1866   cs->fsm.destroy = xr_chart_destroy;
1867   cs->chart_item = chart_item_ref (chart_item);
1868
1869   return &cs->fsm;
1870 }
1871 \f
1872 static bool
1873 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1874 {
1875   return xr->y > 0;
1876 }
1877
1878 static void
1879 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1880 {
1881   /* Nothing to do. */
1882 }
1883
1884 static struct xr_render_fsm *
1885 xr_render_eject (void)
1886 {
1887   static struct xr_render_fsm eject_renderer =
1888     {
1889       xr_eject_render,
1890       xr_eject_destroy
1891     };
1892
1893   return &eject_renderer;
1894 }
1895 \f
1896 static struct xr_render_fsm *
1897 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1898 {
1899   enum text_item_type type = text_item_get_type (text_item);
1900   const char *text = text_item_get_text (text_item);
1901
1902   switch (type)
1903     {
1904     case TEXT_ITEM_PAGE_TITLE:
1905       string_map_replace (&xr->heading_vars, "PageTitle", text);
1906       break;
1907
1908     case TEXT_ITEM_BLANK_LINE:
1909       if (xr->y > 0)
1910         xr->y += xr->char_height;
1911       break;
1912
1913     case TEXT_ITEM_EJECT_PAGE:
1914       if (xr->y > 0)
1915         return xr_render_eject ();
1916       break;
1917
1918     default:
1919       return xr_render_table (
1920         xr, text_item_to_table_item (text_item_ref (text_item)));
1921     }
1922
1923   return NULL;
1924 }
1925
1926 static struct xr_render_fsm *
1927 xr_render_message (struct xr_driver *xr,
1928                    const struct message_item *message_item)
1929 {
1930   char *s = msg_to_string (message_item_get_msg (message_item));
1931   struct text_item *item = text_item_create (TEXT_ITEM_PARAGRAPH, s);
1932   free (s);
1933   return xr_render_table (xr, text_item_to_table_item (item));
1934 }
1935
1936 static struct xr_render_fsm *
1937 xr_render_output_item (struct xr_driver *xr,
1938                        const struct output_item *output_item)
1939 {
1940   if (is_table_item (output_item))
1941     return xr_render_table (xr, table_item_ref (to_table_item (output_item)));
1942   else if (is_chart_item (output_item))
1943     return xr_render_chart (to_chart_item (output_item));
1944   else if (is_text_item (output_item))
1945     return xr_render_text (xr, to_text_item (output_item));
1946   else if (is_message_item (output_item))
1947     return xr_render_message (xr, to_message_item (output_item));
1948   else
1949     return NULL;
1950 }