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