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