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