cairo: Move chart code into cairo-chart.
[pspp] / src / output / cairo-chart.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2004, 2009, 2010, 2011, 2014, 2015,
3    2020 Free Software Foundation, Inc.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
17
18 #include <config.h>
19
20 #include "output/cairo-chart.h"
21 #include "math/chart-geometry.h"
22
23 #include <cairo/cairo-ps.h>
24 #include <cairo/cairo.h>
25 #include <pango/pango.h>
26 #include <pango/pangocairo.h>
27 #include <float.h>
28 #include <math.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "libpspp/assertion.h"
35 #include "libpspp/message.h"
36 #include "math/chart-geometry.h"
37 #include "output/cairo.h"
38 #include "output/chart-item.h"
39 #include "output/charts/barchart.h"
40 #include "output/charts/boxplot.h"
41 #include "output/charts/np-plot.h"
42 #include "output/charts/piechart.h"
43 #include "output/charts/plot-hist.h"
44 #include "output/charts/roc-chart.h"
45 #include "output/charts/scatterplot.h"
46 #include "output/charts/scree.h"
47 #include "output/charts/spreadlevel-plot.h"
48 #include "output/table.h"
49
50 #include "gl/xalloc.h"
51 #include "gl/xvasprintf.h"
52
53 #include "gettext.h"
54 #define _(msgid) gettext (msgid)
55
56 void
57 xrchart_geometry_init (cairo_t *cr, struct xrchart_geometry *geom,
58                        double width, double length)
59 {
60   /* Set default chart geometry. */
61   geom->axis[SCALE_ORDINATE].data_max = 0.900 * length;
62   geom->axis[SCALE_ORDINATE].data_min = 0.200 * length;
63
64   geom->axis[SCALE_ABSCISSA].data_min = 0.150 * width;
65   geom->axis[SCALE_ABSCISSA].data_max = 0.800 * width;
66   geom->abscissa_bottom = 0.070 * length;
67   geom->ordinate_left = 0.050 * width;
68   geom->title_bottom = 0.920 * length;
69   geom->legend_left = 0.810 * width;
70   geom->legend_right = width;
71   geom->font_size = 15.0;
72   geom->in_path = false;
73   geom->dataset = NULL;
74   geom->n_datasets = 0;
75
76   geom->fill_colour = data_colour[0];
77
78   cairo_set_line_width (cr, 1.0);
79
80   cairo_rectangle (cr, geom->axis[SCALE_ABSCISSA].data_min, geom->axis[SCALE_ORDINATE].data_min,
81                    geom->axis[SCALE_ABSCISSA].data_max - geom->axis[SCALE_ABSCISSA].data_min,
82                    geom->axis[SCALE_ORDINATE].data_max - geom->axis[SCALE_ORDINATE].data_min);
83   cairo_stroke (cr);
84 }
85
86 void
87 xrchart_geometry_free (cairo_t *cr UNUSED, struct xrchart_geometry *geom)
88 {
89   int i;
90
91   for (i = 0 ; i < geom->n_datasets; ++i)
92     free (geom->dataset[i]);
93   free (geom->dataset);
94 }
95
96 #if ! PANGO_VERSION_CHECK (1, 22, 0)
97 int pango_layout_get_baseline (PangoLayout    *layout);
98
99 /* Shamelessly copied from the pango source */
100 int
101 pango_layout_get_baseline (PangoLayout    *layout)
102 {
103   int baseline;
104
105   /* XXX this is so inefficient */
106   PangoLayoutIter *iter = pango_layout_get_iter (layout);
107   baseline = pango_layout_iter_get_baseline (iter);
108   pango_layout_iter_free (iter);
109
110   return baseline;
111 }
112 #endif
113
114 /*
115     These colours come from:
116     http://tango.freedesktop.org/static/cvs/tango-art-tools/palettes/Tango-Palette.gpl */
117 const struct xrchart_colour data_colour[XRCHART_N_COLOURS] =
118   {
119     {252, 233,  79},    /* Butter 1 */
120     {138, 226,  52},    /* Chameleon 1 */
121     {252, 175,  62},    /* Orange 1 */
122     {114, 159, 207},    /* Sky Blue 1 */
123     {173, 127, 168},    /* Plum 1 */
124     {233, 185, 110},    /* Chocolate 1 */
125     {239,  41,  41},    /* Scarlet Red 1 */
126     {238, 238, 236},    /* Aluminium 1 */
127
128     {237, 212,   0},    /* Butter 2 */
129     {115, 210,  22},    /* Chameleon 2 */
130     {245, 121,   0},    /* Orange 2 */
131     {52,  101, 164},    /* Sky Blue 2 */
132     {117,  80, 123},    /* Plum 2 */
133     {193, 125,  17},    /* Chocolate 2 */
134     {204,   0,   0},    /* Scarlet Red 2 */
135
136     {136, 138, 133},    /* Aluminium 4 */
137
138     {196, 160,   0},    /* Butter 3 */
139     {78,  154,   6},    /* Chameleon 3 */
140     {206,  92,   0},    /* Orange 3 */
141     {32,   74, 135},    /* Sky Blue 3 */
142     {92,   53, 102},    /* Plum 3 */
143     {143,  89,   2},    /* Chocolate 3 */
144     {164,   0,   0},    /* Scarlet Red 3 */
145     {85,   87,  83},    /* Aluminium 5 */
146
147     {211, 215, 207},    /* Aluminium 2 */
148     {186, 189, 182},    /* Aluminium 3 */
149     {46,   52,  54},    /* Aluminium 6 */
150   };
151
152 void
153 xrchart_draw_marker (cairo_t *cr, double x, double y,
154                      enum xrmarker_type marker, double size)
155 {
156   cairo_save (cr);
157   cairo_translate (cr, x, y);
158   cairo_scale (cr, size / 2.0, size / 2.0);
159   cairo_set_line_width (cr, cairo_get_line_width (cr) / (size / 2.0));
160   switch (marker)
161     {
162     case XRMARKER_CIRCLE:
163       cairo_arc (cr, 0, 0, 1.0, 0, 2 * M_PI);
164       cairo_stroke (cr);
165       break;
166
167     case XRMARKER_ASTERISK:
168       cairo_move_to (cr, 0, -1.0); /* | */
169       cairo_line_to (cr, 0, 1.0);
170       cairo_move_to (cr, -M_SQRT1_2, -M_SQRT1_2); /* / */
171       cairo_line_to (cr, M_SQRT1_2, M_SQRT1_2);
172       cairo_move_to (cr, -M_SQRT1_2, M_SQRT1_2); /* \ */
173       cairo_line_to (cr, M_SQRT1_2, -M_SQRT1_2);
174       cairo_stroke (cr);
175       break;
176
177     case XRMARKER_SQUARE:
178       cairo_rectangle (cr, -1.0, -1.0, 2.0, 2.0);
179       cairo_stroke (cr);
180       break;
181     }
182   cairo_restore (cr);
183 }
184
185 void
186 xrchart_label_rotate (cairo_t *cr, int horz_justify, int vert_justify,
187                       double font_size, const char *string, double angle)
188 {
189   PangoFontDescription *desc;
190   PangoLayout *layout;
191   double x, y;
192
193   desc = pango_font_description_from_string ("Sans");
194   if (desc == NULL)
195     {
196       cairo_new_path (cr);
197       return;
198     }
199   pango_font_description_set_absolute_size (desc, font_size * PANGO_SCALE);
200
201   cairo_save (cr);
202   cairo_rotate (cr, angle);
203   cairo_get_current_point (cr, &x, &y);
204   cairo_translate (cr, x, y);
205   cairo_move_to (cr, 0, 0);
206   cairo_scale (cr, 1.0, -1.0);
207
208   layout = pango_cairo_create_layout (cr);
209   pango_layout_set_font_description (layout, desc);
210   pango_layout_set_markup (layout, string, -1);
211   if (horz_justify != 'l')
212     {
213       int width_pango;
214       double width;
215
216       pango_layout_get_size (layout, &width_pango, NULL);
217       width = (double) width_pango / PANGO_SCALE;
218       if (horz_justify == 'r')
219         cairo_rel_move_to (cr, -width, 0);
220       else
221         cairo_rel_move_to (cr, -width / 2.0, 0);
222     }
223   if (vert_justify == 'x')
224     {
225       int baseline_pango = pango_layout_get_baseline (layout);
226       double baseline = (double) baseline_pango / PANGO_SCALE;
227       cairo_rel_move_to (cr, 0, -baseline);
228     }
229   else if (vert_justify != 't')
230     {
231       int height_pango;
232       double height;
233
234       pango_layout_get_size (layout, NULL, &height_pango);
235       height = (double) height_pango / PANGO_SCALE;
236       if (vert_justify == 'b')
237         cairo_rel_move_to (cr, 0, -height);
238       else if (vert_justify == 'c')
239         cairo_rel_move_to (cr, 0, -height / 2.0);
240     }
241   pango_cairo_show_layout (cr, layout);
242   g_object_unref (layout);
243
244   cairo_restore (cr);
245
246   cairo_new_path (cr);
247
248   pango_font_description_free (desc);
249 }
250
251 void
252 xrchart_label (cairo_t *cr, int horz_justify, int vert_justify,
253                double font_size, const char *string)
254 {
255   xrchart_label_rotate (cr, horz_justify, vert_justify, font_size, string, 0);
256 }
257
258
259 /* Draw a tick mark at position
260    If label is non null, then print it at the tick mark
261 */
262 static void
263 draw_tick_internal (cairo_t *cr, const struct xrchart_geometry *geom,
264                     enum tick_orientation orientation,
265                     bool rotated,
266                     double position,
267                     const char *s);
268
269 void
270 draw_tick (cairo_t *cr, const struct xrchart_geometry *geom,
271            enum tick_orientation orientation,
272            bool rotated,
273            double position,
274            const char *label, ...)
275 {
276   va_list ap;
277   char *s;
278   va_start (ap, label);
279   s = xvasprintf (label, ap);
280
281   if (fabs (position) < DBL_EPSILON)
282     position = 0;
283
284   draw_tick_internal (cr, geom, orientation, rotated, position, s);
285   free (s);
286   va_end (ap);
287 }
288
289
290 static void
291 draw_tick_internal (cairo_t *cr, const struct xrchart_geometry *geom,
292                     enum tick_orientation orientation,
293                     bool rotated,
294                     double position,
295                     const char *s)
296 {
297   const int tickSize = 10;
298   double x, y;
299
300   cairo_move_to (cr, geom->axis[SCALE_ABSCISSA].data_min, geom->axis[SCALE_ORDINATE].data_min);
301
302   if (orientation == SCALE_ABSCISSA)
303     {
304       cairo_rel_move_to (cr, position, 0);
305       cairo_rel_line_to (cr, 0, -tickSize);
306     }
307   else if (orientation == SCALE_ORDINATE)
308     {
309       cairo_rel_move_to (cr, 0, position);
310       cairo_rel_line_to (cr, -tickSize, 0);
311     }
312   else
313     NOT_REACHED ();
314   cairo_get_current_point (cr, &x, &y);
315
316   cairo_stroke (cr);
317
318   if (s != NULL)
319     {
320       cairo_move_to (cr, x, y);
321
322       if (orientation == SCALE_ABSCISSA)
323         {
324           if (rotated)
325             xrchart_label_rotate (cr, 'l', 'c', geom->font_size, s, -G_PI_4);
326           else
327             xrchart_label (cr, 'c', 't', geom->font_size, s);
328         }
329       else if (orientation == SCALE_ORDINATE)
330         {
331           xrchart_label (cr, 'r', 'c', geom->font_size, s);
332         }
333     }
334 }
335
336
337 /* Write the title on a chart*/
338 void
339 xrchart_write_title (cairo_t *cr, const struct xrchart_geometry *geom,
340                    const char *title, ...)
341 {
342   va_list ap;
343   char *s;
344
345   cairo_save (cr);
346   cairo_move_to (cr, geom->axis[SCALE_ABSCISSA].data_min, geom->title_bottom);
347
348   va_start(ap, title);
349   s = xvasprintf (title, ap);
350   xrchart_label (cr, 'l', 'x', geom->font_size * 1.5, s);
351   free (s);
352   va_end (ap);
353
354   cairo_restore (cr);
355 }
356
357 static void
358 xrchart_text_extents (cairo_t *cr, const struct xrchart_geometry *geom,
359                       const char *utf8,
360                       double *width, double *height)
361 {
362   PangoFontDescription *desc;
363   PangoLayout *layout;
364   int width_pango;
365   int height_pango;
366
367   desc = pango_font_description_from_string ("Sans");
368   if (desc == NULL)
369       return;
370   pango_font_description_set_absolute_size (desc, geom->font_size * PANGO_SCALE);
371   layout = pango_cairo_create_layout (cr);
372   pango_layout_set_font_description (layout, desc);
373   pango_layout_set_markup (layout, utf8, -1);
374   pango_layout_get_size (layout, &width_pango, &height_pango);
375   *width = (double) width_pango / PANGO_SCALE;
376   *height = (double) height_pango / PANGO_SCALE;
377   g_object_unref (layout);
378   pango_font_description_free (desc);
379 }
380
381 static bool
382 xrchart_write_scale (cairo_t *cr, struct xrchart_geometry *geom,
383                      double smin, double smax, enum tick_orientation orient)
384 {
385   int s;
386   int ticks;
387
388   double interval;
389   double lower;
390   double upper;
391   double tickscale;
392   char *tick_format_string;
393   bool tickoversize = false;
394
395   if (smax == smin)
396     return false;
397
398   chart_get_scale (smax, smin, &lower, &interval, &ticks);
399
400   tick_format_string = chart_get_ticks_format (lower, interval, ticks, &tickscale);
401
402   upper = lower + interval * (ticks+1);
403
404   geom->axis[orient].max = upper;
405   geom->axis[orient].min = lower;
406
407   struct xrchart_axis *axis = &geom->axis[orient];
408   geom->axis[orient].scale = (fabs ((double) axis->data_max - axis->data_min)
409                               / fabs (axis->max - axis->min));
410
411   if (orient == SCALE_ABSCISSA)
412     {
413       char *test_text;
414       double lower_txt_width, upper_txt_width, unused, width;
415       test_text = xasprintf(tick_format_string, upper*tickscale);
416       xrchart_text_extents (cr, geom, test_text, &upper_txt_width, &unused);
417       free(test_text);
418       test_text = xasprintf(tick_format_string, lower*tickscale);
419       xrchart_text_extents (cr, geom, test_text, &lower_txt_width, &unused);
420       free(test_text);
421       width = MAX(lower_txt_width, upper_txt_width);
422       tickoversize = width > 0.9 *
423         ((double)(geom->axis[SCALE_ABSCISSA].data_max - geom->axis[SCALE_ABSCISSA].data_min))/(ticks+1);
424     }
425
426   double pos = lower;
427
428   for (s = 0 ; s <= ticks; ++s)
429     {
430       draw_tick (cr, geom, orient, tickoversize,
431                  s * interval * geom->axis[orient].scale,
432                  tick_format_string, pos*tickscale);
433       pos += interval;
434     }
435   free(tick_format_string);
436
437   return true;
438 }
439
440 /* Set the scale for the ordinate */
441 bool
442 xrchart_write_yscale (cairo_t *cr, struct xrchart_geometry *geom,
443                     double smin, double smax)
444 {
445   return xrchart_write_scale (cr, geom, smin, smax, SCALE_ORDINATE);
446 }
447
448 /* Set the scale for the abscissa */
449 bool
450 xrchart_write_xscale (cairo_t *cr, struct xrchart_geometry *geom,
451                       double smin, double smax)
452 {
453   return xrchart_write_scale (cr, geom, smin, smax, SCALE_ABSCISSA);
454 }
455
456
457 /* Write the abscissa label */
458 void
459 xrchart_write_xlabel (cairo_t *cr, const struct xrchart_geometry *geom,
460                     const char *label)
461 {
462   cairo_move_to (cr, geom->axis[SCALE_ABSCISSA].data_min, geom->abscissa_bottom);
463   xrchart_label (cr, 'l', 't', geom->font_size, label);
464 }
465
466 /* Write the ordinate label */
467 void
468 xrchart_write_ylabel (cairo_t *cr, const struct xrchart_geometry *geom,
469                     const char *label)
470 {
471   cairo_save (cr);
472   cairo_translate (cr, geom->ordinate_left,   geom->axis[SCALE_ORDINATE].data_min);
473   cairo_rotate (cr, M_PI / 2.0);
474
475   xrchart_label (cr, 'l', 'x', geom->font_size, label);
476   cairo_restore (cr);
477 }
478
479
480 void
481 xrchart_write_legend (cairo_t *cr, const struct xrchart_geometry *geom)
482 {
483   int i;
484   const int vstep = geom->font_size * 2;
485   const int xpad = 10;
486   const int ypad = 10;
487   const int swatch = 20;
488   const int legend_top = geom->axis[SCALE_ORDINATE].data_max;
489   const int legend_bottom = legend_top -
490     (vstep * geom->n_datasets + 2 * ypad);
491
492   cairo_save (cr);
493
494   cairo_rectangle (cr, geom->legend_left, legend_top,
495                    geom->legend_right - xpad - geom->legend_left,
496                    legend_bottom - legend_top);
497   cairo_stroke (cr);
498
499   for (i = 0 ; i < geom->n_datasets ; ++i)
500     {
501       const int ypos = legend_top - vstep * (i + 1);
502       const int xpos = geom->legend_left + xpad;
503       const struct xrchart_colour *colour;
504
505       cairo_move_to (cr, xpos, ypos);
506
507       cairo_save (cr);
508       colour = &data_colour [ i % XRCHART_N_COLOURS];
509       cairo_set_source_rgb (cr,
510                             colour->red / 255.0,
511                             colour->green / 255.0,
512                             colour->blue / 255.0);
513       cairo_rectangle (cr, xpos, ypos, swatch, swatch);
514       cairo_fill_preserve (cr);
515       cairo_stroke (cr);
516       cairo_restore (cr);
517
518       cairo_move_to (cr, xpos + swatch * 1.5, ypos);
519       xrchart_label (cr, 'l', 'x', geom->font_size, geom->dataset[i]);
520     }
521
522   cairo_restore (cr);
523 }
524
525 /* Start a new vector called NAME */
526 void
527 xrchart_vector_start (cairo_t *cr, struct xrchart_geometry *geom, const char *name)
528 {
529   const struct xrchart_colour *colour;
530
531   cairo_save (cr);
532
533   colour = &data_colour[geom->n_datasets % XRCHART_N_COLOURS];
534   cairo_set_source_rgb (cr,
535                         colour->red / 255.0,
536                         colour->green / 255.0,
537                         colour->blue / 255.0);
538
539   geom->n_datasets++;
540   geom->dataset = xrealloc (geom->dataset,
541                             geom->n_datasets * sizeof (*geom->dataset));
542
543   geom->dataset[geom->n_datasets - 1] = strdup (name);
544 }
545
546 /* Plot a data point */
547 void
548 xrchart_datum (cairo_t *cr, const struct xrchart_geometry *geom,
549              int dataset UNUSED, double x, double y)
550 {
551   double x_pos = (x - geom->axis[SCALE_ABSCISSA].min) * geom->axis[SCALE_ABSCISSA].scale + geom->axis[SCALE_ABSCISSA].data_min;
552   double y_pos = (y - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale + geom->axis[SCALE_ORDINATE].data_min;
553
554   xrchart_draw_marker (cr, x_pos, y_pos, XRMARKER_CIRCLE, 10);
555 }
556
557 void
558 xrchart_vector_end (cairo_t *cr, struct xrchart_geometry *geom)
559 {
560   cairo_stroke (cr);
561   cairo_restore (cr);
562   geom->in_path = false;
563 }
564
565 /* Plot a data point */
566 void
567 xrchart_vector (cairo_t *cr, struct xrchart_geometry *geom, double x, double y)
568 {
569   const double x_pos =
570     (x - geom->axis[SCALE_ABSCISSA].min) * geom->axis[SCALE_ABSCISSA].scale + geom->axis[SCALE_ABSCISSA].data_min ;
571
572   const double y_pos =
573     (y - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale + geom->axis[SCALE_ORDINATE].data_min ;
574
575   if (geom->in_path)
576     cairo_line_to (cr, x_pos, y_pos);
577   else
578     {
579       cairo_move_to (cr, x_pos, y_pos);
580       geom->in_path = true;
581     }
582 }
583
584
585
586 /* Draw a line with slope SLOPE and intercept INTERCEPT.
587    between the points limit1 and limit2.
588    If lim_dim is XRCHART_DIM_Y then the limit{1,2} are on the
589    y axis otherwise the x axis
590 */
591 void
592 xrchart_line(cairo_t *cr, const struct xrchart_geometry *geom,
593            double slope, double intercept,
594            double limit1, double limit2, enum xrchart_dim lim_dim)
595 {
596   double x1, y1;
597   double x2, y2;
598
599   if (lim_dim == XRCHART_DIM_Y)
600     {
601       x1 = (limit1 - intercept) / slope;
602       x2 = (limit2 - intercept) / slope;
603       y1 = limit1;
604       y2 = limit2;
605     }
606   else
607     {
608       x1 = limit1;
609       x2 = limit2;
610       y1 = slope * x1 + intercept;
611       y2 = slope * x2 + intercept;
612     }
613
614   y1 = (y1 - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale + geom->axis[SCALE_ORDINATE].data_min;
615   y2 = (y2 - geom->axis[SCALE_ORDINATE].min) * geom->axis[SCALE_ORDINATE].scale + geom->axis[SCALE_ORDINATE].data_min;
616   x1 = (x1 - geom->axis[SCALE_ABSCISSA].min) * geom->axis[SCALE_ABSCISSA].scale + geom->axis[SCALE_ABSCISSA].data_min;
617   x2 = (x2 - geom->axis[SCALE_ABSCISSA].min) * geom->axis[SCALE_ABSCISSA].scale + geom->axis[SCALE_ABSCISSA].data_min;
618
619   cairo_move_to (cr, x1, y1);
620   cairo_line_to (cr, x2, y2);
621   cairo_stroke (cr);
622 }
623 \f
624 void
625 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
626                double width, double height)
627 {
628   struct xrchart_geometry geom;
629
630   cairo_save (cr);
631   cairo_translate (cr, 0, height);
632   cairo_scale (cr, 1.0, -1.0);
633   xrchart_geometry_init (cr, &geom, width, height);
634   if (is_boxplot (chart_item))
635     xrchart_draw_boxplot (chart_item, cr, &geom);
636   else if (is_histogram_chart (chart_item))
637     xrchart_draw_histogram (chart_item, cr, &geom);
638   else if (is_np_plot_chart (chart_item))
639     xrchart_draw_np_plot (chart_item, cr, &geom);
640   else if (is_piechart (chart_item))
641     xrchart_draw_piechart (chart_item, cr, &geom);
642   else if (is_barchart (chart_item))
643     xrchart_draw_barchart (chart_item, cr, &geom);
644   else if (is_roc_chart (chart_item))
645     xrchart_draw_roc (chart_item, cr, &geom);
646   else if (is_scree (chart_item))
647     xrchart_draw_scree (chart_item, cr, &geom);
648   else if (is_spreadlevel_plot_chart (chart_item))
649     xrchart_draw_spreadlevel (chart_item, cr, &geom);
650   else if (is_scatterplot_chart (chart_item))
651     xrchart_draw_scatterplot (chart_item, cr, &geom);
652   else
653     NOT_REACHED ();
654   xrchart_geometry_free (cr, &geom);
655
656   cairo_restore (cr);
657 }
658
659 char *
660 xr_draw_png_chart (const struct chart_item *item,
661                    const char *file_name_template, int number,
662                    const struct cell_color *fg,
663                    const struct cell_color *bg)
664 {
665   const int width = 640;
666   const int length = 480;
667
668   cairo_surface_t *surface;
669   cairo_status_t status;
670   const char *number_pos;
671   char *file_name;
672   cairo_t *cr;
673
674   number_pos = strchr (file_name_template, '#');
675   if (number_pos != NULL)
676     file_name = xasprintf ("%.*s%d%s.png", (int) (number_pos - file_name_template),
677                            file_name_template, number, number_pos + 1);
678   else
679     file_name = xasprintf ("%s.png", file_name_template);
680
681   surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
682   cr = cairo_create (surface);
683
684   cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
685   cairo_paint (cr);
686
687   cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
688
689   xr_draw_chart (item, cr, width, length);
690
691   status = cairo_surface_write_to_png (surface, file_name);
692   if (status != CAIRO_STATUS_SUCCESS)
693     msg (ME, _("error writing output file `%s': %s"),
694            file_name, cairo_status_to_string (status));
695
696   cairo_destroy (cr);
697   cairo_surface_destroy (surface);
698
699   return file_name;
700 }
701
702
703 char *
704 xr_draw_eps_chart (const struct chart_item *item,
705                    const char *file_name_template, int number,
706                    const struct cell_color *fg,
707                    const struct cell_color *bg)
708 {
709   const int width = 640;
710   const int length = 480;
711
712   cairo_surface_t *surface;
713   const char *number_pos;
714   char *file_name;
715   cairo_t *cr;
716
717   number_pos = strchr (file_name_template, '#');
718   if (number_pos != NULL)
719     file_name = xasprintf ("%.*s%d%s.eps", (int) (number_pos - file_name_template),
720                            file_name_template, number, number_pos + 1);
721   else
722     file_name = xasprintf ("%s.eps", file_name_template);
723
724   surface = cairo_ps_surface_create (file_name, width, length);
725   cairo_ps_surface_set_eps (surface, true);
726   cr = cairo_create (surface);
727
728   cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
729   cairo_paint (cr);
730
731   cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
732
733   xr_draw_chart (item, cr, width, length);
734
735   cairo_destroy (cr);
736   cairo_surface_destroy (surface);
737
738   return file_name;
739 }
740