cairo: Allow xr_driver_create()'s caller to usefully set more options.
[pspp-builds.git] / src / output / cairo.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2010 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/message.h"
24 #include "libpspp/start-date.h"
25 #include "libpspp/str.h"
26 #include "libpspp/string-map.h"
27 #include "libpspp/version.h"
28 #include "output/cairo-chart.h"
29 #include "output/chart-item-provider.h"
30 #include "output/charts/boxplot.h"
31 #include "output/charts/np-plot.h"
32 #include "output/charts/piechart.h"
33 #include "output/charts/plot-hist.h"
34 #include "output/charts/roc-chart.h"
35 #include "output/charts/scree.h"
36 #include "output/driver-provider.h"
37 #include "output/message-item.h"
38 #include "output/options.h"
39 #include "output/render.h"
40 #include "output/tab.h"
41 #include "output/table-item.h"
42 #include "output/table.h"
43 #include "output/text-item.h"
44
45 #include <cairo/cairo-pdf.h>
46 #include <cairo/cairo-ps.h>
47 #include <cairo/cairo-svg.h>
48 #include <cairo/cairo.h>
49 #include <pango/pango-font.h>
50 #include <pango/pango-layout.h>
51 #include <pango/pango.h>
52 #include <pango/pangocairo.h>
53 #include <stdlib.h>
54
55 #include "gl/error.h"
56 #include "gl/intprops.h"
57 #include "gl/minmax.h"
58 #include "gl/xalloc.h"
59
60 #include "gettext.h"
61 #define _(msgid) gettext (msgid)
62
63 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
64 #define H TABLE_HORZ
65 #define V TABLE_VERT
66
67 /* Measurements as we present to the rest of PSPP. */
68 #define XR_POINT PANGO_SCALE
69 #define XR_INCH (XR_POINT * 72)
70
71 /* Conversions to and from points. */
72 static double
73 xr_to_pt (int x)
74 {
75   return x / (double) XR_POINT;
76 }
77
78 /* Output types. */
79 enum xr_output_type
80   {
81     XR_PDF,
82     XR_PS,
83     XR_SVG
84   };
85
86 /* Cairo fonts. */
87 enum xr_font_type
88   {
89     XR_FONT_PROPORTIONAL,
90     XR_FONT_EMPHASIS,
91     XR_FONT_FIXED,
92     XR_N_FONTS
93   };
94
95 /* A font for use with Cairo. */
96 struct xr_font
97   {
98     char *string;
99     PangoFontDescription *desc;
100     PangoLayout *layout;
101     PangoFontMetrics *metrics;
102   };
103
104 /* An output item whose rendering is in progress. */
105 struct xr_render_fsm
106   {
107     /* Renders as much of itself as it can on the current page.  Returns true
108        if rendering is complete, false if the output item needs another
109        page. */
110     bool (*render) (struct xr_render_fsm *, struct xr_driver *);
111
112     /* Destroys the output item. */
113     void (*destroy) (struct xr_render_fsm *);
114   };
115
116 /* Cairo output driver. */
117 struct xr_driver
118   {
119     struct output_driver driver;
120
121     /* User parameters. */
122     bool headers;               /* Draw headers at top of page? */
123
124     struct xr_font fonts[XR_N_FONTS];
125     int font_height;            /* In XR units. */
126
127     int width;                  /* Page width minus margins. */
128     int length;                 /* Page length minus margins and header. */
129
130     int left_margin;            /* Left margin in XR units. */
131     int right_margin;           /* Right margin in XR units. */
132     int top_margin;             /* Top margin in XR units. */
133     int bottom_margin;          /* Bottom margin in XR units. */
134
135     int line_gutter;            /* Space around lines. */
136     int line_space;             /* Space between lines. */
137     int line_width;             /* Width of lines. */
138
139     /* Internal state. */
140     struct render_params *params;
141     char *command_name;
142     char *title;
143     char *subtitle;
144     cairo_t *cairo;
145     int page_number;            /* Current page number. */
146     int y;
147     struct xr_render_fsm *fsm;
148   };
149
150 static const struct output_driver_class cairo_driver_class;
151
152 static void draw_headers (struct xr_driver *);
153 static void xr_driver_destroy_fsm (struct xr_driver *);
154 static void xr_driver_run_fsm (struct xr_driver *);
155
156 static bool load_font (struct xr_driver *, struct xr_font *);
157 static void free_font (struct xr_font *);
158
159 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
160                           enum render_line_style styles[TABLE_N_AXES][2]);
161 static void xr_measure_cell_width (void *, const struct table_cell *,
162                                    int *min, int *max);
163 static int xr_measure_cell_height (void *, const struct table_cell *,
164                                    int width);
165 static void xr_draw_cell (void *, const struct table_cell *,
166                           int bb[TABLE_N_AXES][2],
167                           int clip[TABLE_N_AXES][2]);
168
169 static struct xr_render_fsm *xr_render_output_item (
170   struct xr_driver *, const struct output_item *);
171 \f
172 /* Output driver basics. */
173
174 static struct xr_driver *
175 xr_driver_cast (struct output_driver *driver)
176 {
177   assert (driver->class == &cairo_driver_class);
178   return UP_CAST (driver, struct xr_driver, driver);
179 }
180
181 static struct driver_option *
182 opt (struct output_driver *d, struct string_map *options, const char *key,
183      const char *default_value)
184 {
185   return driver_option_get (d, options, key, default_value);
186 }
187
188 static struct xr_driver *
189 xr_allocate (const char *name, int device_type, struct string_map *o)
190 {
191   int paper_width, paper_length;
192   struct output_driver *d;
193   struct xr_driver *xr;
194
195   xr = xzalloc (sizeof *xr);
196   d = &xr->driver;
197   output_driver_init (d, &cairo_driver_class, name, device_type);
198   xr->font_height = XR_POINT * 10;
199   xr->fonts[XR_FONT_FIXED].string
200     = parse_string (opt (d, o, "fixed-font", "monospace"));
201   xr->fonts[XR_FONT_PROPORTIONAL].string
202     = parse_string (opt (d, o, "prop-font", "serif"));
203   xr->fonts[XR_FONT_EMPHASIS].string
204     = parse_string (opt (d, o, "emph-font", "serif italic"));
205   xr->line_gutter = XR_POINT;
206   xr->line_space = XR_POINT;
207   xr->line_width = XR_POINT / 2;
208   xr->page_number = 0;
209
210   xr->headers = parse_boolean (opt (d, o, "headers", "true"));
211
212   parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
213   xr->left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
214   xr->right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
215   xr->top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
216   xr->bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
217
218   if (xr->headers)
219     xr->top_margin += 3 * xr->font_height;
220   xr->width = paper_width - xr->left_margin - xr->right_margin;
221   xr->length = paper_length - xr->top_margin - xr->bottom_margin;
222
223   return xr;
224 }
225
226 static bool
227 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
228 {
229   int i;
230
231   xr->cairo = cairo;
232
233   cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
234
235   for (i = 0; i < XR_N_FONTS; i++)
236     if (!load_font (xr, &xr->fonts[i]))
237       return false;
238
239   if (xr->params == NULL)
240     {
241       int single_width, double_width;
242
243       xr->params = xmalloc (sizeof *xr->params);
244       xr->params->draw_line = xr_draw_line;
245       xr->params->measure_cell_width = xr_measure_cell_width;
246       xr->params->measure_cell_height = xr_measure_cell_height;
247       xr->params->draw_cell = xr_draw_cell;
248       xr->params->aux = xr;
249       xr->params->size[H] = xr->width;
250       xr->params->size[V] = xr->length;
251       xr->params->font_size[H] = xr->font_height / 2; /* XXX */
252       xr->params->font_size[V] = xr->font_height;
253
254       single_width = 2 * xr->line_gutter + xr->line_width;
255       double_width = 2 * xr->line_gutter + xr->line_space + 2 * xr->line_width;
256       for (i = 0; i < TABLE_N_AXES; i++)
257         {
258           xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
259           xr->params->line_widths[i][RENDER_LINE_SINGLE] = single_width;
260           xr->params->line_widths[i][RENDER_LINE_DOUBLE] = double_width;
261         }
262     }
263
264   return true;
265 }
266
267 static struct output_driver *
268 xr_create (const char *file_name, enum settings_output_devices device_type,
269            struct string_map *o, enum xr_output_type file_type)
270 {
271   enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
272   struct output_driver *d;
273   struct xr_driver *xr;
274   cairo_surface_t *surface;
275   cairo_status_t status;
276   double width_pt, length_pt;
277
278   xr = xr_allocate (file_name, device_type, o);
279   d = &xr->driver;
280
281   width_pt = (xr->width + xr->left_margin + xr->right_margin) / 1000.0;
282   length_pt = (xr->length + xr->top_margin + xr->bottom_margin) / 1000.0;
283   if (file_type == XR_PDF)
284     surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
285   else if (file_type == XR_PS)
286     surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
287   else if (file_type == XR_SVG)
288     surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
289   else
290     NOT_REACHED ();
291
292   status = cairo_surface_status (surface);
293   if (status != CAIRO_STATUS_SUCCESS)
294     {
295       error (0, 0, _("error opening output file \"%s\": %s"),
296              file_name, cairo_status_to_string (status));
297       cairo_surface_destroy (surface);
298       goto error;
299     }
300
301   xr->cairo = cairo_create (surface);
302   cairo_surface_destroy (surface);
303
304   if (!xr_set_cairo (xr, xr->cairo))
305     goto error;
306
307   cairo_save (xr->cairo);
308   xr_driver_next_page (xr, xr->cairo);
309
310   if (xr->width / (xr->font_height / 2) < MIN_WIDTH)
311     {
312       error (0, 0, _("The defined page is not wide enough to hold at least %d "
313                      "characters in the default font.  In fact, there's only "
314                      "room for %d characters."),
315              MIN_WIDTH,
316              xr->width / (xr->font_height / 2));
317       goto error;
318     }
319
320   if (xr->length / xr->font_height < MIN_LENGTH)
321     {
322       error (0, 0, _("The defined page is not long "
323                      "enough to hold margins and headers, plus least %d "
324                      "lines of the default fonts.  In fact, there's only "
325                      "room for %d lines."),
326              MIN_LENGTH,
327              xr->length / xr->font_height);
328       goto error;
329     }
330
331   draw_headers (xr);
332
333   return &xr->driver;
334
335  error:
336   output_driver_destroy (&xr->driver);
337   return NULL;
338 }
339
340 static struct output_driver *
341 xr_pdf_create (const char *file_name, enum settings_output_devices device_type,
342                struct string_map *o)
343 {
344   return xr_create (file_name, device_type, o, XR_PDF);
345 }
346
347 static struct output_driver *
348 xr_ps_create (const char *file_name, enum settings_output_devices device_type,
349                struct string_map *o)
350 {
351   return xr_create (file_name, device_type, o, XR_PS);
352 }
353
354 static struct output_driver *
355 xr_svg_create (const char *file_name, enum settings_output_devices device_type,
356                struct string_map *o)
357 {
358   return xr_create (file_name, device_type, o, XR_SVG);
359 }
360
361 static void
362 xr_destroy (struct output_driver *driver)
363 {
364   struct xr_driver *xr = xr_driver_cast (driver);
365   size_t i;
366
367   xr_driver_destroy_fsm (xr);
368
369   if (xr->cairo != NULL)
370     {
371       cairo_status_t status;
372
373       cairo_surface_finish (cairo_get_target (xr->cairo));
374       status = cairo_status (xr->cairo);
375       if (status != CAIRO_STATUS_SUCCESS)
376         error (0, 0, _("error drawing output for %s driver: %s"),
377                output_driver_get_name (driver),
378                cairo_status_to_string (status));
379       cairo_destroy (xr->cairo);
380     }
381
382   free (xr->command_name);
383   for (i = 0; i < XR_N_FONTS; i++)
384     free_font (&xr->fonts[i]);
385   free (xr->params);
386   free (xr);
387 }
388
389 static void
390 xr_flush (struct output_driver *driver)
391 {
392   struct xr_driver *xr = xr_driver_cast (driver);
393
394   cairo_surface_flush (cairo_get_target (xr->cairo));
395 }
396
397 static void
398 xr_init_caption_cell (const char *caption, struct table_cell *cell)
399 {
400   cell->contents = caption;
401   cell->options = TAB_LEFT;
402   cell->destructor = NULL;
403 }
404
405 static struct render_page *
406 xr_render_table_item (struct xr_driver *xr, const struct table_item *item,
407                       int *caption_heightp)
408 {
409   const char *caption = table_item_get_caption (item);
410
411   if (caption != NULL)
412     {
413       /* XXX doesn't do well with very large captions */
414       struct table_cell cell;
415       xr_init_caption_cell (caption, &cell);
416       *caption_heightp = xr_measure_cell_height (xr, &cell, xr->width);
417     }
418   else
419     *caption_heightp = 0;
420
421   return render_page_create (xr->params, table_item_get_table (item));
422 }
423
424 static void
425 xr_submit (struct output_driver *driver, const struct output_item *output_item)
426 {
427   struct xr_driver *xr = xr_driver_cast (driver);
428
429   xr_driver_output_item (xr, output_item);
430   while (xr_driver_need_new_page (xr))
431     {
432       cairo_restore (xr->cairo);
433       cairo_show_page (xr->cairo);
434       cairo_save (xr->cairo);
435       xr_driver_next_page (xr, xr->cairo);
436     }
437 }
438 \f
439 /* Functions for rendering a series of output items to a series of Cairo
440    contexts, with pagination, possibly including headers.
441
442    Used by PSPPIRE for printing, and by the basic Cairo output driver above as
443    its underlying implementation.
444
445    See the big comment in cairo.h for intended usage. */
446
447 /* Gives new page CAIRO to XR for output.  CAIRO may be null to skip actually
448    rendering the page (which might be useful to find out how many pages an
449    output document has without actually rendering it). */
450 void
451 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
452 {
453   if (cairo != NULL)
454     cairo_translate (cairo,
455                      xr_to_pt (xr->left_margin),
456                      xr_to_pt (xr->top_margin));
457
458   xr->page_number++;
459   xr->cairo = cairo;
460   xr->y = 0;
461   draw_headers (xr);
462   xr_driver_run_fsm (xr);
463 }
464
465 /* Start rendering OUTPUT_ITEM to XR.  Only valid if XR is not in the middle of
466    rendering a previous output item, that is, only if xr_driver_need_new_page()
467    returns false. */
468 void
469 xr_driver_output_item (struct xr_driver *xr,
470                        const struct output_item *output_item)
471 {
472   assert (xr->fsm == NULL);
473   xr->fsm = xr_render_output_item (xr, output_item);
474   xr_driver_run_fsm (xr);
475 }
476
477 /* Returns true if XR is in the middle of rendering an output item and needs a
478    new page to be appended using xr_driver_next_page() to make progress,
479    otherwise false. */
480 bool
481 xr_driver_need_new_page (const struct xr_driver *xr)
482 {
483   return xr->fsm != NULL;
484 }
485
486 /* Returns true if the current page doesn't have any content yet (besides
487    headers, if enabled). */
488 bool
489 xr_driver_is_page_blank (const struct xr_driver *xr)
490 {
491   return xr->y == 0;
492 }
493
494 static void
495 xr_driver_destroy_fsm (struct xr_driver *xr)
496 {
497   if (xr->fsm != NULL)
498     {
499       xr->fsm->destroy (xr->fsm);
500       xr->fsm = NULL;
501     }
502 }
503
504 static void
505 xr_driver_run_fsm (struct xr_driver *xr)
506 {
507   if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
508     xr_driver_destroy_fsm (xr);
509 }
510 \f
511 static void
512 xr_layout_cell (struct xr_driver *, const struct table_cell *,
513                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
514                 PangoWrapMode, int *width, int *height);
515
516 static void
517 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
518 {
519   cairo_new_path (xr->cairo);
520   cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
521   cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
522   cairo_stroke (xr->cairo);
523 }
524
525 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
526    shortening it to X0...X1 if SHORTEN is true.
527    Draws a horizontal line X1...X3 at Y if RIGHT says so,
528    shortening it to X2...X3 if SHORTEN is true. */
529 static void
530 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
531            enum render_line_style left, enum render_line_style right,
532            bool shorten)
533 {
534   if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
535     dump_line (xr, x0, y, x3, y);
536   else
537     {
538       if (left != RENDER_LINE_NONE)
539         dump_line (xr, x0, y, shorten ? x1 : x2, y);
540       if (right != RENDER_LINE_NONE)
541         dump_line (xr, shorten ? x2 : x1, y, x3, y);
542     }
543 }
544
545 /* Draws a vertical line Y0...Y2 at X if TOP says so,
546    shortening it to Y0...Y1 if SHORTEN is true.
547    Draws a vertical line Y1...Y3 at X if BOTTOM says so,
548    shortening it to Y2...Y3 if SHORTEN is true. */
549 static void
550 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
551            enum render_line_style top, enum render_line_style bottom,
552            bool shorten)
553 {
554   if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
555     dump_line (xr, x, y0, x, y3);
556   else
557     {
558       if (top != RENDER_LINE_NONE)
559         dump_line (xr, x, y0, x, shorten ? y1 : y2);
560       if (bottom != RENDER_LINE_NONE)
561         dump_line (xr, x, shorten ? y2 : y1, x, y3);
562     }
563 }
564
565 static void
566 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
567               enum render_line_style styles[TABLE_N_AXES][2])
568 {
569   const int x0 = bb[H][0];
570   const int y0 = bb[V][0];
571   const int x3 = bb[H][1];
572   const int y3 = bb[V][1];
573   const int top = styles[H][0];
574   const int left = styles[V][0];
575   const int bottom = styles[H][1];
576   const int right = styles[V][1];
577
578   /* The algorithm here is somewhat subtle, to allow it to handle
579      all the kinds of intersections that we need.
580
581      Three additional ordinates are assigned along the x axis.  The
582      first is xc, midway between x0 and x3.  The others are x1 and
583      x2; for a single vertical line these are equal to xc, and for
584      a double vertical line they are the ordinates of the left and
585      right half of the double line.
586
587      yc, y1, and y2 are assigned similarly along the y axis.
588
589      The following diagram shows the coordinate system and output
590      for double top and bottom lines, single left line, and no
591      right line:
592
593                  x0       x1 xc  x2      x3
594                y0 ________________________
595                   |        #     #       |
596                   |        #     #       |
597                   |        #     #       |
598                   |        #     #       |
599                   |        #     #       |
600      y1 = y2 = yc |#########     #       |
601                   |        #     #       |
602                   |        #     #       |
603                   |        #     #       |
604                   |        #     #       |
605                y3 |________#_____#_______|
606   */
607   struct xr_driver *xr = xr_;
608
609   /* Offset from center of each line in a pair of double lines. */
610   int double_line_ofs = (xr->line_space + xr->line_width) / 2;
611
612   /* Are the lines along each axis single or double?
613      (It doesn't make sense to have different kinds of line on the
614      same axis, so we don't try to gracefully handle that case.) */
615   bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
616   bool double_horz = left == RENDER_LINE_DOUBLE || right == RENDER_LINE_DOUBLE;
617
618   /* When horizontal lines are doubled,
619      the left-side line along y1 normally runs from x0 to x2,
620      and the right-side line along y1 from x3 to x1.
621      If the top-side line is also doubled, we shorten the y1 lines,
622      so that the left-side line runs only to x1,
623      and the right-side line only to x2.
624      Otherwise, the horizontal line at y = y1 below would cut off
625      the intersection, which looks ugly:
626                x0       x1     x2      x3
627              y0 ________________________
628                 |        #     #       |
629                 |        #     #       |
630                 |        #     #       |
631                 |        #     #       |
632              y1 |#########     ########|
633                 |                      |
634                 |                      |
635              y2 |######################|
636                 |                      |
637                 |                      |
638              y3 |______________________|
639      It is more of a judgment call when the horizontal line is
640      single.  We actually choose to cut off the line anyhow, as
641      shown in the first diagram above.
642   */
643   bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
644   bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
645   bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
646   int horz_line_ofs = double_vert ? double_line_ofs : 0;
647   int xc = (x0 + x3) / 2;
648   int x1 = xc - horz_line_ofs;
649   int x2 = xc + horz_line_ofs;
650
651   bool shorten_x1_lines = left == RENDER_LINE_DOUBLE;
652   bool shorten_x2_lines = right == RENDER_LINE_DOUBLE;
653   bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
654   int vert_line_ofs = double_horz ? double_line_ofs : 0;
655   int yc = (y0 + y3) / 2;
656   int y1 = yc - vert_line_ofs;
657   int y2 = yc + vert_line_ofs;
658
659   if (!double_horz)
660     horz_line (xr, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
661   else
662     {
663       horz_line (xr, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
664       horz_line (xr, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
665     }
666
667   if (!double_vert)
668     vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
669   else
670     {
671       vert_line (xr, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
672       vert_line (xr, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
673     }
674 }
675
676 static void
677 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
678                        int *min_width, int *max_width)
679 {
680   struct xr_driver *xr = xr_;
681   int bb[TABLE_N_AXES][2];
682   int clip[TABLE_N_AXES][2];
683   int h;
684
685   bb[H][0] = 0;
686   bb[H][1] = INT_MAX;
687   bb[V][0] = 0;
688   bb[V][1] = INT_MAX;
689   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
690   xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, max_width, &h);
691
692   bb[H][1] = 1;
693   xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, min_width, &h);
694 }
695
696 static int
697 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
698 {
699   struct xr_driver *xr = xr_;
700   int bb[TABLE_N_AXES][2];
701   int clip[TABLE_N_AXES][2];
702   int w, h;
703
704   bb[H][0] = 0;
705   bb[H][1] = width;
706   bb[V][0] = 0;
707   bb[V][1] = INT_MAX;
708   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
709   xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
710   return h;
711 }
712
713 static void
714 xr_draw_cell (void *xr_, const struct table_cell *cell,
715               int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
716 {
717   struct xr_driver *xr = xr_;
718   int w, h;
719
720   xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
721 }
722 \f
723 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
724    and with the given cell OPTIONS for XR. */
725 static int
726 draw_text (struct xr_driver *xr, const char *string, int x, int y,
727            int max_width, unsigned int options)
728 {
729   struct table_cell cell;
730   int bb[TABLE_N_AXES][2];
731   int w, h;
732
733   cell.contents = string;
734   cell.options = options;
735   bb[H][0] = x;
736   bb[V][0] = y - xr->y;
737   bb[H][1] = x + max_width;
738   bb[V][1] = xr->font_height - xr->y;
739   xr_layout_cell (xr, &cell, bb, bb, PANGO_WRAP_WORD_CHAR, &w, &h);
740   return w;
741 }
742
743 /* Writes LEFT left-justified and RIGHT right-justified within
744    (X0...X1) at Y.  LEFT or RIGHT or both may be null. */
745 static void
746 draw_header_line (struct xr_driver *xr, const char *left, const char *right,
747                   int x0, int x1, int y)
748 {
749   int right_width = 0;
750   if (right != NULL)
751     right_width = (draw_text (xr, right, x0, y, x1 - x0, TAB_RIGHT)
752                    + xr->font_height / 2);
753   if (left != NULL)
754     draw_text (xr, left, x0, y, x1 - x0 - right_width, TAB_LEFT);
755 }
756
757 /* Draw top of page headers for XR. */
758 static void
759 draw_headers (struct xr_driver *xr)
760 {
761   char *r1, *r2;
762   int x0, x1;
763   int y;
764
765   if (!xr->headers || xr->cairo == NULL)
766     return;
767
768   y = -3 * xr->font_height;
769   x0 = xr->font_height / 2;
770   x1 = xr->width - xr->font_height / 2;
771
772   /* Draw box. */
773   cairo_rectangle (xr->cairo, 0, xr_to_pt (y), xr_to_pt (xr->width),
774                    xr_to_pt (2 * (xr->font_height
775                                   + xr->line_width + xr->line_gutter)));
776   cairo_save (xr->cairo);
777   cairo_set_source_rgb (xr->cairo, 0.9, 0.9, 0.9);
778   cairo_fill_preserve (xr->cairo);
779   cairo_restore (xr->cairo);
780   cairo_stroke (xr->cairo);
781
782   y += xr->line_width + xr->line_gutter;
783
784   r1 = xasprintf (_("%s - Page %d"), get_start_date (), xr->page_number);
785   r2 = xasprintf ("%s - %s", version, host_system);
786
787   draw_header_line (xr, xr->title, r1, x0, x1, y);
788   y += xr->font_height;
789
790   draw_header_line (xr, xr->subtitle, r2, x0, x1, y);
791
792   free (r1);
793   free (r2);
794 }
795 \f
796 static void
797 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
798                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
799                 PangoWrapMode wrap, int *width, int *height)
800 {
801   struct xr_font *font;
802
803   font = (cell->options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
804           : cell->options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
805           : &xr->fonts[XR_FONT_PROPORTIONAL]);
806
807   pango_layout_set_text (font->layout, cell->contents, -1);
808
809   pango_layout_set_alignment (
810     font->layout,
811     ((cell->options & TAB_ALIGNMENT) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
812      : (cell->options & TAB_ALIGNMENT) == TAB_LEFT ? PANGO_ALIGN_LEFT
813      : PANGO_ALIGN_CENTER));
814   pango_layout_set_width (font->layout,
815                           bb[H][1] == INT_MAX ? -1 : bb[H][1] - bb[H][0]);
816   pango_layout_set_wrap (font->layout, wrap);
817
818   if (clip[H][0] != clip[H][1])
819     {
820       cairo_save (xr->cairo);
821
822       if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
823         {
824           double x0 = xr_to_pt (clip[H][0]);
825           double y0 = xr_to_pt (clip[V][0] + xr->y);
826           double x1 = xr_to_pt (clip[H][1]);
827           double y1 = xr_to_pt (clip[V][1] + xr->y);
828
829           cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
830           cairo_clip (xr->cairo);
831         }
832
833       cairo_translate (xr->cairo,
834                        xr_to_pt (bb[H][0]),
835                        xr_to_pt (bb[V][0] + xr->y));
836       pango_cairo_show_layout (xr->cairo, font->layout);
837       cairo_restore (xr->cairo);
838     }
839
840   if (width != NULL || height != NULL)
841     {
842       int w, h;
843
844       pango_layout_get_size (font->layout, &w, &h);
845       if (width != NULL)
846         *width = w;
847       if (height != NULL)
848         *height = h;
849     }
850 }
851 \f
852 /* Attempts to load FONT, initializing its other members based on
853    its 'string' member and the information in DRIVER.  Returns true
854    if successful, otherwise false. */
855 static bool
856 load_font (struct xr_driver *xr, struct xr_font *font)
857 {
858   PangoContext *context;
859   PangoLanguage *language;
860
861   font->desc = pango_font_description_from_string (font->string);
862   if (font->desc == NULL)
863     {
864       error (0, 0, _("\"%s\": bad font specification"), font->string);
865       return false;
866     }
867   pango_font_description_set_absolute_size (font->desc, xr->font_height);
868
869   font->layout = pango_cairo_create_layout (xr->cairo);
870   pango_layout_set_font_description (font->layout, font->desc);
871
872   language = pango_language_get_default ();
873   context = pango_layout_get_context (font->layout);
874   font->metrics = pango_context_get_metrics (context, font->desc, language);
875
876   return true;
877 }
878
879 /* Frees FONT. */
880 static void
881 free_font (struct xr_font *font)
882 {
883   free (font->string);
884   if (font->desc != NULL)
885     pango_font_description_free (font->desc);
886   pango_font_metrics_unref (font->metrics);
887   if (font->layout != NULL)
888     g_object_unref (font->layout);
889 }
890
891 struct output_driver_factory pdf_driver_factory = { "pdf", xr_pdf_create };
892 struct output_driver_factory ps_driver_factory = { "ps", xr_ps_create };
893 struct output_driver_factory svg_driver_factory = { "svg", xr_svg_create };
894
895 static const struct output_driver_class cairo_driver_class =
896 {
897   "cairo",
898   xr_destroy,
899   xr_submit,
900   xr_flush,
901 };
902 \f
903 /* GUI rendering helpers. */
904
905 struct xr_rendering
906   {
907     /* Table items. */
908     struct render_page *page;
909     struct xr_driver *xr;
910     int title_height;
911
912     /* Chart items. */
913     struct chart_item *chart;
914   };
915
916 #define CHART_WIDTH 500
917 #define CHART_HEIGHT 375
918
919 struct xr_driver *
920 xr_driver_create (cairo_t *cairo, struct string_map *options)
921 {
922   struct xr_driver *xr = xr_allocate ("cairo", 0, options);
923   if (!xr_set_cairo (xr, cairo))
924     {
925       output_driver_destroy (&xr->driver);
926       return NULL;
927     }
928   return xr;
929 }
930
931 static struct xr_rendering *
932 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
933 {
934   struct table_item *table_item;
935   struct xr_rendering *r;
936
937   table_item = table_item_create (table_from_string (0, text), NULL);
938   r = xr_rendering_create (xr, &table_item->output_item, cr);
939   table_item_unref (table_item);
940
941   return r;
942 }
943
944 struct xr_rendering *
945 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
946                      cairo_t *cr)
947 {
948   struct xr_rendering *r = NULL;
949
950   if (is_text_item (item))
951     r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
952                                   cr);
953   else if (is_message_item (item))
954     {
955       const struct message_item *message_item = to_message_item (item);
956       const struct msg *msg = message_item_get_msg (message_item);
957       char *s = msg_to_string (msg, NULL);
958       r = xr_rendering_create_text (xr, s, cr);
959       free (s);
960     }
961   else if (is_table_item (item))
962     {
963       r = xzalloc (sizeof *r);
964       r->xr = xr;
965       xr_set_cairo (xr, cr);
966       r->page = xr_render_table_item (xr, to_table_item (item),
967                                       &r->title_height);
968     }
969   else if (is_chart_item (item))
970     {
971       r = xzalloc (sizeof *r);
972       r->chart = to_chart_item (output_item_ref (item));
973     }
974
975   return r;
976 }
977
978 void
979 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
980 {
981   if (r->chart == NULL)
982     {
983       *w = render_page_get_size (r->page, H) / 1024;
984       *h = (render_page_get_size (r->page, V) + r->title_height) / 1024;
985     }
986   else
987     {
988       *w = CHART_WIDTH;
989       *h = CHART_HEIGHT;
990     }
991 }
992
993 void
994 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr)
995 {
996   if (r->chart == NULL)
997     {
998       struct xr_driver *xr = r->xr;
999
1000       xr_set_cairo (xr, cr);
1001       xr->y = 0;
1002       render_page_draw (r->page);
1003     }
1004   else
1005     xr_draw_chart (r->chart, cr, 0, 0, CHART_WIDTH, CHART_HEIGHT);
1006 }
1007
1008 void
1009 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1010                double x, double y, double width, double height)
1011 {
1012   struct xrchart_geometry geom;
1013
1014   cairo_save (cr);
1015   cairo_translate (cr, x, y + height);
1016   cairo_scale (cr, 1.0, -1.0);
1017   xrchart_geometry_init (cr, &geom, width, height);
1018   if (is_boxplot (chart_item))
1019     xrchart_draw_boxplot (chart_item, cr, &geom);
1020   else if (is_histogram_chart (chart_item))
1021     xrchart_draw_histogram (chart_item, cr, &geom);
1022   else if (is_np_plot_chart (chart_item))
1023     xrchart_draw_np_plot (chart_item, cr, &geom);
1024   else if (is_piechart (chart_item))
1025     xrchart_draw_piechart (chart_item, cr, &geom);
1026   else if (is_roc_chart (chart_item))
1027     xrchart_draw_roc (chart_item, cr, &geom);
1028   else if (is_scree (chart_item))
1029     xrchart_draw_scree (chart_item, cr, &geom);
1030   else
1031     NOT_REACHED ();
1032   xrchart_geometry_free (cr, &geom);
1033
1034   cairo_restore (cr);
1035 }
1036
1037 char *
1038 xr_draw_png_chart (const struct chart_item *item,
1039                    const char *file_name_template, int number)
1040 {
1041   const int width = 640;
1042   const int length = 480;
1043
1044   cairo_surface_t *surface;
1045   cairo_status_t status;
1046   const char *number_pos;
1047   char *file_name;
1048   cairo_t *cr;
1049
1050   number_pos = strchr (file_name_template, '#');
1051   if (number_pos != NULL)
1052     file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1053                            file_name_template, number, number_pos + 1);
1054   else
1055     file_name = xstrdup (file_name_template);
1056
1057   surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1058   cr = cairo_create (surface);
1059
1060   cairo_save (cr);
1061   cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
1062   cairo_rectangle (cr, 0, 0, width, length);
1063   cairo_fill (cr);
1064   cairo_restore (cr);
1065
1066   cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
1067
1068   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1069
1070   status = cairo_surface_write_to_png (surface, file_name);
1071   if (status != CAIRO_STATUS_SUCCESS)
1072     error (0, 0, _("error writing output file \"%s\": %s"),
1073            file_name, cairo_status_to_string (status));
1074
1075   cairo_destroy (cr);
1076   cairo_surface_destroy (surface);
1077
1078   return file_name;
1079 }
1080 \f
1081 struct xr_table_state
1082   {
1083     struct xr_render_fsm fsm;
1084     struct table_item *table_item;
1085     struct render_break x_break;
1086     struct render_break y_break;
1087     int caption_height;
1088   };
1089
1090 static bool
1091 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1092 {
1093   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1094
1095   for (;;)
1096     {
1097       struct render_page *y_slice;
1098       int space;
1099
1100       while (!render_break_has_next (&ts->y_break))
1101         {
1102           struct render_page *x_slice;
1103
1104           render_break_destroy (&ts->y_break);
1105           if (!render_break_has_next (&ts->x_break))
1106             return false;
1107
1108           x_slice = render_break_next (&ts->x_break, xr->width);
1109           render_break_init (&ts->y_break, x_slice, V);
1110         }
1111
1112       space = xr->length - xr->y;
1113       if (render_break_next_size (&ts->y_break) > space)
1114         {
1115           assert (xr->y > 0);
1116           return true;
1117         }
1118
1119       y_slice = render_break_next (&ts->y_break, space);
1120       if (ts->caption_height)
1121         {
1122           if (xr->cairo)
1123             {
1124               struct table_cell cell;
1125               int bb[TABLE_N_AXES][2];
1126
1127               xr_init_caption_cell (table_item_get_caption (ts->table_item),
1128                                     &cell);
1129               bb[H][0] = 0;
1130               bb[H][1] = xr->width;
1131               bb[V][0] = 0;
1132               bb[V][1] = ts->caption_height;
1133               xr_draw_cell (xr, &cell, bb, bb);
1134             }
1135           xr->y += ts->caption_height;
1136           ts->caption_height = 0;
1137         }
1138
1139       if (xr->cairo)
1140         render_page_draw (y_slice);
1141       xr->y += render_page_get_size (y_slice, V);
1142       render_page_unref (y_slice);
1143     }
1144 }
1145
1146 static void
1147 xr_table_destroy (struct xr_render_fsm *fsm)
1148 {
1149   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1150
1151   table_item_unref (ts->table_item);
1152   render_break_destroy (&ts->x_break);
1153   render_break_destroy (&ts->y_break);
1154   free (ts);
1155 }
1156
1157 static struct xr_render_fsm *
1158 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1159 {
1160   struct xr_table_state *ts;
1161   struct render_page *page;
1162
1163   ts = xmalloc (sizeof *ts);
1164   ts->fsm.render = xr_table_render;
1165   ts->fsm.destroy = xr_table_destroy;
1166   ts->table_item = table_item_ref (table_item);
1167
1168   if (xr->y > 0)
1169     xr->y += xr->font_height;
1170
1171   page = xr_render_table_item (xr, table_item, &ts->caption_height);
1172   xr->params->size[V] = xr->length - ts->caption_height;
1173
1174   render_break_init (&ts->x_break, page, H);
1175   render_break_init_empty (&ts->y_break);
1176
1177   return &ts->fsm;
1178 }
1179 \f
1180 struct xr_chart_state
1181   {
1182     struct xr_render_fsm fsm;
1183     struct chart_item *chart_item;
1184   };
1185
1186 static bool
1187 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1188 {
1189   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1190
1191   if (xr->y > 0)
1192     return true;
1193
1194   if (xr->cairo != NULL)
1195     xr_draw_chart (cs->chart_item, xr->cairo, 0.0, 0.0,
1196                    xr_to_pt (xr->width), xr_to_pt (xr->length));
1197   xr->y = xr->length;
1198
1199   return false;
1200 }
1201
1202 static void
1203 xr_chart_destroy (struct xr_render_fsm *fsm)
1204 {
1205   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1206
1207   chart_item_unref (cs->chart_item);
1208   free (cs);
1209 }
1210
1211 static struct xr_render_fsm *
1212 xr_render_chart (const struct chart_item *chart_item)
1213 {
1214   struct xr_chart_state *cs;
1215
1216   cs = xmalloc (sizeof *cs);
1217   cs->fsm.render = xr_chart_render;
1218   cs->fsm.destroy = xr_chart_destroy;
1219   cs->chart_item = chart_item_ref (chart_item);
1220
1221   return &cs->fsm;
1222 }
1223 \f
1224 static bool
1225 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1226 {
1227   return xr->y > 0;
1228 }
1229
1230 static void
1231 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1232 {
1233   /* Nothing to do. */
1234 }
1235
1236 static struct xr_render_fsm *
1237 xr_render_eject (void)
1238 {
1239   static struct xr_render_fsm eject_renderer =
1240     {
1241       xr_eject_render,
1242       xr_eject_destroy
1243     };
1244
1245   return &eject_renderer;
1246 }
1247 \f
1248 static struct xr_render_fsm *
1249 xr_create_text_renderer (struct xr_driver *xr, const char *text)
1250 {
1251   struct table_item *table_item;
1252   struct xr_render_fsm *fsm;
1253
1254   table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
1255   fsm = xr_render_table (xr, table_item);
1256   table_item_unref (table_item);
1257
1258   return fsm;
1259 }
1260
1261 static struct xr_render_fsm *
1262 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1263 {
1264   enum text_item_type type = text_item_get_type (text_item);
1265   const char *text = text_item_get_text (text_item);
1266
1267   switch (type)
1268     {
1269     case TEXT_ITEM_TITLE:
1270       free (xr->title);
1271       xr->title = xstrdup (text);
1272       draw_headers (xr);
1273       break;
1274
1275     case TEXT_ITEM_SUBTITLE:
1276       free (xr->subtitle);
1277       xr->subtitle = xstrdup (text);
1278       draw_headers (xr);
1279       break;
1280
1281     case TEXT_ITEM_COMMAND_CLOSE:
1282       break;
1283
1284     case TEXT_ITEM_BLANK_LINE:
1285       if (xr->y > 0)
1286         xr->y += xr->font_height;
1287       break;
1288
1289     case TEXT_ITEM_EJECT_PAGE:
1290       if (xr->y > 0)
1291         return xr_render_eject ();
1292       break;
1293
1294     default:
1295       return xr_create_text_renderer (xr, text);
1296     }
1297
1298   return NULL;
1299 }
1300
1301 static struct xr_render_fsm *
1302 xr_render_message (struct xr_driver *xr,
1303                    const struct message_item *message_item)
1304 {
1305   const struct msg *msg = message_item_get_msg (message_item);
1306   struct xr_render_fsm *fsm;
1307   char *s;
1308
1309   s = msg_to_string (msg, xr->command_name);
1310   fsm = xr_create_text_renderer (xr, s);
1311   free (s);
1312
1313   return fsm;
1314 }
1315
1316 static struct xr_render_fsm *
1317 xr_render_output_item (struct xr_driver *xr,
1318                        const struct output_item *output_item)
1319 {
1320   if (is_table_item (output_item))
1321     return xr_render_table (xr, to_table_item (output_item));
1322   else if (is_chart_item (output_item))
1323     return xr_render_chart (to_chart_item (output_item));
1324   else if (is_text_item (output_item))
1325     return xr_render_text (xr, to_text_item (output_item));
1326   else if (is_message_item (output_item))
1327     return xr_render_message (xr, to_message_item (output_item));
1328   else
1329     return NULL;
1330 }