cairo: Implement xr_driver_destroy() and use it during printing.
[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 /* Destroy XR, which should have been created with xr_driver_create().  Any
932    cairo_t added to XR is not destroyed, because it is owned by the client. */
933 void
934 xr_driver_destroy (struct xr_driver *xr)
935 {
936   if (xr != NULL)
937     {
938       xr->cairo = NULL;
939       output_driver_destroy (&xr->driver);
940     }
941 }
942
943 static struct xr_rendering *
944 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
945 {
946   struct table_item *table_item;
947   struct xr_rendering *r;
948
949   table_item = table_item_create (table_from_string (0, text), NULL);
950   r = xr_rendering_create (xr, &table_item->output_item, cr);
951   table_item_unref (table_item);
952
953   return r;
954 }
955
956 struct xr_rendering *
957 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
958                      cairo_t *cr)
959 {
960   struct xr_rendering *r = NULL;
961
962   if (is_text_item (item))
963     r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
964                                   cr);
965   else if (is_message_item (item))
966     {
967       const struct message_item *message_item = to_message_item (item);
968       const struct msg *msg = message_item_get_msg (message_item);
969       char *s = msg_to_string (msg, NULL);
970       r = xr_rendering_create_text (xr, s, cr);
971       free (s);
972     }
973   else if (is_table_item (item))
974     {
975       r = xzalloc (sizeof *r);
976       r->xr = xr;
977       xr_set_cairo (xr, cr);
978       r->page = xr_render_table_item (xr, to_table_item (item),
979                                       &r->title_height);
980     }
981   else if (is_chart_item (item))
982     {
983       r = xzalloc (sizeof *r);
984       r->chart = to_chart_item (output_item_ref (item));
985     }
986
987   return r;
988 }
989
990 void
991 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
992 {
993   if (r->chart == NULL)
994     {
995       *w = render_page_get_size (r->page, H) / 1024;
996       *h = (render_page_get_size (r->page, V) + r->title_height) / 1024;
997     }
998   else
999     {
1000       *w = CHART_WIDTH;
1001       *h = CHART_HEIGHT;
1002     }
1003 }
1004
1005 void
1006 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr)
1007 {
1008   if (r->chart == NULL)
1009     {
1010       struct xr_driver *xr = r->xr;
1011
1012       xr_set_cairo (xr, cr);
1013       xr->y = 0;
1014       render_page_draw (r->page);
1015     }
1016   else
1017     xr_draw_chart (r->chart, cr, 0, 0, CHART_WIDTH, CHART_HEIGHT);
1018 }
1019
1020 void
1021 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1022                double x, double y, double width, double height)
1023 {
1024   struct xrchart_geometry geom;
1025
1026   cairo_save (cr);
1027   cairo_translate (cr, x, y + height);
1028   cairo_scale (cr, 1.0, -1.0);
1029   xrchart_geometry_init (cr, &geom, width, height);
1030   if (is_boxplot (chart_item))
1031     xrchart_draw_boxplot (chart_item, cr, &geom);
1032   else if (is_histogram_chart (chart_item))
1033     xrchart_draw_histogram (chart_item, cr, &geom);
1034   else if (is_np_plot_chart (chart_item))
1035     xrchart_draw_np_plot (chart_item, cr, &geom);
1036   else if (is_piechart (chart_item))
1037     xrchart_draw_piechart (chart_item, cr, &geom);
1038   else if (is_roc_chart (chart_item))
1039     xrchart_draw_roc (chart_item, cr, &geom);
1040   else if (is_scree (chart_item))
1041     xrchart_draw_scree (chart_item, cr, &geom);
1042   else
1043     NOT_REACHED ();
1044   xrchart_geometry_free (cr, &geom);
1045
1046   cairo_restore (cr);
1047 }
1048
1049 char *
1050 xr_draw_png_chart (const struct chart_item *item,
1051                    const char *file_name_template, int number)
1052 {
1053   const int width = 640;
1054   const int length = 480;
1055
1056   cairo_surface_t *surface;
1057   cairo_status_t status;
1058   const char *number_pos;
1059   char *file_name;
1060   cairo_t *cr;
1061
1062   number_pos = strchr (file_name_template, '#');
1063   if (number_pos != NULL)
1064     file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1065                            file_name_template, number, number_pos + 1);
1066   else
1067     file_name = xstrdup (file_name_template);
1068
1069   surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1070   cr = cairo_create (surface);
1071
1072   cairo_save (cr);
1073   cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
1074   cairo_rectangle (cr, 0, 0, width, length);
1075   cairo_fill (cr);
1076   cairo_restore (cr);
1077
1078   cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
1079
1080   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1081
1082   status = cairo_surface_write_to_png (surface, file_name);
1083   if (status != CAIRO_STATUS_SUCCESS)
1084     error (0, 0, _("error writing output file \"%s\": %s"),
1085            file_name, cairo_status_to_string (status));
1086
1087   cairo_destroy (cr);
1088   cairo_surface_destroy (surface);
1089
1090   return file_name;
1091 }
1092 \f
1093 struct xr_table_state
1094   {
1095     struct xr_render_fsm fsm;
1096     struct table_item *table_item;
1097     struct render_break x_break;
1098     struct render_break y_break;
1099     int caption_height;
1100   };
1101
1102 static bool
1103 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1104 {
1105   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1106
1107   for (;;)
1108     {
1109       struct render_page *y_slice;
1110       int space;
1111
1112       while (!render_break_has_next (&ts->y_break))
1113         {
1114           struct render_page *x_slice;
1115
1116           render_break_destroy (&ts->y_break);
1117           if (!render_break_has_next (&ts->x_break))
1118             return false;
1119
1120           x_slice = render_break_next (&ts->x_break, xr->width);
1121           render_break_init (&ts->y_break, x_slice, V);
1122         }
1123
1124       space = xr->length - xr->y;
1125       if (render_break_next_size (&ts->y_break) > space)
1126         {
1127           assert (xr->y > 0);
1128           return true;
1129         }
1130
1131       y_slice = render_break_next (&ts->y_break, space);
1132       if (ts->caption_height)
1133         {
1134           if (xr->cairo)
1135             {
1136               struct table_cell cell;
1137               int bb[TABLE_N_AXES][2];
1138
1139               xr_init_caption_cell (table_item_get_caption (ts->table_item),
1140                                     &cell);
1141               bb[H][0] = 0;
1142               bb[H][1] = xr->width;
1143               bb[V][0] = 0;
1144               bb[V][1] = ts->caption_height;
1145               xr_draw_cell (xr, &cell, bb, bb);
1146             }
1147           xr->y += ts->caption_height;
1148           ts->caption_height = 0;
1149         }
1150
1151       if (xr->cairo)
1152         render_page_draw (y_slice);
1153       xr->y += render_page_get_size (y_slice, V);
1154       render_page_unref (y_slice);
1155     }
1156 }
1157
1158 static void
1159 xr_table_destroy (struct xr_render_fsm *fsm)
1160 {
1161   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1162
1163   table_item_unref (ts->table_item);
1164   render_break_destroy (&ts->x_break);
1165   render_break_destroy (&ts->y_break);
1166   free (ts);
1167 }
1168
1169 static struct xr_render_fsm *
1170 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1171 {
1172   struct xr_table_state *ts;
1173   struct render_page *page;
1174
1175   ts = xmalloc (sizeof *ts);
1176   ts->fsm.render = xr_table_render;
1177   ts->fsm.destroy = xr_table_destroy;
1178   ts->table_item = table_item_ref (table_item);
1179
1180   if (xr->y > 0)
1181     xr->y += xr->font_height;
1182
1183   page = xr_render_table_item (xr, table_item, &ts->caption_height);
1184   xr->params->size[V] = xr->length - ts->caption_height;
1185
1186   render_break_init (&ts->x_break, page, H);
1187   render_break_init_empty (&ts->y_break);
1188
1189   return &ts->fsm;
1190 }
1191 \f
1192 struct xr_chart_state
1193   {
1194     struct xr_render_fsm fsm;
1195     struct chart_item *chart_item;
1196   };
1197
1198 static bool
1199 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1200 {
1201   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1202
1203   if (xr->y > 0)
1204     return true;
1205
1206   if (xr->cairo != NULL)
1207     xr_draw_chart (cs->chart_item, xr->cairo, 0.0, 0.0,
1208                    xr_to_pt (xr->width), xr_to_pt (xr->length));
1209   xr->y = xr->length;
1210
1211   return false;
1212 }
1213
1214 static void
1215 xr_chart_destroy (struct xr_render_fsm *fsm)
1216 {
1217   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1218
1219   chart_item_unref (cs->chart_item);
1220   free (cs);
1221 }
1222
1223 static struct xr_render_fsm *
1224 xr_render_chart (const struct chart_item *chart_item)
1225 {
1226   struct xr_chart_state *cs;
1227
1228   cs = xmalloc (sizeof *cs);
1229   cs->fsm.render = xr_chart_render;
1230   cs->fsm.destroy = xr_chart_destroy;
1231   cs->chart_item = chart_item_ref (chart_item);
1232
1233   return &cs->fsm;
1234 }
1235 \f
1236 static bool
1237 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1238 {
1239   return xr->y > 0;
1240 }
1241
1242 static void
1243 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1244 {
1245   /* Nothing to do. */
1246 }
1247
1248 static struct xr_render_fsm *
1249 xr_render_eject (void)
1250 {
1251   static struct xr_render_fsm eject_renderer =
1252     {
1253       xr_eject_render,
1254       xr_eject_destroy
1255     };
1256
1257   return &eject_renderer;
1258 }
1259 \f
1260 static struct xr_render_fsm *
1261 xr_create_text_renderer (struct xr_driver *xr, const char *text)
1262 {
1263   struct table_item *table_item;
1264   struct xr_render_fsm *fsm;
1265
1266   table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
1267   fsm = xr_render_table (xr, table_item);
1268   table_item_unref (table_item);
1269
1270   return fsm;
1271 }
1272
1273 static struct xr_render_fsm *
1274 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1275 {
1276   enum text_item_type type = text_item_get_type (text_item);
1277   const char *text = text_item_get_text (text_item);
1278
1279   switch (type)
1280     {
1281     case TEXT_ITEM_TITLE:
1282       free (xr->title);
1283       xr->title = xstrdup (text);
1284       draw_headers (xr);
1285       break;
1286
1287     case TEXT_ITEM_SUBTITLE:
1288       free (xr->subtitle);
1289       xr->subtitle = xstrdup (text);
1290       draw_headers (xr);
1291       break;
1292
1293     case TEXT_ITEM_COMMAND_CLOSE:
1294       break;
1295
1296     case TEXT_ITEM_BLANK_LINE:
1297       if (xr->y > 0)
1298         xr->y += xr->font_height;
1299       break;
1300
1301     case TEXT_ITEM_EJECT_PAGE:
1302       if (xr->y > 0)
1303         return xr_render_eject ();
1304       break;
1305
1306     default:
1307       return xr_create_text_renderer (xr, text);
1308     }
1309
1310   return NULL;
1311 }
1312
1313 static struct xr_render_fsm *
1314 xr_render_message (struct xr_driver *xr,
1315                    const struct message_item *message_item)
1316 {
1317   const struct msg *msg = message_item_get_msg (message_item);
1318   struct xr_render_fsm *fsm;
1319   char *s;
1320
1321   s = msg_to_string (msg, xr->command_name);
1322   fsm = xr_create_text_renderer (xr, s);
1323   free (s);
1324
1325   return fsm;
1326 }
1327
1328 static struct xr_render_fsm *
1329 xr_render_output_item (struct xr_driver *xr,
1330                        const struct output_item *output_item)
1331 {
1332   if (is_table_item (output_item))
1333     return xr_render_table (xr, to_table_item (output_item));
1334   else if (is_chart_item (output_item))
1335     return xr_render_chart (to_chart_item (output_item));
1336   else if (is_text_item (output_item))
1337     return xr_render_text (xr, to_text_item (output_item));
1338   else if (is_message_item (output_item))
1339     return xr_render_message (xr, to_message_item (output_item));
1340   else
1341     return NULL;
1342 }