cairo: Get rid of unused PangoLayouts.
[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/cairo-fsm.h"
33 #include "output/chart-item-provider.h"
34 #include "output/charts/boxplot.h"
35 #include "output/charts/np-plot.h"
36 #include "output/charts/piechart.h"
37 #include "output/charts/barchart.h"
38 #include "output/charts/plot-hist.h"
39 #include "output/charts/roc-chart.h"
40 #include "output/charts/spreadlevel-plot.h"
41 #include "output/charts/scree.h"
42 #include "output/charts/scatterplot.h"
43 #include "output/driver-provider.h"
44 #include "output/group-item.h"
45 #include "output/message-item.h"
46 #include "output/options.h"
47 #include "output/page-eject-item.h"
48 #include "output/page-setup-item.h"
49 #include "output/render.h"
50 #include "output/table-item.h"
51 #include "output/table.h"
52 #include "output/text-item.h"
53
54 #include <cairo/cairo-pdf.h>
55 #include <cairo/cairo-ps.h>
56 #include <cairo/cairo-svg.h>
57
58 #include <cairo/cairo.h>
59 #include <inttypes.h>
60 #include <math.h>
61 #include <pango/pango-font.h>
62 #include <pango/pango-layout.h>
63 #include <pango/pango.h>
64 #include <pango/pangocairo.h>
65 #include <stdlib.h>
66
67 #include "gl/c-ctype.h"
68 #include "gl/c-strcase.h"
69 #include "gl/intprops.h"
70 #include "gl/minmax.h"
71 #include "gl/xalloc.h"
72
73 #include "gettext.h"
74 #define _(msgid) gettext (msgid)
75
76 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
77 #define H TABLE_HORZ
78 #define V TABLE_VERT
79
80 /* The unit used for internal measurements is inch/(72 * XR_POINT).
81    (Thus, XR_POINT units represent one point.) */
82 #define XR_POINT PANGO_SCALE
83
84 /* Conversions to and from points. */
85 static double
86 xr_to_pt (int x)
87 {
88   return x / (double) XR_POINT;
89 }
90
91 /* Dimensions for drawing lines in tables. */
92 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
93 #define XR_LINE_SPACE XR_POINT       /* Space between double lines. */
94
95 /* Output types. */
96 enum xr_output_type
97   {
98     XR_PDF,
99     XR_PS,
100     XR_SVG
101   };
102
103 /* Cairo output driver. */
104 struct xr_driver
105   {
106     struct output_driver driver;
107
108     /* User parameters. */
109     PangoFontDescription *fonts[XR_N_FONTS];
110
111     int width;                  /* Page width minus margins. */
112     int length;                 /* Page length minus margins and header. */
113
114     int left_margin;            /* Left margin in inch/(72 * XR_POINT). */
115     int right_margin;           /* Right margin in inch/(72 * XR_POINT). */
116     int top_margin;             /* Top margin in inch/(72 * XR_POINT). */
117     int bottom_margin;          /* Bottom margin in inch/(72 * XR_POINT). */
118
119     int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
120     int object_spacing;         /* Space between output objects. */
121
122     struct cell_color bg;       /* Background color */
123     struct cell_color fg;       /* Foreground color */
124     bool transparent;           /* true -> do not render background */
125     bool systemcolors;          /* true -> do not change colors     */
126
127     int initial_page_number;
128
129     struct page_heading headings[2]; /* Top and bottom headings. */
130     int headings_height[2];
131
132     /* Internal state. */
133     struct xr_fsm_style *style;
134     double font_scale;
135     int char_width, char_height;
136     cairo_t *cairo;
137     cairo_surface_t *surface;
138     int page_number;            /* Current page number. */
139     int y;
140     struct xr_fsm *fsm;
141   };
142
143 static const struct output_driver_class cairo_driver_class;
144
145 static void xr_driver_destroy_fsm (struct xr_driver *);
146 static void xr_driver_run_fsm (struct xr_driver *);
147 \f
148 /* Output driver basics. */
149
150 static struct xr_driver *
151 xr_driver_cast (struct output_driver *driver)
152 {
153   assert (driver->class == &cairo_driver_class);
154   return UP_CAST (driver, struct xr_driver, driver);
155 }
156
157 static struct driver_option *
158 opt (struct output_driver *d, struct string_map *options, const char *key,
159      const char *default_value)
160 {
161   return driver_option_get (d, options, key, default_value);
162 }
163
164 static PangoFontDescription *
165 parse_font (const char *font, int default_size, bool bold, bool italic)
166 {
167   if (!c_strcasecmp (font, "Monospaced"))
168     font = "Monospace";
169
170   PangoFontDescription *desc = pango_font_description_from_string (font);
171   if (desc == NULL)
172     return NULL;
173
174   /* If the font description didn't include an explicit font size, then set it
175      to DEFAULT_SIZE, which is in inch/72000 units. */
176   if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
177     pango_font_description_set_size (desc,
178                                      (default_size / 1000.0) * PANGO_SCALE);
179
180   pango_font_description_set_weight (desc, (bold
181                                             ? PANGO_WEIGHT_BOLD
182                                             : PANGO_WEIGHT_NORMAL));
183   pango_font_description_set_style (desc, (italic
184                                            ? PANGO_STYLE_ITALIC
185                                            : PANGO_STYLE_NORMAL));
186
187   return desc;
188 }
189
190 static PangoFontDescription *
191 parse_font_option (struct output_driver *d, struct string_map *options,
192                    const char *key, const char *default_value,
193                    int default_size, bool bold, bool italic)
194 {
195   char *string = parse_string (opt (d, options, key, default_value));
196   PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
197   if (!desc)
198     {
199       msg (MW, _("`%s': bad font specification"), string);
200
201       /* Fall back to DEFAULT_VALUE, which had better be a valid font
202          description. */
203       desc = parse_font (default_value, default_size, bold, italic);
204       assert (desc != NULL);
205     }
206   free (string);
207
208   return desc;
209 }
210
211 static void
212 apply_options (struct xr_driver *xr, struct string_map *o)
213 {
214   struct output_driver *d = &xr->driver;
215
216   /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
217   int left_margin, right_margin;
218   int top_margin, bottom_margin;
219   int paper_width, paper_length;
220   int font_size;
221   int min_break[TABLE_N_AXES];
222
223   /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
224   const double scale = XR_POINT / 1000.;
225
226   int i;
227
228   for (i = 0; i < XR_N_FONTS; i++)
229     if (xr->fonts[i] != NULL)
230       pango_font_description_free (xr->fonts[i]);
231
232   font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
233   xr->fonts[XR_FONT_FIXED] = parse_font_option
234     (d, o, "fixed-font", "monospace", font_size, false, false);
235   xr->fonts[XR_FONT_PROPORTIONAL] = parse_font_option (
236     d, o, "prop-font", "sans serif", font_size, false, false);
237
238   xr->fg = parse_color (opt (d, o, "foreground-color", "#000000000000"));
239   xr->bg = parse_color (opt (d, o, "background-color", "#FFFFFFFFFFFF"));
240
241   xr->transparent = parse_boolean (opt (d, o, "transparent", "false"));
242   xr->systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
243
244   /* Get dimensions.  */
245   parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
246   left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
247   right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
248   top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
249   bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
250
251   min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
252   min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
253
254   int object_spacing = (parse_dimension (opt (d, o, "object-spacing", NULL))
255                         * scale);
256
257   /* Convert to inch/(XR_POINT * 72). */
258   xr->left_margin = left_margin * scale;
259   xr->right_margin = right_margin * scale;
260   xr->top_margin = top_margin * scale;
261   xr->bottom_margin = bottom_margin * scale;
262   xr->width = (paper_width - left_margin - right_margin) * scale;
263   xr->length = (paper_length - top_margin - bottom_margin) * scale;
264   xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
265   xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
266   xr->object_spacing = object_spacing >= 0 ? object_spacing : XR_POINT * 12;
267
268   /* There are no headings so headings_height can stay 0. */
269 }
270
271 static struct xr_driver *
272 xr_allocate (const char *name, int device_type, struct string_map *o,
273              double font_scale)
274 {
275   struct xr_driver *xr = xzalloc (sizeof *xr);
276   struct output_driver *d = &xr->driver;
277
278   output_driver_init (d, &cairo_driver_class, name, device_type);
279
280   /* This is a nasty kluge for an issue that does not make sense.  On any
281      surface other than a screen (e.g. for output to PDF or PS or SVG), the
282      fonts are way too big by default.  A "9-point" font seems to appear about
283      16 points tall.  We use a scale factor for these surfaces to help, but the
284      underlying issue is a mystery. */
285   xr->font_scale = font_scale;
286
287   apply_options (xr, o);
288
289   return xr;
290 }
291
292 static int
293 pango_to_xr (int pango)
294 {
295   return (XR_POINT != PANGO_SCALE
296           ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
297           : pango);
298 }
299
300 static int
301 xr_to_pango (int xr)
302 {
303   return (XR_POINT != PANGO_SCALE
304           ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
305           : xr);
306 }
307
308 static void
309 xr_measure_fonts (cairo_t *cairo, PangoFontDescription *fonts[XR_N_FONTS],
310                   int *char_width, int *char_height)
311 {
312   *char_width = 0;
313   *char_height = 0;
314   for (int i = 0; i < XR_N_FONTS; i++)
315     {
316       PangoLayout *layout = pango_cairo_create_layout (cairo);
317       pango_layout_set_font_description (layout, fonts[i]);
318
319       pango_layout_set_text (layout, "0", 1);
320
321       int cw, ch;
322       pango_layout_get_size (layout, &cw, &ch);
323       *char_width = MAX (*char_width, pango_to_xr (cw));
324       *char_height = MAX (*char_height, pango_to_xr (ch));
325
326       g_object_unref (G_OBJECT (layout));
327     }
328 }
329
330 static int
331 get_layout_height (PangoLayout *layout)
332 {
333   int w, h;
334   pango_layout_get_size (layout, &w, &h);
335   return h;
336 }
337
338 static int
339 xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
340                         const struct page_heading *ph, int page_number,
341                         int width, bool draw, int base_y)
342 {
343   PangoLayout *layout = pango_cairo_create_layout (cairo);
344   pango_layout_set_font_description (layout, font);
345
346   int y = 0;
347   for (size_t i = 0; i < ph->n; i++)
348     {
349       const struct page_paragraph *pp = &ph->paragraphs[i];
350
351       char *markup = output_driver_substitute_heading_vars (pp->markup,
352                                                             page_number);
353       pango_layout_set_markup (layout, markup, -1);
354       free (markup);
355
356       pango_layout_set_alignment (
357         layout,
358         (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
359          : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER
360          : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
361          : PANGO_ALIGN_RIGHT));
362       pango_layout_set_width (layout, xr_to_pango (width));
363       if (draw)
364         {
365           cairo_save (cairo);
366           cairo_translate (cairo, 0, xr_to_pt (y + base_y));
367           pango_cairo_show_layout (cairo, layout);
368           cairo_restore (cairo);
369         }
370
371       y += pango_to_xr (get_layout_height (layout));
372     }
373
374   g_object_unref (G_OBJECT (layout));
375
376   return y;
377 }
378
379 static int
380 xr_measure_headings (cairo_surface_t *surface,
381                      const PangoFontDescription *font,
382                      const struct page_heading headings[2],
383                      int width, int object_spacing, int height[2])
384 {
385   cairo_t *cairo = cairo_create (surface);
386   int total = 0;
387   for (int i = 0; i < 2; i++)
388     {
389       int h = xr_render_page_heading (cairo, font, &headings[i], -1,
390                                       width, false, 0);
391
392       /* If the top heading is nonempty, add some space below it. */
393       if (h && i == 0)
394         h += object_spacing;
395
396       if (height)
397         height[i] = h;
398       total += h;
399     }
400   cairo_destroy (cairo);
401   return total;
402 }
403
404 static bool
405 xr_check_fonts (cairo_surface_t *surface,
406                 PangoFontDescription *fonts[XR_N_FONTS],
407                 int usable_width, int usable_length)
408 {
409   cairo_t *cairo = cairo_create (surface);
410   int char_width, char_height;
411   xr_measure_fonts (cairo, fonts, &char_width, &char_height);
412   cairo_destroy (cairo);
413
414   bool ok = true;
415   enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
416   if (usable_width / char_width < MIN_WIDTH)
417     {
418       msg (ME, _("The defined page is not wide enough to hold at least %d "
419                  "characters in the default font.  In fact, there's only "
420                  "room for %d characters."),
421            MIN_WIDTH, usable_width / char_width);
422       ok = false;
423     }
424   if (usable_length / char_height < MIN_LENGTH)
425     {
426       msg (ME, _("The defined page is not long enough to hold at least %d "
427                  "lines in the default font.  In fact, there's only "
428                  "room for %d lines."),
429            MIN_LENGTH, usable_length / char_height);
430       ok = false;
431     }
432   return ok;
433 }
434
435 static void
436 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
437 {
438   xr->cairo = cairo;
439
440   cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
441
442   xr_measure_fonts (xr->cairo, xr->fonts, &xr->char_width, &xr->char_height);
443
444   if (xr->style == NULL)
445     {
446       xr->style = xmalloc (sizeof *xr->style);
447       *xr->style = (struct xr_fsm_style) {
448         .ref_cnt = 1,
449         .size = { [H] = xr->width, [V] = xr->length },
450         .min_break = { [H] = xr->min_break[H], [V] = xr->min_break[V] },
451         .use_system_colors = xr->systemcolors,
452         .transparent = xr->transparent,
453         .font_scale = xr->font_scale,
454       };
455
456       for (size_t i = 0; i < XR_N_FONTS; i++)
457         xr->style->fonts[i] = pango_font_description_copy (xr->fonts[i]);
458     }
459
460   if (!xr->systemcolors)
461     cairo_set_source_rgb (xr->cairo,
462                           xr->fg.r / 255.0, xr->fg.g / 255.0, xr->fg.b / 255.0);
463 }
464
465 static struct output_driver *
466 xr_create (struct file_handle *fh, enum settings_output_devices device_type,
467            struct string_map *o, enum xr_output_type file_type)
468 {
469   const char *file_name = fh_get_file_name (fh);
470   struct xr_driver *xr = xr_allocate (file_name, device_type, o, 72.0 / 128.0);
471   double width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
472   double length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
473   if (file_type == XR_PDF)
474     xr->surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
475   else if (file_type == XR_PS)
476     xr->surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
477   else if (file_type == XR_SVG)
478     xr->surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
479   else
480     NOT_REACHED ();
481
482   cairo_status_t status = cairo_surface_status (xr->surface);
483   if (status != CAIRO_STATUS_SUCCESS)
484     {
485       msg (ME, _("error opening output file `%s': %s"),
486            file_name, cairo_status_to_string (status));
487       goto error;
488     }
489
490   if (!xr_check_fonts (xr->surface, xr->fonts, xr->width, xr->length))
491     goto error;
492
493   fh_unref (fh);
494   return &xr->driver;
495
496  error:
497   fh_unref (fh);
498   output_driver_destroy (&xr->driver);
499   return NULL;
500 }
501
502 static struct output_driver *
503 xr_pdf_create (struct  file_handle *fh, enum settings_output_devices device_type,
504                struct string_map *o)
505 {
506   return xr_create (fh, device_type, o, XR_PDF);
507 }
508
509 static struct output_driver *
510 xr_ps_create (struct  file_handle *fh, enum settings_output_devices device_type,
511                struct string_map *o)
512 {
513   return xr_create (fh, device_type, o, XR_PS);
514 }
515
516 static struct output_driver *
517 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
518                struct string_map *o)
519 {
520   return xr_create (fh, device_type, o, XR_SVG);
521 }
522
523 static void
524 xr_destroy (struct output_driver *driver)
525 {
526   struct xr_driver *xr = xr_driver_cast (driver);
527   size_t i;
528
529   xr_driver_destroy_fsm (xr);
530
531   if (xr->cairo != NULL)
532     {
533       cairo_surface_finish (xr->surface);
534       cairo_status_t status = cairo_status (xr->cairo);
535       if (status != CAIRO_STATUS_SUCCESS)
536         fprintf (stderr,  _("error drawing output for %s driver: %s"),
537                  output_driver_get_name (driver),
538                  cairo_status_to_string (status));
539       cairo_surface_destroy (xr->surface);
540
541       cairo_destroy (xr->cairo);
542     }
543
544   for (i = 0; i < XR_N_FONTS; i++)
545     if (xr->fonts[i] != NULL)
546       pango_font_description_free (xr->fonts[i]);
547
548   xr_fsm_style_unref (xr->style);
549   free (xr);
550 }
551
552 static void
553 xr_flush (struct output_driver *driver)
554 {
555   struct xr_driver *xr = xr_driver_cast (driver);
556
557   cairo_surface_flush (cairo_get_target (xr->cairo));
558 }
559
560 static void
561 xr_update_page_setup (struct output_driver *driver,
562                       const struct page_setup *ps)
563 {
564   struct xr_driver *xr = xr_driver_cast (driver);
565
566   xr->initial_page_number = ps->initial_page_number;
567   xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
568
569   if (xr->cairo)
570     return;
571
572   int usable[TABLE_N_AXES];
573   for (int i = 0; i < 2; i++)
574     usable[i] = (ps->paper[i]
575                  - (ps->margins[i][0] + ps->margins[i][1])) * 72 * XR_POINT;
576
577   int headings_height[2];
578   usable[V] -= xr_measure_headings (
579     xr->surface, xr->fonts[XR_FONT_PROPORTIONAL], ps->headings,
580     usable[H], xr->object_spacing, headings_height);
581
582   enum table_axis h = ps->orientation == PAGE_LANDSCAPE;
583   enum table_axis v = !h;
584   if (!xr_check_fonts (xr->surface, xr->fonts, usable[h], usable[v]))
585     return;
586
587   for (int i = 0; i < 2; i++)
588     {
589       page_heading_uninit (&xr->headings[i]);
590       page_heading_copy (&xr->headings[i], &ps->headings[i]);
591       xr->headings_height[i] = headings_height[i];
592     }
593   xr->width = usable[h];
594   xr->length = usable[v];
595   xr->left_margin = ps->margins[h][0] * 72 * XR_POINT;
596   xr->right_margin = ps->margins[h][1] * 72 * XR_POINT;
597   xr->top_margin = ps->margins[v][0] * 72 * XR_POINT;
598   xr->bottom_margin = ps->margins[v][1] * 72 * XR_POINT;
599   cairo_pdf_surface_set_size (xr->surface,
600                               ps->paper[h] * 72.0, ps->paper[v] * 72.0);
601 }
602
603 static void
604 xr_submit (struct output_driver *driver, const struct output_item *output_item)
605 {
606   struct xr_driver *xr = xr_driver_cast (driver);
607
608   if (is_page_setup_item (output_item))
609     {
610       xr_update_page_setup (driver,
611                             to_page_setup_item (output_item)->page_setup);
612       return;
613     }
614
615   if (!xr->cairo)
616     {
617       xr->page_number = xr->initial_page_number - 1;
618       xr_set_cairo (xr, cairo_create (xr->surface));
619       cairo_save (xr->cairo);
620       xr_driver_next_page (xr, xr->cairo);
621     }
622
623   xr_driver_output_item (xr, output_item);
624   while (xr_driver_need_new_page (xr))
625     {
626       cairo_restore (xr->cairo);
627       cairo_show_page (xr->cairo);
628       cairo_save (xr->cairo);
629       xr_driver_next_page (xr, xr->cairo);
630     }
631 }
632 \f
633 /* Functions for rendering a series of output items to a series of Cairo
634    contexts, with pagination.
635
636    Used by PSPPIRE for printing, and by the basic Cairo output driver above as
637    its underlying implementation.
638
639    See the big comment in cairo.h for intended usage. */
640
641 /* Gives new page CAIRO to XR for output. */
642 void
643 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
644 {
645   if (!xr->transparent)
646     {
647       cairo_save (cairo);
648       cairo_set_source_rgb (cairo,
649                             xr->bg.r / 255.0, xr->bg.g / 255.0, xr->bg.b / 255.0);
650       cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
651       cairo_fill (cairo);
652       cairo_restore (cairo);
653     }
654   cairo_translate (cairo,
655                    xr_to_pt (xr->left_margin),
656                    xr_to_pt (xr->top_margin + xr->headings_height[0]));
657
658   xr->page_number++;
659   xr->cairo = cairo;
660   xr->y = 0;
661
662   xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL],
663                           &xr->headings[0], xr->page_number, xr->width, true,
664                           -xr->headings_height[0]);
665   xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL],
666                           &xr->headings[1], xr->page_number, xr->width, true,
667                           xr->length);
668
669   xr_driver_run_fsm (xr);
670 }
671
672 /* Start rendering OUTPUT_ITEM to XR.  Only valid if XR is not in the middle of
673    rendering a previous output item, that is, only if xr_driver_need_new_page()
674    returns false. */
675 void
676 xr_driver_output_item (struct xr_driver *xr,
677                        const struct output_item *output_item)
678 {
679   assert (xr->fsm == NULL);
680   xr->fsm = xr_fsm_create (output_item, xr->style, xr->cairo);
681   xr_driver_run_fsm (xr);
682 }
683
684 /* Returns true if XR is in the middle of rendering an output item and needs a
685    new page to be appended using xr_driver_next_page() to make progress,
686    otherwise false. */
687 bool
688 xr_driver_need_new_page (const struct xr_driver *xr)
689 {
690   return xr->fsm != NULL;
691 }
692
693 /* Returns true if the current page doesn't have any content yet. */
694 bool
695 xr_driver_is_page_blank (const struct xr_driver *xr)
696 {
697   return xr->y == 0;
698 }
699
700 static void
701 xr_driver_destroy_fsm (struct xr_driver *xr)
702 {
703   xr_fsm_destroy (xr->fsm);
704   xr->fsm = NULL;
705 }
706
707 static void
708 xr_driver_run_fsm (struct xr_driver *xr)
709 {
710   if (xr->fsm != NULL)
711     {
712       cairo_save (xr->cairo);
713       cairo_translate (xr->cairo, 0, xr_to_pt (xr->y));
714       int used = xr_fsm_draw_slice (xr->fsm, xr->cairo, xr->length - xr->y);
715       xr->y += used;
716       cairo_restore (xr->cairo);
717
718       if (xr_fsm_is_empty (xr->fsm))
719         xr_driver_destroy_fsm (xr);
720     }
721 }
722 \f
723 struct output_driver_factory pdf_driver_factory =
724   { "pdf", "pspp.pdf", xr_pdf_create };
725 struct output_driver_factory ps_driver_factory =
726   { "ps", "pspp.ps", xr_ps_create };
727 struct output_driver_factory svg_driver_factory =
728   { "svg", "pspp.svg", xr_svg_create };
729
730 static const struct output_driver_class cairo_driver_class =
731 {
732   "cairo",
733   xr_destroy,
734   xr_submit,
735   xr_flush,
736 };
737 \f
738 struct xr_driver *
739 xr_driver_create (cairo_t *cairo, struct string_map *options)
740 {
741   struct xr_driver *xr = xr_allocate ("cairo", 0, options, 1.0);
742   xr_set_cairo (xr, cairo);
743   return xr;
744 }
745
746 /* Destroy XR, which should have been created with xr_driver_create().  Any
747    cairo_t added to XR is not destroyed, because it is owned by the client. */
748 void
749 xr_driver_destroy (struct xr_driver *xr)
750 {
751   if (xr != NULL)
752     {
753       xr->cairo = NULL;
754       output_driver_destroy (&xr->driver);
755     }
756 }
757 static void
758 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
759                double x, double y, double width, double height)
760 {
761   struct xrchart_geometry geom;
762
763   cairo_save (cr);
764   cairo_translate (cr, x, y + height);
765   cairo_scale (cr, 1.0, -1.0);
766   xrchart_geometry_init (cr, &geom, width, height);
767   if (is_boxplot (chart_item))
768     xrchart_draw_boxplot (chart_item, cr, &geom);
769   else if (is_histogram_chart (chart_item))
770     xrchart_draw_histogram (chart_item, cr, &geom);
771   else if (is_np_plot_chart (chart_item))
772     xrchart_draw_np_plot (chart_item, cr, &geom);
773   else if (is_piechart (chart_item))
774     xrchart_draw_piechart (chart_item, cr, &geom);
775   else if (is_barchart (chart_item))
776     xrchart_draw_barchart (chart_item, cr, &geom);
777   else if (is_roc_chart (chart_item))
778     xrchart_draw_roc (chart_item, cr, &geom);
779   else if (is_scree (chart_item))
780     xrchart_draw_scree (chart_item, cr, &geom);
781   else if (is_spreadlevel_plot_chart (chart_item))
782     xrchart_draw_spreadlevel (chart_item, cr, &geom);
783   else if (is_scatterplot_chart (chart_item))
784     xrchart_draw_scatterplot (chart_item, cr, &geom);
785   else
786     NOT_REACHED ();
787   xrchart_geometry_free (cr, &geom);
788
789   cairo_restore (cr);
790 }
791
792 char *
793 xr_draw_png_chart (const struct chart_item *item,
794                    const char *file_name_template, int number,
795                    const struct cell_color *fg,
796                    const struct cell_color *bg)
797 {
798   const int width = 640;
799   const int length = 480;
800
801   cairo_surface_t *surface;
802   cairo_status_t status;
803   const char *number_pos;
804   char *file_name;
805   cairo_t *cr;
806
807   number_pos = strchr (file_name_template, '#');
808   if (number_pos != NULL)
809     file_name = xasprintf ("%.*s%d%s.png", (int) (number_pos - file_name_template),
810                            file_name_template, number, number_pos + 1);
811   else
812     file_name = xasprintf ("%s.png", file_name_template);
813
814   surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
815   cr = cairo_create (surface);
816
817   cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
818   cairo_paint (cr);
819
820   cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
821
822   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
823
824   status = cairo_surface_write_to_png (surface, file_name);
825   if (status != CAIRO_STATUS_SUCCESS)
826     msg (ME, _("error writing output file `%s': %s"),
827            file_name, cairo_status_to_string (status));
828
829   cairo_destroy (cr);
830   cairo_surface_destroy (surface);
831
832   return file_name;
833 }
834
835
836 char *
837 xr_draw_eps_chart (const struct chart_item *item,
838                    const char *file_name_template, int number,
839                    const struct cell_color *fg,
840                    const struct cell_color *bg)
841 {
842   const int width = 640;
843   const int length = 480;
844
845   cairo_surface_t *surface;
846   const char *number_pos;
847   char *file_name;
848   cairo_t *cr;
849
850   number_pos = strchr (file_name_template, '#');
851   if (number_pos != NULL)
852     file_name = xasprintf ("%.*s%d%s.eps", (int) (number_pos - file_name_template),
853                            file_name_template, number, number_pos + 1);
854   else
855     file_name = xasprintf ("%s.eps", file_name_template);
856
857   surface = cairo_ps_surface_create (file_name, width, length);
858   cairo_ps_surface_set_eps (surface, true);
859   cr = cairo_create (surface);
860
861   cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
862   cairo_paint (cr);
863
864   cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
865
866   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
867
868   cairo_destroy (cr);
869   cairo_surface_destroy (surface);
870
871   return file_name;
872 }
873