e619088fa41ed00a3985cda38a0d2e3d441a8e54
[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 struct xr_rendering *
1619 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1620                      cairo_t *cr)
1621 {
1622   struct xr_rendering *r = NULL;
1623
1624   if (is_text_item (item))
1625     r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1626                                   cr);
1627   else if (is_message_item (item))
1628     {
1629       const struct message_item *message_item = to_message_item (item);
1630       char *s = msg_to_string (message_item_get_msg (message_item));
1631       r = xr_rendering_create_text (xr, s, cr);
1632       free (s);
1633     }
1634   else if (is_table_item (item))
1635     {
1636       r = xzalloc (sizeof *r);
1637       r->item = output_item_ref (item);
1638       r->xr = xr;
1639       xr_set_cairo (xr, cr);
1640       r->p = render_pager_create (xr->params, to_table_item (item));
1641     }
1642   else if (is_chart_item (item))
1643     {
1644       r = xzalloc (sizeof *r);
1645       r->item = output_item_ref (item);
1646     }
1647   else if (is_group_open_item (item))
1648     r = xr_rendering_create_text (xr, to_group_open_item (item)->command_name,
1649                                   cr);
1650
1651   return r;
1652 }
1653
1654 void
1655 xr_rendering_destroy (struct xr_rendering *r)
1656 {
1657   if (r)
1658     {
1659       output_item_unref (r->item);
1660       render_pager_destroy (r->p);
1661       free (r);
1662     }
1663 }
1664
1665 void
1666 xr_rendering_measure (const struct xr_rendering *r, int *wp, int *hp)
1667 {
1668   int w, h;
1669
1670   if (is_table_item (r->item))
1671     {
1672       w = render_pager_get_size (r->p, H) / XR_POINT;
1673       h = render_pager_get_size (r->p, V) / XR_POINT;
1674     }
1675   else
1676     {
1677       w = CHART_WIDTH;
1678       h = CHART_HEIGHT;
1679     }
1680
1681   if (wp)
1682     *wp = w;
1683   if (hp)
1684     *hp = h;
1685 }
1686
1687 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1688                     double x, double y, double width, double height);
1689
1690 /* Draws onto CR */
1691 void
1692 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1693                    int x0, int y0, int x1, int y1)
1694 {
1695   if (is_table_item (r->item))
1696     {
1697       struct xr_driver *xr = r->xr;
1698
1699       xr_set_cairo (xr, cr);
1700
1701       render_pager_draw_region (r->p, x0 * XR_POINT, y0 * XR_POINT,
1702                                 (x1 - x0) * XR_POINT, (y1 - y0) * XR_POINT);
1703     }
1704   else
1705     xr_draw_chart (to_chart_item (r->item), cr,
1706                    0, 0, CHART_WIDTH, CHART_HEIGHT);
1707 }
1708
1709 static void
1710 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1711                double x, double y, double width, double height)
1712 {
1713   struct xrchart_geometry geom;
1714
1715   cairo_save (cr);
1716   cairo_translate (cr, x, y + height);
1717   cairo_scale (cr, 1.0, -1.0);
1718   xrchart_geometry_init (cr, &geom, width, height);
1719   if (is_boxplot (chart_item))
1720     xrchart_draw_boxplot (chart_item, cr, &geom);
1721   else if (is_histogram_chart (chart_item))
1722     xrchart_draw_histogram (chart_item, cr, &geom);
1723   else if (is_np_plot_chart (chart_item))
1724     xrchart_draw_np_plot (chart_item, cr, &geom);
1725   else if (is_piechart (chart_item))
1726     xrchart_draw_piechart (chart_item, cr, &geom);
1727   else if (is_barchart (chart_item))
1728     xrchart_draw_barchart (chart_item, cr, &geom);
1729   else if (is_roc_chart (chart_item))
1730     xrchart_draw_roc (chart_item, cr, &geom);
1731   else if (is_scree (chart_item))
1732     xrchart_draw_scree (chart_item, cr, &geom);
1733   else if (is_spreadlevel_plot_chart (chart_item))
1734     xrchart_draw_spreadlevel (chart_item, cr, &geom);
1735   else if (is_scatterplot_chart (chart_item))
1736     xrchart_draw_scatterplot (chart_item, cr, &geom);
1737   else
1738     NOT_REACHED ();
1739   xrchart_geometry_free (cr, &geom);
1740
1741   cairo_restore (cr);
1742 }
1743
1744 char *
1745 xr_draw_png_chart (const struct chart_item *item,
1746                    const char *file_name_template, int number,
1747                    const struct cell_color *fg,
1748                    const struct cell_color *bg)
1749 {
1750   const int width = 640;
1751   const int length = 480;
1752
1753   cairo_surface_t *surface;
1754   cairo_status_t status;
1755   const char *number_pos;
1756   char *file_name;
1757   cairo_t *cr;
1758
1759   number_pos = strchr (file_name_template, '#');
1760   if (number_pos != NULL)
1761     file_name = xasprintf ("%.*s%d%s.png", (int) (number_pos - file_name_template),
1762                            file_name_template, number, number_pos + 1);
1763   else
1764     file_name = xasprintf ("%s.png", file_name_template);
1765
1766   surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1767   cr = cairo_create (surface);
1768
1769   cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1770   cairo_paint (cr);
1771
1772   cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1773
1774   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1775
1776   status = cairo_surface_write_to_png (surface, file_name);
1777   if (status != CAIRO_STATUS_SUCCESS)
1778     msg (ME, _("error writing output file `%s': %s"),
1779            file_name, cairo_status_to_string (status));
1780
1781   cairo_destroy (cr);
1782   cairo_surface_destroy (surface);
1783
1784   return file_name;
1785 }
1786
1787
1788 char *
1789 xr_draw_eps_chart (const struct chart_item *item,
1790                    const char *file_name_template, int number,
1791                    const struct cell_color *fg,
1792                    const struct cell_color *bg)
1793 {
1794   const int width = 640;
1795   const int length = 480;
1796
1797   cairo_surface_t *surface;
1798   const char *number_pos;
1799   char *file_name;
1800   cairo_t *cr;
1801
1802   number_pos = strchr (file_name_template, '#');
1803   if (number_pos != NULL)
1804     file_name = xasprintf ("%.*s%d%s.eps", (int) (number_pos - file_name_template),
1805                            file_name_template, number, number_pos + 1);
1806   else
1807     file_name = xasprintf ("%s.eps", file_name_template);
1808
1809   surface = cairo_ps_surface_create (file_name, width, length);
1810   cairo_ps_surface_set_eps (surface, true);
1811   cr = cairo_create (surface);
1812
1813   cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1814   cairo_paint (cr);
1815
1816   cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1817
1818   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1819
1820   cairo_destroy (cr);
1821   cairo_surface_destroy (surface);
1822
1823   return file_name;
1824 }
1825
1826 \f
1827
1828 struct xr_table_state
1829   {
1830     struct xr_render_fsm fsm;
1831     struct render_pager *p;
1832   };
1833
1834 static bool
1835 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1836 {
1837   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1838
1839   while (render_pager_has_next (ts->p))
1840     {
1841       int used;
1842
1843       used = render_pager_draw_next (ts->p, xr->length - xr->y);
1844       if (!used)
1845         {
1846           assert (xr->y > 0);
1847           return true;
1848         }
1849       else
1850         xr->y += used;
1851     }
1852   return false;
1853 }
1854
1855 static void
1856 xr_table_destroy (struct xr_render_fsm *fsm)
1857 {
1858   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1859
1860   render_pager_destroy (ts->p);
1861   free (ts);
1862 }
1863
1864 static struct xr_render_fsm *
1865 xr_render_table (struct xr_driver *xr, struct table_item *table_item)
1866 {
1867   struct xr_table_state *ts;
1868
1869   ts = xmalloc (sizeof *ts);
1870   ts->fsm.render = xr_table_render;
1871   ts->fsm.destroy = xr_table_destroy;
1872
1873   if (xr->y > 0)
1874     xr->y += xr->char_height;
1875
1876   ts->p = render_pager_create (xr->params, table_item);
1877   table_item_unref (table_item);
1878
1879   return &ts->fsm;
1880 }
1881 \f
1882 struct xr_chart_state
1883   {
1884     struct xr_render_fsm fsm;
1885     struct chart_item *chart_item;
1886   };
1887
1888 static bool
1889 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1890 {
1891   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1892
1893   const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
1894
1895   if (xr->y > xr->length - chart_height)
1896     return true;
1897
1898   if (xr->cairo != NULL)
1899     {
1900       xr_draw_chart (cs->chart_item, xr->cairo,
1901                      0.0,
1902                      xr_to_pt (xr->y),
1903                      xr_to_pt (xr->width),
1904                      xr_to_pt (chart_height));
1905     }
1906   xr->y += chart_height;
1907
1908   return false;
1909 }
1910
1911 static void
1912 xr_chart_destroy (struct xr_render_fsm *fsm)
1913 {
1914   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1915
1916   chart_item_unref (cs->chart_item);
1917   free (cs);
1918 }
1919
1920 static struct xr_render_fsm *
1921 xr_render_chart (const struct chart_item *chart_item)
1922 {
1923   struct xr_chart_state *cs;
1924
1925   cs = xmalloc (sizeof *cs);
1926   cs->fsm.render = xr_chart_render;
1927   cs->fsm.destroy = xr_chart_destroy;
1928   cs->chart_item = chart_item_ref (chart_item);
1929
1930   return &cs->fsm;
1931 }
1932 \f
1933 static bool
1934 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1935 {
1936   return xr->y > 0;
1937 }
1938
1939 static void
1940 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1941 {
1942   /* Nothing to do. */
1943 }
1944
1945 static struct xr_render_fsm *
1946 xr_render_eject (void)
1947 {
1948   static struct xr_render_fsm eject_renderer =
1949     {
1950       xr_eject_render,
1951       xr_eject_destroy
1952     };
1953
1954   return &eject_renderer;
1955 }
1956 \f
1957 static struct xr_render_fsm *
1958 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1959 {
1960   enum text_item_type type = text_item_get_type (text_item);
1961
1962   switch (type)
1963     {
1964     case TEXT_ITEM_PAGE_TITLE:
1965       break;
1966
1967     case TEXT_ITEM_EJECT_PAGE:
1968       if (xr->y > 0)
1969         return xr_render_eject ();
1970       break;
1971
1972     default:
1973       return xr_render_table (
1974         xr, text_item_to_table_item (text_item_ref (text_item)));
1975     }
1976
1977   return NULL;
1978 }
1979
1980 static struct xr_render_fsm *
1981 xr_render_message (struct xr_driver *xr,
1982                    const struct message_item *message_item)
1983 {
1984   char *s = msg_to_string (message_item_get_msg (message_item));
1985   struct text_item *item = text_item_create (TEXT_ITEM_LOG, s);
1986   free (s);
1987   return xr_render_table (xr, text_item_to_table_item (item));
1988 }
1989
1990 static struct xr_render_fsm *
1991 xr_render_output_item (struct xr_driver *xr,
1992                        const struct output_item *output_item)
1993 {
1994   if (is_table_item (output_item))
1995     return xr_render_table (xr, table_item_ref (to_table_item (output_item)));
1996   else if (is_chart_item (output_item))
1997     return xr_render_chart (to_chart_item (output_item));
1998   else if (is_text_item (output_item))
1999     return xr_render_text (xr, to_text_item (output_item));
2000   else if (is_message_item (output_item))
2001     return xr_render_message (xr, to_message_item (output_item));
2002   else
2003     return NULL;
2004 }
2005
2006 bool
2007 xr_draw_svg_file (struct xr_rendering *r,
2008                   const char *filename)
2009 {
2010   int width, height;
2011   g_assert (r);
2012   xr_rendering_measure (r, &width, &height);
2013   cairo_surface_t *surface = cairo_svg_surface_create (filename, width, height);
2014   if (!surface)
2015     {
2016       g_error ("Could not create cairo svg surface with file %s", filename);
2017       return FALSE;
2018     }
2019   cairo_t *cr = cairo_create (surface);
2020   xr_rendering_draw (r, cr, 0, 0, width, height);
2021   cairo_destroy (cr);
2022   cairo_surface_destroy (surface);
2023   return TRUE;
2024 }