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