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