7c6cf925c64ee2418cef9966066729c987637c0a
[pspp] / src / output / cairo.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 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/pool.h"
25 #include "libpspp/start-date.h"
26 #include "libpspp/str.h"
27 #include "libpspp/string-map.h"
28 #include "libpspp/version.h"
29 #include "data/file-handle-def.h"
30 #include "output/cairo-chart.h"
31 #include "output/chart-item-provider.h"
32 #include "output/charts/boxplot.h"
33 #include "output/charts/np-plot.h"
34 #include "output/charts/piechart.h"
35 #include "output/charts/barchart.h"
36 #include "output/charts/plot-hist.h"
37 #include "output/charts/roc-chart.h"
38 #include "output/charts/spreadlevel-plot.h"
39 #include "output/charts/scree.h"
40 #include "output/charts/scatterplot.h"
41 #include "output/driver-provider.h"
42 #include "output/group-item.h"
43 #include "output/message-item.h"
44 #include "output/options.h"
45 #include "output/render.h"
46 #include "output/tab.h"
47 #include "output/table-item.h"
48 #include "output/table.h"
49 #include "output/text-item.h"
50
51 #include <cairo/cairo-pdf.h>
52 #include <cairo/cairo-ps.h>
53 #include <cairo/cairo-svg.h>
54 #include <cairo/cairo.h>
55 #include <math.h>
56 #include <pango/pango-font.h>
57 #include <pango/pango-layout.h>
58 #include <pango/pango.h>
59 #include <pango/pangocairo.h>
60 #include <stdlib.h>
61
62 #include "gl/c-ctype.h"
63 #include "gl/c-strcase.h"
64 #include "gl/intprops.h"
65 #include "gl/minmax.h"
66 #include "gl/xalloc.h"
67
68 #include "gettext.h"
69 #define _(msgid) gettext (msgid)
70
71 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
72 #define H TABLE_HORZ
73 #define V TABLE_VERT
74
75 /* The unit used for internal measurements is inch/(72 * XR_POINT).
76    (Thus, XR_POINT units represent one point.) */
77 #define XR_POINT PANGO_SCALE
78
79 /* Conversions to and from points. */
80 static double
81 xr_to_pt (int x)
82 {
83   return x / (double) XR_POINT;
84 }
85
86 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
87 static int
88 px_to_xr (int x)
89 {
90   return x * (PANGO_SCALE * 72 / 96);
91 }
92
93 /* Dimensions for drawing lines in tables. */
94 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
95 #define XR_LINE_SPACE XR_POINT       /* Space between double lines. */
96
97 /* Output types. */
98 enum xr_output_type
99   {
100     XR_PDF,
101     XR_PS,
102     XR_SVG
103   };
104
105 /* Cairo fonts. */
106 enum xr_font_type
107   {
108     XR_FONT_PROPORTIONAL,
109     XR_FONT_EMPHASIS,
110     XR_FONT_FIXED,
111     XR_N_FONTS
112   };
113
114 /* A font for use with Cairo. */
115 struct xr_font
116   {
117     PangoFontDescription *desc;
118     PangoLayout *layout;
119   };
120
121 /* An output item whose rendering is in progress. */
122 struct xr_render_fsm
123   {
124     /* Renders as much of itself as it can on the current page.  Returns true
125        if rendering is complete, false if the output item needs another
126        page. */
127     bool (*render) (struct xr_render_fsm *, struct xr_driver *);
128
129     /* Destroys the output item. */
130     void (*destroy) (struct xr_render_fsm *);
131   };
132
133 /* Cairo output driver. */
134 struct xr_driver
135   {
136     struct output_driver driver;
137
138     /* User parameters. */
139     struct xr_font fonts[XR_N_FONTS];
140
141     int width;                  /* Page width minus margins. */
142     int length;                 /* Page length minus margins and header. */
143
144     int left_margin;            /* Left margin in inch/(72 * XR_POINT). */
145     int right_margin;           /* Right margin in inch/(72 * XR_POINT). */
146     int top_margin;             /* Top margin in inch/(72 * XR_POINT). */
147     int bottom_margin;          /* Bottom margin in inch/(72 * XR_POINT). */
148
149     int line_space;             /* Space between lines. */
150     int line_width;             /* Width of lines. */
151
152     int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
153
154     struct xr_color bg;    /* Background color */
155     struct xr_color fg;    /* Foreground color */
156
157     /* Internal state. */
158     struct render_params *params;
159     int char_width, char_height;
160     char *command_name;
161     char *title;
162     char *subtitle;
163     cairo_t *cairo;
164     int page_number;            /* Current page number. */
165     int x, y;
166     struct xr_render_fsm *fsm;
167     int nest;
168   };
169
170 static const struct output_driver_class cairo_driver_class;
171
172 static void xr_driver_destroy_fsm (struct xr_driver *);
173 static void xr_driver_run_fsm (struct xr_driver *);
174
175 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
176                           enum render_line_style styles[TABLE_N_AXES][2],
177                           struct cell_color colors[TABLE_N_AXES][2]);
178 static void xr_measure_cell_width (void *, const struct table_cell *,
179                                    int *min, int *max);
180 static int xr_measure_cell_height (void *, const struct table_cell *,
181                                    int width);
182 static void xr_draw_cell (void *, const struct table_cell *, int color_idx,
183                           int bb[TABLE_N_AXES][2],
184                           int spill[TABLE_N_AXES][2],
185                           int clip[TABLE_N_AXES][2]);
186 static int xr_adjust_break (void *, const struct table_cell *,
187                             int width, int height);
188
189 static struct xr_render_fsm *xr_render_output_item (
190   struct xr_driver *, const struct output_item *);
191 \f
192 /* Output driver basics. */
193
194 static struct xr_driver *
195 xr_driver_cast (struct output_driver *driver)
196 {
197   assert (driver->class == &cairo_driver_class);
198   return UP_CAST (driver, struct xr_driver, driver);
199 }
200
201 static struct driver_option *
202 opt (struct output_driver *d, struct string_map *options, const char *key,
203      const char *default_value)
204 {
205   return driver_option_get (d, options, key, default_value);
206 }
207
208 /* Parse color information specified by KEY into {RED,GREEN,BLUE}.
209    Currently, the input string must be of the form "#RRRRGGGGBBBB"
210    Future implementations might allow things like "yellow" and
211    "sky-blue-ultra-brown"
212 */
213 void
214 parse_color (struct output_driver *d, struct string_map *options,
215              const char *key, const char *default_value,
216              struct xr_color *color)
217 {
218   int red, green, blue;
219   char *string = parse_string (opt (d, options, key, default_value));
220
221   if (3 != sscanf (string, "#%04x%04x%04x", &red, &green, &blue))
222     {
223       /* If the parsed option string fails, then try the default value */
224       if ( 3 != sscanf (default_value, "#%04x%04x%04x", &red, &green, &blue))
225         {
226           /* ... and if that fails set everything to zero */
227           red = green = blue = 0;
228         }
229     }
230
231   free (string);
232
233   /* Convert 16 bit ints to float */
234   color->red = red / (double) 0xFFFF;
235   color->green = green / (double) 0xFFFF;
236   color->blue = blue / (double) 0xFFFF;
237 }
238
239 static PangoFontDescription *
240 parse_font (const char *font, int default_size, bool bold, bool italic)
241 {
242   if (!c_strcasecmp (font, "Monospaced"))
243     font = "Monospace";
244
245   PangoFontDescription *desc = pango_font_description_from_string (font);
246   if (desc == NULL)
247     return NULL;
248
249   /* If the font description didn't include an explicit font size, then set it
250      to DEFAULT_SIZE, which is in inch/72000 units. */
251   if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
252     pango_font_description_set_size (desc,
253                                      (default_size / 1000.0) * PANGO_SCALE);
254
255   pango_font_description_set_weight (desc, (bold
256                                             ? PANGO_WEIGHT_BOLD
257                                             : PANGO_WEIGHT_NORMAL));
258   pango_font_description_set_style (desc, (italic
259                                            ? PANGO_STYLE_ITALIC
260                                            : PANGO_STYLE_NORMAL));
261
262   return desc;
263 }
264
265 static PangoFontDescription *
266 parse_font_option (struct output_driver *d, struct string_map *options,
267                    const char *key, const char *default_value,
268                    int default_size, bool bold, bool italic)
269 {
270   char *string = parse_string (opt (d, options, key, default_value));
271   PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
272   if (!desc)
273     {
274       msg (MW, _("`%s': bad font specification"), string);
275
276       /* Fall back to DEFAULT_VALUE, which had better be a valid font
277          description. */
278       desc = parse_font (default_value, default_size, bold, italic);
279       assert (desc != NULL);
280     }
281   free (string);
282
283   return desc;
284 }
285
286 static void
287 apply_options (struct xr_driver *xr, struct string_map *o)
288 {
289   struct output_driver *d = &xr->driver;
290
291   /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
292   int left_margin, right_margin;
293   int top_margin, bottom_margin;
294   int paper_width, paper_length;
295   int font_size;
296   int min_break[TABLE_N_AXES];
297
298   /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
299   const double scale = XR_POINT / 1000.;
300
301   int i;
302
303   for (i = 0; i < XR_N_FONTS; i++)
304     {
305       struct xr_font *font = &xr->fonts[i];
306
307       if (font->desc != NULL)
308         pango_font_description_free (font->desc);
309     }
310
311   font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
312   xr->fonts[XR_FONT_FIXED].desc = parse_font_option
313     (d, o, "fixed-font", "monospace", font_size, false, false);
314   xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font_option (
315     d, o, "prop-font", "sans serif", font_size, false, false);
316   xr->fonts[XR_FONT_EMPHASIS].desc = parse_font_option (
317     d, o, "emph-font", "sans serif", font_size, false, true);
318
319   xr->page_number = 0;
320
321   parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg);
322   parse_color (d, o, "foreground-color", "#000000000000", &xr->fg);
323
324   /* Get dimensions.  */
325   parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
326   left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
327   right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
328   top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
329   bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
330
331   min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
332   min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
333
334   /* Convert to inch/(XR_POINT * 72). */
335   xr->left_margin = left_margin * scale;
336   xr->right_margin = right_margin * scale;
337   xr->top_margin = top_margin * scale;
338   xr->bottom_margin = bottom_margin * scale;
339   xr->width = (paper_width - left_margin - right_margin) * scale;
340   xr->length = (paper_length - top_margin - bottom_margin) * scale;
341   xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
342   xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
343 }
344
345 static struct xr_driver *
346 xr_allocate (const char *name, int device_type, struct string_map *o)
347 {
348   struct xr_driver *xr = xzalloc (sizeof *xr);
349   struct output_driver *d = &xr->driver;
350
351   output_driver_init (d, &cairo_driver_class, name, device_type);
352
353   apply_options (xr, o);
354
355   return xr;
356 }
357
358 static int
359 pango_to_xr (int pango)
360 {
361   return (XR_POINT != PANGO_SCALE
362           ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
363           : pango);
364 }
365
366 static int
367 xr_to_pango (int xr)
368 {
369   return (XR_POINT != PANGO_SCALE
370           ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
371           : xr);
372 }
373
374 static bool
375 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
376 {
377   int i;
378
379   xr->cairo = cairo;
380
381   cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
382
383   xr->char_width = 0;
384   xr->char_height = 0;
385   for (i = 0; i < XR_N_FONTS; i++)
386     {
387       struct xr_font *font = &xr->fonts[i];
388       int char_width, char_height;
389
390       font->layout = pango_cairo_create_layout (cairo);
391       pango_layout_set_font_description (font->layout, font->desc);
392
393       pango_layout_set_text (font->layout, "0", 1);
394       pango_layout_get_size (font->layout, &char_width, &char_height);
395       xr->char_width = MAX (xr->char_width, pango_to_xr (char_width));
396       xr->char_height = MAX (xr->char_height, pango_to_xr (char_height));
397     }
398
399   if (xr->params == NULL)
400     {
401       xr->params = xmalloc (sizeof *xr->params);
402       xr->params->draw_line = xr_draw_line;
403       xr->params->measure_cell_width = xr_measure_cell_width;
404       xr->params->measure_cell_height = xr_measure_cell_height;
405       xr->params->adjust_break = xr_adjust_break;
406       xr->params->draw_cell = xr_draw_cell;
407       xr->params->aux = xr;
408       xr->params->size[H] = xr->width;
409       xr->params->size[V] = xr->length;
410       xr->params->font_size[H] = xr->char_width;
411       xr->params->font_size[V] = xr->char_height;
412
413       int lw = XR_LINE_WIDTH;
414       int ls = XR_LINE_SPACE;
415       for (i = 0; i < TABLE_N_AXES; i++)
416         {
417           xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
418           xr->params->line_widths[i][RENDER_LINE_SINGLE] = lw;
419           xr->params->line_widths[i][RENDER_LINE_DASHED] = lw;
420           xr->params->line_widths[i][RENDER_LINE_THICK] = lw * 2;
421           xr->params->line_widths[i][RENDER_LINE_THIN] = lw / 2;
422           xr->params->line_widths[i][RENDER_LINE_DOUBLE] = 2 * lw + ls;
423         }
424
425       for (i = 0; i < TABLE_N_AXES; i++)
426         xr->params->min_break[i] = xr->min_break[i];
427       xr->params->supports_margins = true;
428       xr->params->rtl = render_direction_rtl ();
429     }
430
431   cairo_set_source_rgb (xr->cairo, xr->fg.red, xr->fg.green, xr->fg.blue);
432
433   return true;
434 }
435
436 static struct output_driver *
437 xr_create (const char *file_name, enum settings_output_devices device_type,
438            struct string_map *o, enum xr_output_type file_type)
439 {
440   enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
441   struct xr_driver *xr;
442   cairo_surface_t *surface;
443   cairo_status_t status;
444   double width_pt, length_pt;
445
446   xr = xr_allocate (file_name, device_type, o);
447
448   width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
449   length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
450   if (file_type == XR_PDF)
451     surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
452   else if (file_type == XR_PS)
453     surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
454   else if (file_type == XR_SVG)
455     surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
456   else
457     NOT_REACHED ();
458
459   status = cairo_surface_status (surface);
460   if (status != CAIRO_STATUS_SUCCESS)
461     {
462       msg (ME, _("error opening output file `%s': %s"),
463              file_name, cairo_status_to_string (status));
464       cairo_surface_destroy (surface);
465       goto error;
466     }
467
468   xr->cairo = cairo_create (surface);
469   cairo_surface_destroy (surface);
470
471   if (!xr_set_cairo (xr, xr->cairo))
472     goto error;
473
474   cairo_save (xr->cairo);
475   xr_driver_next_page (xr, xr->cairo);
476
477   if (xr->width / xr->char_width < MIN_WIDTH)
478     {
479       msg (ME, _("The defined page is not wide enough to hold at least %d "
480                      "characters in the default font.  In fact, there's only "
481                      "room for %d characters."),
482              MIN_WIDTH,
483              xr->width / xr->char_width);
484       goto error;
485     }
486
487   if (xr->length / xr->char_height < MIN_LENGTH)
488     {
489       msg (ME, _("The defined page is not long enough to hold at least %d "
490                      "lines in the default font.  In fact, there's only "
491                      "room for %d lines."),
492              MIN_LENGTH,
493              xr->length / xr->char_height);
494       goto error;
495     }
496
497   return &xr->driver;
498
499  error:
500   output_driver_destroy (&xr->driver);
501   return NULL;
502 }
503
504 static struct output_driver *
505 xr_pdf_create (struct  file_handle *fh, enum settings_output_devices device_type,
506                struct string_map *o)
507 {
508   struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_PDF);
509   fh_unref (fh);
510   return od ;
511 }
512
513 static struct output_driver *
514 xr_ps_create (struct  file_handle *fh, enum settings_output_devices device_type,
515                struct string_map *o)
516 {
517   struct output_driver *od =  xr_create (fh_get_file_name (fh), device_type, o, XR_PS);
518   fh_unref (fh);
519   return od ;
520 }
521
522 static struct output_driver *
523 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
524                struct string_map *o)
525 {
526   struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_SVG);
527   fh_unref (fh);
528   return od ;
529 }
530
531 static void
532 xr_destroy (struct output_driver *driver)
533 {
534   struct xr_driver *xr = xr_driver_cast (driver);
535   size_t i;
536
537   xr_driver_destroy_fsm (xr);
538
539   if (xr->cairo != NULL)
540     {
541       cairo_status_t status;
542
543       cairo_surface_finish (cairo_get_target (xr->cairo));
544       status = cairo_status (xr->cairo);
545       if (status != CAIRO_STATUS_SUCCESS)
546         msg (ME, _("error drawing output for %s driver: %s"),
547                output_driver_get_name (driver),
548                cairo_status_to_string (status));
549       cairo_destroy (xr->cairo);
550     }
551
552   for (i = 0; i < XR_N_FONTS; i++)
553     {
554       struct xr_font *font = &xr->fonts[i];
555
556       if (font->desc != NULL)
557         pango_font_description_free (font->desc);
558       if (font->layout != NULL)
559         g_object_unref (font->layout);
560     }
561
562   free (xr->params);
563   free (xr);
564 }
565
566 static void
567 xr_flush (struct output_driver *driver)
568 {
569   struct xr_driver *xr = xr_driver_cast (driver);
570
571   cairo_surface_flush (cairo_get_target (xr->cairo));
572 }
573
574 static void
575 xr_submit (struct output_driver *driver, const struct output_item *output_item)
576 {
577   struct xr_driver *xr = xr_driver_cast (driver);
578
579   xr_driver_output_item (xr, output_item);
580   while (xr_driver_need_new_page (xr))
581     {
582       cairo_restore (xr->cairo);
583       cairo_show_page (xr->cairo);
584       cairo_save (xr->cairo);
585       xr_driver_next_page (xr, xr->cairo);
586     }
587 }
588 \f
589 /* Functions for rendering a series of output items to a series of Cairo
590    contexts, with pagination.
591
592    Used by PSPPIRE for printing, and by the basic Cairo output driver above as
593    its underlying implementation.
594
595    See the big comment in cairo.h for intended usage. */
596
597 /* Gives new page CAIRO to XR for output. */
598 void
599 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
600 {
601   cairo_save (cairo);
602   cairo_set_source_rgb (cairo, xr->bg.red, xr->bg.green, xr->bg.blue);
603   cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
604   cairo_fill (cairo);
605   cairo_restore (cairo);
606
607   cairo_translate (cairo,
608                    xr_to_pt (xr->left_margin),
609                    xr_to_pt (xr->top_margin));
610
611   xr->page_number++;
612   xr->cairo = cairo;
613   xr->x = xr->y = 0;
614   xr_driver_run_fsm (xr);
615 }
616
617 /* Start rendering OUTPUT_ITEM to XR.  Only valid if XR is not in the middle of
618    rendering a previous output item, that is, only if xr_driver_need_new_page()
619    returns false. */
620 void
621 xr_driver_output_item (struct xr_driver *xr,
622                        const struct output_item *output_item)
623 {
624   assert (xr->fsm == NULL);
625   xr->fsm = xr_render_output_item (xr, output_item);
626   xr_driver_run_fsm (xr);
627 }
628
629 /* Returns true if XR is in the middle of rendering an output item and needs a
630    new page to be appended using xr_driver_next_page() to make progress,
631    otherwise false. */
632 bool
633 xr_driver_need_new_page (const struct xr_driver *xr)
634 {
635   return xr->fsm != NULL;
636 }
637
638 /* Returns true if the current page doesn't have any content yet. */
639 bool
640 xr_driver_is_page_blank (const struct xr_driver *xr)
641 {
642   return xr->y == 0;
643 }
644
645 static void
646 xr_driver_destroy_fsm (struct xr_driver *xr)
647 {
648   if (xr->fsm != NULL)
649     {
650       xr->fsm->destroy (xr->fsm);
651       xr->fsm = NULL;
652     }
653 }
654
655 static void
656 xr_driver_run_fsm (struct xr_driver *xr)
657 {
658   if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
659     xr_driver_destroy_fsm (xr);
660 }
661 \f
662 static void
663 xr_layout_cell (struct xr_driver *, const struct table_cell *,
664                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
665                 int *width, int *height, int *brk);
666
667 static void
668 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
669            const struct cell_color *color)
670 {
671   cairo_new_path (xr->cairo);
672   cairo_set_source_rgb (xr->cairo,
673                         color->r / 255.0, color->g / 255.0, color->b / 255.0);
674   cairo_set_line_width (
675     xr->cairo,
676     xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
677               : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
678               : XR_LINE_WIDTH));
679   cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
680   cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
681   cairo_stroke (xr->cairo);
682 }
683
684 static void UNUSED
685 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
686 {
687   cairo_new_path (xr->cairo);
688   cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
689   cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
690   cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y0 + xr->y));
691   cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
692   cairo_line_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y1 + xr->y));
693   cairo_close_path (xr->cairo);
694   cairo_stroke (xr->cairo);
695 }
696
697 static void
698 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
699 {
700   cairo_new_path (xr->cairo);
701   cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
702   cairo_rectangle (xr->cairo,
703                    xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y),
704                    xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
705   cairo_fill (xr->cairo);
706 }
707
708 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
709    shortening it to X0...X1 if SHORTEN is true.
710    Draws a horizontal line X1...X3 at Y if RIGHT says so,
711    shortening it to X2...X3 if SHORTEN is true. */
712 static void
713 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
714            enum render_line_style left, enum render_line_style right,
715            const struct cell_color *left_color,
716            const struct cell_color *right_color,
717            bool shorten)
718 {
719   if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
720       && cell_color_equal (left_color, right_color))
721     dump_line (xr, x0, y, x3, y, left, left_color);
722   else
723     {
724       if (left != RENDER_LINE_NONE)
725         dump_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
726       if (right != RENDER_LINE_NONE)
727         dump_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
728     }
729 }
730
731 /* Draws a vertical line Y0...Y2 at X if TOP says so,
732    shortening it to Y0...Y1 if SHORTEN is true.
733    Draws a vertical line Y1...Y3 at X if BOTTOM says so,
734    shortening it to Y2...Y3 if SHORTEN is true. */
735 static void
736 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
737            enum render_line_style top, enum render_line_style bottom,
738            const struct cell_color *top_color,
739            const struct cell_color *bottom_color,
740            bool shorten)
741 {
742   if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
743       && cell_color_equal (top_color, bottom_color))
744     dump_line (xr, x, y0, x, y3, top, top_color);
745   else
746     {
747       if (top != RENDER_LINE_NONE)
748         dump_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
749       if (bottom != RENDER_LINE_NONE)
750         dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
751     }
752 }
753
754 static void
755 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
756               enum render_line_style styles[TABLE_N_AXES][2],
757               struct cell_color colors[TABLE_N_AXES][2])
758 {
759   const int x0 = bb[H][0];
760   const int y0 = bb[V][0];
761   const int x3 = bb[H][1];
762   const int y3 = bb[V][1];
763   const int top = styles[H][0];
764   const int bottom = styles[H][1];
765
766   int start_side = render_direction_rtl();
767   int end_side = !start_side;
768   const int start_of_line = styles[V][start_side];
769   const int end_of_line   = styles[V][end_side];
770   const struct cell_color *top_color = &colors[H][0];
771   const struct cell_color *bottom_color = &colors[H][1];
772   const struct cell_color *start_color = &colors[V][start_side];
773   const struct cell_color *end_color = &colors[V][end_side];
774
775   /* The algorithm here is somewhat subtle, to allow it to handle
776      all the kinds of intersections that we need.
777
778      Three additional ordinates are assigned along the x axis.  The
779      first is xc, midway between x0 and x3.  The others are x1 and
780      x2; for a single vertical line these are equal to xc, and for
781      a double vertical line they are the ordinates of the left and
782      right half of the double line.
783
784      yc, y1, and y2 are assigned similarly along the y axis.
785
786      The following diagram shows the coordinate system and output
787      for double top and bottom lines, single left line, and no
788      right line:
789
790                  x0       x1 xc  x2      x3
791                y0 ________________________
792                   |        #     #       |
793                   |        #     #       |
794                   |        #     #       |
795                   |        #     #       |
796                   |        #     #       |
797      y1 = y2 = yc |#########     #       |
798                   |        #     #       |
799                   |        #     #       |
800                   |        #     #       |
801                   |        #     #       |
802                y3 |________#_____#_______|
803   */
804   struct xr_driver *xr = xr_;
805
806   /* Offset from center of each line in a pair of double lines. */
807   int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
808
809   /* Are the lines along each axis single or double?
810      (It doesn't make sense to have different kinds of line on the
811      same axis, so we don't try to gracefully handle that case.) */
812   bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
813   bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
814
815   /* When horizontal lines are doubled,
816      the left-side line along y1 normally runs from x0 to x2,
817      and the right-side line along y1 from x3 to x1.
818      If the top-side line is also doubled, we shorten the y1 lines,
819      so that the left-side line runs only to x1,
820      and the right-side line only to x2.
821      Otherwise, the horizontal line at y = y1 below would cut off
822      the intersection, which looks ugly:
823                x0       x1     x2      x3
824              y0 ________________________
825                 |        #     #       |
826                 |        #     #       |
827                 |        #     #       |
828                 |        #     #       |
829              y1 |#########     ########|
830                 |                      |
831                 |                      |
832              y2 |######################|
833                 |                      |
834                 |                      |
835              y3 |______________________|
836      It is more of a judgment call when the horizontal line is
837      single.  We actually choose to cut off the line anyhow, as
838      shown in the first diagram above.
839   */
840   bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
841   bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
842   bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
843   int horz_line_ofs = double_vert ? double_line_ofs : 0;
844   int xc = (x0 + x3) / 2;
845   int x1 = xc - horz_line_ofs;
846   int x2 = xc + horz_line_ofs;
847
848   bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
849   bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
850   bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
851   int vert_line_ofs = double_horz ? double_line_ofs : 0;
852   int yc = (y0 + y3) / 2;
853   int y1 = yc - vert_line_ofs;
854   int y2 = yc + vert_line_ofs;
855
856   if (!double_horz)
857     horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
858                start_color, end_color, shorten_yc_line);
859   else
860     {
861       horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
862                  start_color, end_color, shorten_y1_lines);
863       horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
864                  start_color, end_color, shorten_y2_lines);
865     }
866
867   if (!double_vert)
868     vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
869                shorten_xc_line);
870   else
871     {
872       vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
873                  shorten_x1_lines);
874       vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
875                  shorten_x2_lines);
876     }
877 }
878
879 static void
880 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
881                        int *min_width, int *max_width)
882 {
883   struct xr_driver *xr = xr_;
884   int bb[TABLE_N_AXES][2];
885   int clip[TABLE_N_AXES][2];
886   int h;
887
888   bb[H][0] = 0;
889   bb[H][1] = INT_MAX;
890   bb[V][0] = 0;
891   bb[V][1] = INT_MAX;
892   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
893   xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
894
895   bb[H][1] = 1;
896   xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
897
898   if (*min_width > 0)
899     *min_width += px_to_xr (cell->style->margin[H][0]
900                             + cell->style->margin[H][1]);
901   if (*max_width > 0)
902     *max_width += px_to_xr (cell->style->margin[H][0]
903                             + cell->style->margin[H][1]);
904 }
905
906 static int
907 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
908 {
909   struct xr_driver *xr = xr_;
910   int bb[TABLE_N_AXES][2];
911   int clip[TABLE_N_AXES][2];
912   int w, h;
913
914   bb[H][0] = 0;
915   bb[H][1] = width - px_to_xr (cell->style->margin[H][0]
916                                + cell->style->margin[H][1]);
917   bb[V][0] = 0;
918   bb[V][1] = INT_MAX;
919   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
920   xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
921   h += px_to_xr (cell->style->margin[V][0] + cell->style->margin[V][1]);
922   return h;
923 }
924
925 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
926
927 static void
928 xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
929               int bb[TABLE_N_AXES][2],
930               int spill[TABLE_N_AXES][2],
931               int clip[TABLE_N_AXES][2])
932 {
933   struct xr_driver *xr = xr_;
934   int w, h, brk;
935
936   cairo_save (xr->cairo);
937   int bg_clip[TABLE_N_AXES][2];
938   for (int axis = 0; axis < TABLE_N_AXES; axis++)
939     {
940       bg_clip[axis][0] = clip[axis][0];
941       if (bb[axis][0] == clip[axis][0])
942         bg_clip[axis][0] -= spill[axis][0];
943
944       bg_clip[axis][1] = clip[axis][1];
945       if (bb[axis][1] == clip[axis][1])
946         bg_clip[axis][1] += spill[axis][1];
947     }
948   xr_clip (xr, bg_clip);
949   cairo_set_source_rgb (xr->cairo,
950                         cell->style->bg[color_idx].r / 255.,
951                         cell->style->bg[color_idx].g / 255.,
952                         cell->style->bg[color_idx].b / 255.);
953   fill_rectangle (xr,
954                   bb[H][0] - spill[H][0],
955                   bb[V][0] - spill[V][0],
956                   bb[H][1] + spill[H][1],
957                   bb[V][1] + spill[V][1]);
958   cairo_restore (xr->cairo);
959
960   cairo_save (xr->cairo);
961   cairo_set_source_rgb (xr->cairo,
962                         cell->style->fg[color_idx].r / 255.,
963                         cell->style->fg[color_idx].g / 255.,
964                         cell->style->fg[color_idx].b / 255.);
965
966   for (int axis = 0; axis < TABLE_N_AXES; axis++)
967     {
968       bb[axis][0] += px_to_xr (cell->style->margin[axis][0]);
969       bb[axis][1] -= px_to_xr (cell->style->margin[axis][1]);
970     }
971   if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
972     xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
973   cairo_restore (xr->cairo);
974 }
975
976 static int
977 xr_adjust_break (void *xr_, const struct table_cell *cell,
978                  int width, int height)
979 {
980   struct xr_driver *xr = xr_;
981   int bb[TABLE_N_AXES][2];
982   int clip[TABLE_N_AXES][2];
983   int w, h, brk;
984
985   if (xr_measure_cell_height (xr_, cell, width) < height)
986     return -1;
987
988   bb[H][0] = 0;
989   bb[H][1] = width - px_to_xr (cell->style->margin[H][0]
990                                + cell->style->margin[H][1]);
991   if (bb[H][1] <= 0)
992     return 0;
993   bb[V][0] = 0;
994   bb[V][1] = height - px_to_xr (cell->style->margin[V][0]
995                                 + cell->style->margin[V][1]);
996   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
997   xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
998   return brk;
999 }
1000 \f
1001 static void
1002 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
1003 {
1004   if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1005     {
1006       double x0 = xr_to_pt (clip[H][0] + xr->x);
1007       double y0 = xr_to_pt (clip[V][0] + xr->y);
1008       double x1 = xr_to_pt (clip[H][1] + xr->x);
1009       double y1 = xr_to_pt (clip[V][1] + xr->y);
1010
1011       cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1012       cairo_clip (xr->cairo);
1013     }
1014 }
1015
1016 static void
1017 add_attr_with_start (PangoAttrList *list, PangoAttribute *attr, guint start_index)
1018 {
1019   attr->start_index = start_index;
1020   pango_attr_list_insert (list, attr);
1021 }
1022
1023 static int
1024 xr_layout_cell_text (struct xr_driver *xr,
1025                      const struct cell_contents *contents,
1026                      const struct cell_style *style,
1027                      int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1028                      int *widthp, int *brk)
1029 {
1030   unsigned int options = contents->options;
1031   int w, h;
1032
1033   struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1034                           : options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
1035                           : &xr->fonts[XR_FONT_PROPORTIONAL]);
1036   struct xr_font local_font;
1037   if (style->font)
1038     {
1039       PangoFontDescription *desc = parse_font (
1040         style->font,
1041         style->font_size ? style->font_size * 1000 * 72 / 128 : 10000,
1042         style->bold, style->italic);
1043       if (desc)
1044         {
1045           PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1046           pango_layout_set_font_description (layout, desc);
1047
1048           local_font.desc = desc;
1049           local_font.layout = layout;
1050           font = &local_font;
1051         }
1052     }
1053
1054   int footnote_adjustment;
1055   if (contents->n_footnotes == 0)
1056     footnote_adjustment = 0;
1057   else if (contents->n_footnotes == 1 && (options & TAB_HALIGN) == TAB_RIGHT)
1058     {
1059       PangoAttrList *attrs;
1060
1061       const char *marker = contents->footnotes[0]->marker;
1062       pango_layout_set_text (font->layout, marker, strlen (marker));
1063
1064       attrs = pango_attr_list_new ();
1065       pango_attr_list_insert (attrs, pango_attr_rise_new (7000));
1066       pango_layout_set_attributes (font->layout, attrs);
1067       pango_attr_list_unref (attrs);
1068
1069       pango_layout_get_size (font->layout, &w, &h);
1070       footnote_adjustment = MIN (w, px_to_xr (style->margin[H][1]));
1071     }
1072   else
1073     footnote_adjustment = px_to_xr (style->margin[H][1]);
1074
1075   struct string tmp = DS_EMPTY_INITIALIZER;
1076   const char *text = contents->text;
1077
1078   /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
1079      Pango's implementation of it): it will break after a period or a comma
1080      that precedes a digit, e.g. in ".000" it will break after the period.
1081      This code looks for such a situation and inserts a U+2060 WORD JOINER
1082      to prevent the break.
1083
1084      This isn't necessary when the decimal point is between two digits
1085      (e.g. "0.000" won't be broken) or when the display width is not limited so
1086      that word wrapping won't happen.
1087
1088      It isn't necessary to look for more than one period or comma, as would
1089      happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
1090      are present then there will always be a digit on both sides of every
1091      period and comma. */
1092   if (bb[H][1] != INT_MAX)
1093     {
1094       const char *decimal = text + strcspn (text, ".,");
1095       if (decimal[0]
1096           && c_isdigit (decimal[1])
1097           && (decimal == text || !c_isdigit (decimal[-1])))
1098         {
1099           ds_extend (&tmp, strlen (text) + 16);
1100           ds_put_substring (&tmp, ss_buffer (text, decimal - text + 1));
1101           ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
1102           ds_put_cstr (&tmp, decimal + 1);
1103         }
1104     }
1105
1106   if (footnote_adjustment)
1107     {
1108       bb[H][1] += footnote_adjustment;
1109
1110       if (ds_is_empty (&tmp))
1111         {
1112           ds_extend (&tmp, strlen (text) + 16);
1113           ds_put_cstr (&tmp, text);
1114         }
1115       size_t initial_length = ds_length (&tmp);
1116
1117       cell_contents_format_footnote_markers (contents, &tmp);
1118       pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
1119
1120       PangoAttrList *attrs = pango_attr_list_new ();
1121       if (style->underline)
1122         pango_attr_list_insert (attrs, pango_attr_underline_new (
1123                                PANGO_UNDERLINE_SINGLE));
1124       add_attr_with_start (attrs, pango_attr_rise_new (7000), initial_length);
1125       add_attr_with_start (
1126         attrs, pango_attr_font_desc_new (font->desc), initial_length);
1127       pango_layout_set_attributes (font->layout, attrs);
1128       pango_attr_list_unref (attrs);
1129     }
1130   else
1131     {
1132       const char *content = ds_is_empty (&tmp) ? text : ds_cstr (&tmp);
1133       pango_layout_set_text (font->layout, content, -1);
1134
1135       if (style->underline)
1136         {
1137           PangoAttrList *attrs = pango_attr_list_new ();
1138           pango_attr_list_insert (attrs, pango_attr_underline_new (
1139                                     PANGO_UNDERLINE_SINGLE));
1140           pango_layout_set_attributes (font->layout, attrs);
1141           pango_attr_list_unref (attrs);
1142         }
1143     }
1144   ds_destroy (&tmp);
1145
1146   pango_layout_set_alignment (
1147     font->layout,
1148     ((options & TAB_HALIGN) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
1149      : (options & TAB_HALIGN) == TAB_LEFT ? PANGO_ALIGN_LEFT
1150      : PANGO_ALIGN_CENTER));
1151   pango_layout_set_width (
1152     font->layout,
1153     bb[H][1] == INT_MAX ? -1 : xr_to_pango (bb[H][1] - bb[H][0]));
1154   pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1155
1156   if (clip[H][0] != clip[H][1])
1157     {
1158       cairo_save (xr->cairo);
1159       xr_clip (xr, clip);
1160       cairo_translate (xr->cairo,
1161                        xr_to_pt (bb[H][0] + xr->x),
1162                        xr_to_pt (bb[V][0] + xr->y));
1163       pango_cairo_show_layout (xr->cairo, font->layout);
1164
1165       /* If enabled, this draws a blue rectangle around the extents of each
1166          line of text, which can be rather useful for debugging layout
1167          issues. */
1168       if (0)
1169         {
1170           PangoLayoutIter *iter;
1171           iter = pango_layout_get_iter (font->layout);
1172           do
1173             {
1174               PangoRectangle extents;
1175
1176               pango_layout_iter_get_line_extents (iter, &extents, NULL);
1177               cairo_save (xr->cairo);
1178               cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1179               dump_rectangle (xr,
1180                               pango_to_xr (extents.x) - xr->x,
1181                               pango_to_xr (extents.y) - xr->y,
1182                               pango_to_xr (extents.x + extents.width) - xr->x,
1183                               pango_to_xr (extents.y + extents.height) - xr->y);
1184               cairo_restore (xr->cairo);
1185             }
1186           while (pango_layout_iter_next_line (iter));
1187           pango_layout_iter_free (iter);
1188         }
1189
1190       cairo_restore (xr->cairo);
1191     }
1192
1193   pango_layout_get_size (font->layout, &w, &h);
1194   w = pango_to_xr (w);
1195   h = pango_to_xr (h);
1196   if (w > *widthp)
1197     *widthp = w;
1198   if (bb[V][0] + h >= bb[V][1])
1199     {
1200       PangoLayoutIter *iter;
1201       int best UNUSED = 0;
1202
1203       /* Choose a breakpoint between lines instead of in the middle of one. */
1204       iter = pango_layout_get_iter (font->layout);
1205       do
1206         {
1207           PangoRectangle extents;
1208           int y0, y1;
1209           int bottom;
1210
1211           pango_layout_iter_get_line_extents (iter, NULL, &extents);
1212           pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1213           extents.x = pango_to_xr (extents.x);
1214           extents.y = pango_to_xr (y0);
1215           extents.width = pango_to_xr (extents.width);
1216           extents.height = pango_to_xr (y1 - y0);
1217           bottom = bb[V][0] + extents.y + extents.height;
1218           if (bottom < bb[V][1])
1219             {
1220               if (brk && clip[H][0] != clip[H][1])
1221                 best = bottom;
1222               if (brk)
1223                 *brk = bottom;
1224             }
1225           else
1226             break;
1227         }
1228       while (pango_layout_iter_next_line (iter));
1229       pango_layout_iter_free (iter);
1230
1231       /* If enabled, draws a green line across the chosen breakpoint, which can
1232          be useful for debugging issues with breaking.  */
1233       if (0)
1234         {
1235           if (best && !xr->nest)
1236             dump_line (xr, -xr->left_margin, best,
1237                        xr->width + xr->right_margin, best,
1238                        RENDER_LINE_SINGLE, &CELL_COLOR (0, 255, 0));
1239         }
1240     }
1241
1242   pango_layout_set_attributes (font->layout, NULL);
1243
1244   if (font == &local_font)
1245     {
1246       g_object_unref (G_OBJECT (font->layout));
1247       pango_font_description_free (font->desc);
1248     }
1249
1250   return bb[V][0] + h;
1251 }
1252
1253 static void
1254 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1255                 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1256                 int *width, int *height, int *brk)
1257 {
1258   int bb[TABLE_N_AXES][2];
1259   size_t i;
1260
1261   *width = 0;
1262   *height = 0;
1263   if (brk)
1264     *brk = 0;
1265
1266   memcpy (bb, bb_, sizeof bb);
1267
1268   /* If enabled, draws a blue rectangle around the cell extents, which can be
1269      useful for debugging layout. */
1270   if (0)
1271     {
1272       if (clip[H][0] != clip[H][1])
1273         {
1274           int offset = (xr->nest) * XR_POINT;
1275
1276           cairo_save (xr->cairo);
1277           cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1278           dump_rectangle (xr,
1279                           bb[H][0] + offset, bb[V][0] + offset,
1280                           bb[H][1] - offset, bb[V][1] - offset);
1281           cairo_restore (xr->cairo);
1282         }
1283     }
1284
1285   for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
1286     {
1287       const struct cell_contents *contents = &cell->contents[i];
1288
1289       if (brk)
1290         *brk = bb[V][0];
1291       if (i > 0)
1292         {
1293           bb[V][0] += xr->char_height / 2;
1294           if (bb[V][0] >= bb[V][1])
1295             break;
1296           if (brk)
1297             *brk = bb[V][0];
1298         }
1299
1300       bb[V][0] = xr_layout_cell_text (xr, contents, cell->style, bb, clip,
1301                                       width, brk);
1302     }
1303   *height = bb[V][0] - bb_[V][0];
1304 }
1305 \f
1306 struct output_driver_factory pdf_driver_factory =
1307   { "pdf", "pspp.pdf", xr_pdf_create };
1308 struct output_driver_factory ps_driver_factory =
1309   { "ps", "pspp.ps", xr_ps_create };
1310 struct output_driver_factory svg_driver_factory =
1311   { "svg", "pspp.svg", xr_svg_create };
1312
1313 static const struct output_driver_class cairo_driver_class =
1314 {
1315   "cairo",
1316   xr_destroy,
1317   xr_submit,
1318   xr_flush,
1319 };
1320 \f
1321 /* GUI rendering helpers. */
1322
1323 struct xr_rendering
1324   {
1325     struct output_item *item;
1326
1327     /* Table items. */
1328     struct render_pager *p;
1329     struct xr_driver *xr;
1330   };
1331
1332 #define CHART_WIDTH 500
1333 #define CHART_HEIGHT 375
1334
1335
1336
1337 struct xr_driver *
1338 xr_driver_create (cairo_t *cairo, struct string_map *options)
1339 {
1340   struct xr_driver *xr = xr_allocate ("cairo", 0, options);
1341   if (!xr_set_cairo (xr, cairo))
1342     {
1343       output_driver_destroy (&xr->driver);
1344       return NULL;
1345     }
1346   return xr;
1347 }
1348
1349 /* Destroy XR, which should have been created with xr_driver_create().  Any
1350    cairo_t added to XR is not destroyed, because it is owned by the client. */
1351 void
1352 xr_driver_destroy (struct xr_driver *xr)
1353 {
1354   if (xr != NULL)
1355     {
1356       xr->cairo = NULL;
1357       output_driver_destroy (&xr->driver);
1358     }
1359 }
1360
1361 static struct xr_rendering *
1362 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1363 {
1364   struct table_item *table_item;
1365   struct xr_rendering *r;
1366
1367   table_item = table_item_create (table_from_string (TAB_LEFT, text),
1368                                   NULL, NULL);
1369   r = xr_rendering_create (xr, &table_item->output_item, cr);
1370   table_item_unref (table_item);
1371
1372   return r;
1373 }
1374
1375 void
1376 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1377 {
1378   if (is_table_item (xr->item))
1379     apply_options (xr->xr, o);
1380 }
1381
1382 struct xr_rendering *
1383 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1384                      cairo_t *cr)
1385 {
1386   struct xr_rendering *r = NULL;
1387
1388   if (is_text_item (item))
1389     r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1390                                   cr);
1391   else if (is_message_item (item))
1392     {
1393       const struct message_item *message_item = to_message_item (item);
1394       char *s = msg_to_string (message_item_get_msg (message_item));
1395       r = xr_rendering_create_text (xr, s, cr);
1396       free (s);
1397     }
1398   else if (is_table_item (item))
1399     {
1400       r = xzalloc (sizeof *r);
1401       r->item = output_item_ref (item);
1402       r->xr = xr;
1403       xr_set_cairo (xr, cr);
1404       r->p = render_pager_create (xr->params, to_table_item (item));
1405     }
1406   else if (is_chart_item (item))
1407     {
1408       r = xzalloc (sizeof *r);
1409       r->item = output_item_ref (item);
1410     }
1411   else if (is_group_open_item (item))
1412     r = xr_rendering_create_text (xr, to_group_open_item (item)->command_name,
1413                                   cr);
1414
1415   return r;
1416 }
1417
1418 void
1419 xr_rendering_destroy (struct xr_rendering *r)
1420 {
1421   if (r)
1422     {
1423       output_item_unref (r->item);
1424       render_pager_destroy (r->p);
1425       free (r);
1426     }
1427 }
1428
1429 void
1430 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
1431 {
1432   if (is_table_item (r->item))
1433     {
1434       *w = render_pager_get_size (r->p, H) / XR_POINT;
1435       *h = render_pager_get_size (r->p, V) / XR_POINT;
1436     }
1437   else
1438     {
1439       *w = CHART_WIDTH;
1440       *h = CHART_HEIGHT;
1441     }
1442 }
1443
1444 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1445                     double x, double y, double width, double height);
1446
1447 /* Draws onto CR */
1448 void
1449 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1450                    int x0, int y0, int x1, int y1)
1451 {
1452   if (is_table_item (r->item))
1453     {
1454       struct xr_driver *xr = r->xr;
1455
1456       xr_set_cairo (xr, cr);
1457
1458       render_pager_draw_region (r->p, x0 * XR_POINT, y0 * XR_POINT,
1459                                 (x1 - x0) * XR_POINT, (y1 - y0) * XR_POINT);
1460     }
1461   else
1462     xr_draw_chart (to_chart_item (r->item), cr,
1463                    0, 0, CHART_WIDTH, CHART_HEIGHT);
1464 }
1465
1466 static void
1467 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1468                double x, double y, double width, double height)
1469 {
1470   struct xrchart_geometry geom;
1471
1472   cairo_save (cr);
1473   cairo_translate (cr, x, y + height);
1474   cairo_scale (cr, 1.0, -1.0);
1475   xrchart_geometry_init (cr, &geom, width, height);
1476   if (is_boxplot (chart_item))
1477     xrchart_draw_boxplot (chart_item, cr, &geom);
1478   else if (is_histogram_chart (chart_item))
1479     xrchart_draw_histogram (chart_item, cr, &geom);
1480   else if (is_np_plot_chart (chart_item))
1481     xrchart_draw_np_plot (chart_item, cr, &geom);
1482   else if (is_piechart (chart_item))
1483     xrchart_draw_piechart (chart_item, cr, &geom);
1484   else if (is_barchart (chart_item))
1485     xrchart_draw_barchart (chart_item, cr, &geom);
1486   else if (is_roc_chart (chart_item))
1487     xrchart_draw_roc (chart_item, cr, &geom);
1488   else if (is_scree (chart_item))
1489     xrchart_draw_scree (chart_item, cr, &geom);
1490   else if (is_spreadlevel_plot_chart (chart_item))
1491     xrchart_draw_spreadlevel (chart_item, cr, &geom);
1492   else if (is_scatterplot_chart (chart_item))
1493     xrchart_draw_scatterplot (chart_item, cr, &geom);
1494   else
1495     NOT_REACHED ();
1496   xrchart_geometry_free (cr, &geom);
1497
1498   cairo_restore (cr);
1499 }
1500
1501 char *
1502 xr_draw_png_chart (const struct chart_item *item,
1503                    const char *file_name_template, int number,
1504                    const struct xr_color *fg,
1505                    const struct xr_color *bg
1506                    )
1507 {
1508   const int width = 640;
1509   const int length = 480;
1510
1511   cairo_surface_t *surface;
1512   cairo_status_t status;
1513   const char *number_pos;
1514   char *file_name;
1515   cairo_t *cr;
1516
1517   number_pos = strchr (file_name_template, '#');
1518   if (number_pos != NULL)
1519     file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1520                            file_name_template, number, number_pos + 1);
1521   else
1522     file_name = xstrdup (file_name_template);
1523
1524   surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1525   cr = cairo_create (surface);
1526
1527   cairo_set_source_rgb (cr, bg->red, bg->green, bg->blue);
1528   cairo_paint (cr);
1529
1530   cairo_set_source_rgb (cr, fg->red, fg->green, fg->blue);
1531
1532   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1533
1534   status = cairo_surface_write_to_png (surface, file_name);
1535   if (status != CAIRO_STATUS_SUCCESS)
1536     msg (ME, _("error writing output file `%s': %s"),
1537            file_name, cairo_status_to_string (status));
1538
1539   cairo_destroy (cr);
1540   cairo_surface_destroy (surface);
1541
1542   return file_name;
1543 }
1544 \f
1545 struct xr_table_state
1546   {
1547     struct xr_render_fsm fsm;
1548     struct table_item *table_item;
1549     struct render_pager *p;
1550   };
1551
1552 static bool
1553 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1554 {
1555   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1556
1557   while (render_pager_has_next (ts->p))
1558     {
1559       int used;
1560
1561       used = render_pager_draw_next (ts->p, xr->length - xr->y);
1562       if (!used)
1563         {
1564           assert (xr->y > 0);
1565           return true;
1566         }
1567       else
1568         xr->y += used;
1569     }
1570   return false;
1571 }
1572
1573 static void
1574 xr_table_destroy (struct xr_render_fsm *fsm)
1575 {
1576   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1577
1578   table_item_unref (ts->table_item);
1579   render_pager_destroy (ts->p);
1580   free (ts);
1581 }
1582
1583 static struct xr_render_fsm *
1584 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1585 {
1586   struct xr_table_state *ts;
1587
1588   ts = xmalloc (sizeof *ts);
1589   ts->fsm.render = xr_table_render;
1590   ts->fsm.destroy = xr_table_destroy;
1591   ts->table_item = table_item_ref (table_item);
1592
1593   if (xr->y > 0)
1594     xr->y += xr->char_height;
1595
1596   ts->p = render_pager_create (xr->params, table_item);
1597
1598   return &ts->fsm;
1599 }
1600 \f
1601 struct xr_chart_state
1602   {
1603     struct xr_render_fsm fsm;
1604     struct chart_item *chart_item;
1605   };
1606
1607 static bool
1608 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1609 {
1610   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1611
1612   const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
1613
1614   if (xr->y > xr->length - chart_height)
1615     return true;
1616
1617   if (xr->cairo != NULL)
1618     {
1619       xr_draw_chart (cs->chart_item, xr->cairo,
1620                      0.0,
1621                      xr_to_pt (xr->y),
1622                      xr_to_pt (xr->width),
1623                      xr_to_pt (chart_height));
1624     }
1625   xr->y += chart_height;
1626
1627   return false;
1628 }
1629
1630 static void
1631 xr_chart_destroy (struct xr_render_fsm *fsm)
1632 {
1633   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1634
1635   chart_item_unref (cs->chart_item);
1636   free (cs);
1637 }
1638
1639 static struct xr_render_fsm *
1640 xr_render_chart (const struct chart_item *chart_item)
1641 {
1642   struct xr_chart_state *cs;
1643
1644   cs = xmalloc (sizeof *cs);
1645   cs->fsm.render = xr_chart_render;
1646   cs->fsm.destroy = xr_chart_destroy;
1647   cs->chart_item = chart_item_ref (chart_item);
1648
1649   return &cs->fsm;
1650 }
1651 \f
1652 static bool
1653 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1654 {
1655   return xr->y > 0;
1656 }
1657
1658 static void
1659 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1660 {
1661   /* Nothing to do. */
1662 }
1663
1664 static struct xr_render_fsm *
1665 xr_render_eject (void)
1666 {
1667   static struct xr_render_fsm eject_renderer =
1668     {
1669       xr_eject_render,
1670       xr_eject_destroy
1671     };
1672
1673   return &eject_renderer;
1674 }
1675 \f
1676 static struct xr_render_fsm *
1677 xr_create_text_renderer (struct xr_driver *xr, const struct text_item *item)
1678 {
1679   struct tab_table *tab = tab_create (1, 1);
1680
1681   struct cell_style *style = pool_alloc (tab->container, sizeof *style);
1682   *style = (struct cell_style) CELL_STYLE_INITIALIZER;
1683   if (item->font)
1684     style->font = pool_strdup (tab->container, item->font);
1685   style->font_size = item->font_size;
1686   style->bold = item->bold;
1687   style->italic = item->italic;
1688   style->underline = item->underline;
1689   tab->styles[0] = style;
1690
1691   tab_text (tab, 0, 0, TAB_LEFT, text_item_get_text (item));
1692   struct table_item *table_item = table_item_create (&tab->table, NULL, NULL);
1693   struct xr_render_fsm *fsm = xr_render_table (xr, table_item);
1694   table_item_unref (table_item);
1695
1696   return fsm;
1697 }
1698
1699 static struct xr_render_fsm *
1700 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1701 {
1702   enum text_item_type type = text_item_get_type (text_item);
1703
1704   switch (type)
1705     {
1706     case TEXT_ITEM_PAGE_TITLE:
1707       break;
1708
1709     case TEXT_ITEM_BLANK_LINE:
1710       if (xr->y > 0)
1711         xr->y += xr->char_height;
1712       break;
1713
1714     case TEXT_ITEM_EJECT_PAGE:
1715       if (xr->y > 0)
1716         return xr_render_eject ();
1717       break;
1718
1719     default:
1720       return xr_create_text_renderer (xr, text_item);
1721     }
1722
1723   return NULL;
1724 }
1725
1726 static struct xr_render_fsm *
1727 xr_render_message (struct xr_driver *xr,
1728                    const struct message_item *message_item)
1729 {
1730   char *s = msg_to_string (message_item_get_msg (message_item));
1731   struct text_item *item = text_item_create (TEXT_ITEM_PARAGRAPH, s);
1732   free (s);
1733   struct xr_render_fsm *fsm = xr_create_text_renderer (xr, item);
1734   text_item_unref (item);
1735
1736   return fsm;
1737 }
1738
1739 static struct xr_render_fsm *
1740 xr_render_output_item (struct xr_driver *xr,
1741                        const struct output_item *output_item)
1742 {
1743   if (is_table_item (output_item))
1744     return xr_render_table (xr, to_table_item (output_item));
1745   else if (is_chart_item (output_item))
1746     return xr_render_chart (to_chart_item (output_item));
1747   else if (is_text_item (output_item))
1748     return xr_render_text (xr, to_text_item (output_item));
1749   else if (is_message_item (output_item))
1750     return xr_render_message (xr, to_message_item (output_item));
1751   else
1752     return NULL;
1753 }