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