61944624a7ee088f040349e591ada53edd4468ba
[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_all (struct xr_rendering *r, cairo_t *cr)
1450 {
1451   if (is_table_item (r->item))
1452     {
1453       struct xr_driver *xr = r->xr;
1454
1455       xr_set_cairo (xr, cr);
1456
1457       render_pager_draw (r->p);
1458
1459     }
1460   else
1461     xr_draw_chart (to_chart_item (r->item), cr,
1462                    0, 0, CHART_WIDTH, CHART_HEIGHT);
1463 }
1464
1465 static void
1466 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1467                double x, double y, double width, double height)
1468 {
1469   struct xrchart_geometry geom;
1470
1471   cairo_save (cr);
1472   cairo_translate (cr, x, y + height);
1473   cairo_scale (cr, 1.0, -1.0);
1474   xrchart_geometry_init (cr, &geom, width, height);
1475   if (is_boxplot (chart_item))
1476     xrchart_draw_boxplot (chart_item, cr, &geom);
1477   else if (is_histogram_chart (chart_item))
1478     xrchart_draw_histogram (chart_item, cr, &geom);
1479   else if (is_np_plot_chart (chart_item))
1480     xrchart_draw_np_plot (chart_item, cr, &geom);
1481   else if (is_piechart (chart_item))
1482     xrchart_draw_piechart (chart_item, cr, &geom);
1483   else if (is_barchart (chart_item))
1484     xrchart_draw_barchart (chart_item, cr, &geom);
1485   else if (is_roc_chart (chart_item))
1486     xrchart_draw_roc (chart_item, cr, &geom);
1487   else if (is_scree (chart_item))
1488     xrchart_draw_scree (chart_item, cr, &geom);
1489   else if (is_spreadlevel_plot_chart (chart_item))
1490     xrchart_draw_spreadlevel (chart_item, cr, &geom);
1491   else if (is_scatterplot_chart (chart_item))
1492     xrchart_draw_scatterplot (chart_item, cr, &geom);
1493   else
1494     NOT_REACHED ();
1495   xrchart_geometry_free (cr, &geom);
1496
1497   cairo_restore (cr);
1498 }
1499
1500 char *
1501 xr_draw_png_chart (const struct chart_item *item,
1502                    const char *file_name_template, int number,
1503                    const struct xr_color *fg,
1504                    const struct xr_color *bg
1505                    )
1506 {
1507   const int width = 640;
1508   const int length = 480;
1509
1510   cairo_surface_t *surface;
1511   cairo_status_t status;
1512   const char *number_pos;
1513   char *file_name;
1514   cairo_t *cr;
1515
1516   number_pos = strchr (file_name_template, '#');
1517   if (number_pos != NULL)
1518     file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1519                            file_name_template, number, number_pos + 1);
1520   else
1521     file_name = xstrdup (file_name_template);
1522
1523   surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1524   cr = cairo_create (surface);
1525
1526   cairo_set_source_rgb (cr, bg->red, bg->green, bg->blue);
1527   cairo_paint (cr);
1528
1529   cairo_set_source_rgb (cr, fg->red, fg->green, fg->blue);
1530
1531   xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1532
1533   status = cairo_surface_write_to_png (surface, file_name);
1534   if (status != CAIRO_STATUS_SUCCESS)
1535     msg (ME, _("error writing output file `%s': %s"),
1536            file_name, cairo_status_to_string (status));
1537
1538   cairo_destroy (cr);
1539   cairo_surface_destroy (surface);
1540
1541   return file_name;
1542 }
1543 \f
1544 struct xr_table_state
1545   {
1546     struct xr_render_fsm fsm;
1547     struct table_item *table_item;
1548     struct render_pager *p;
1549   };
1550
1551 static bool
1552 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1553 {
1554   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1555
1556   while (render_pager_has_next (ts->p))
1557     {
1558       int used;
1559
1560       used = render_pager_draw_next (ts->p, xr->length - xr->y);
1561       if (!used)
1562         {
1563           assert (xr->y > 0);
1564           return true;
1565         }
1566       else
1567         xr->y += used;
1568     }
1569   return false;
1570 }
1571
1572 static void
1573 xr_table_destroy (struct xr_render_fsm *fsm)
1574 {
1575   struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1576
1577   table_item_unref (ts->table_item);
1578   render_pager_destroy (ts->p);
1579   free (ts);
1580 }
1581
1582 static struct xr_render_fsm *
1583 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1584 {
1585   struct xr_table_state *ts;
1586
1587   ts = xmalloc (sizeof *ts);
1588   ts->fsm.render = xr_table_render;
1589   ts->fsm.destroy = xr_table_destroy;
1590   ts->table_item = table_item_ref (table_item);
1591
1592   if (xr->y > 0)
1593     xr->y += xr->char_height;
1594
1595   ts->p = render_pager_create (xr->params, table_item);
1596
1597   return &ts->fsm;
1598 }
1599 \f
1600 struct xr_chart_state
1601   {
1602     struct xr_render_fsm fsm;
1603     struct chart_item *chart_item;
1604   };
1605
1606 static bool
1607 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1608 {
1609   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1610
1611   const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
1612
1613   if (xr->y > xr->length - chart_height)
1614     return true;
1615
1616   if (xr->cairo != NULL)
1617     {
1618       xr_draw_chart (cs->chart_item, xr->cairo,
1619                      0.0,
1620                      xr_to_pt (xr->y),
1621                      xr_to_pt (xr->width),
1622                      xr_to_pt (chart_height));
1623     }
1624   xr->y += chart_height;
1625
1626   return false;
1627 }
1628
1629 static void
1630 xr_chart_destroy (struct xr_render_fsm *fsm)
1631 {
1632   struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1633
1634   chart_item_unref (cs->chart_item);
1635   free (cs);
1636 }
1637
1638 static struct xr_render_fsm *
1639 xr_render_chart (const struct chart_item *chart_item)
1640 {
1641   struct xr_chart_state *cs;
1642
1643   cs = xmalloc (sizeof *cs);
1644   cs->fsm.render = xr_chart_render;
1645   cs->fsm.destroy = xr_chart_destroy;
1646   cs->chart_item = chart_item_ref (chart_item);
1647
1648   return &cs->fsm;
1649 }
1650 \f
1651 static bool
1652 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1653 {
1654   return xr->y > 0;
1655 }
1656
1657 static void
1658 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1659 {
1660   /* Nothing to do. */
1661 }
1662
1663 static struct xr_render_fsm *
1664 xr_render_eject (void)
1665 {
1666   static struct xr_render_fsm eject_renderer =
1667     {
1668       xr_eject_render,
1669       xr_eject_destroy
1670     };
1671
1672   return &eject_renderer;
1673 }
1674 \f
1675 static struct xr_render_fsm *
1676 xr_create_text_renderer (struct xr_driver *xr, const struct text_item *item)
1677 {
1678   struct tab_table *tab = tab_create (1, 1);
1679
1680   struct cell_style *style = pool_alloc (tab->container, sizeof *style);
1681   *style = (struct cell_style) CELL_STYLE_INITIALIZER;
1682   if (item->font)
1683     style->font = pool_strdup (tab->container, item->font);
1684   style->font_size = item->font_size;
1685   style->bold = item->bold;
1686   style->italic = item->italic;
1687   style->underline = item->underline;
1688   tab->styles[0] = style;
1689
1690   tab_text (tab, 0, 0, TAB_LEFT, text_item_get_text (item));
1691   struct table_item *table_item = table_item_create (&tab->table, NULL, NULL);
1692   struct xr_render_fsm *fsm = xr_render_table (xr, table_item);
1693   table_item_unref (table_item);
1694
1695   return fsm;
1696 }
1697
1698 static struct xr_render_fsm *
1699 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1700 {
1701   enum text_item_type type = text_item_get_type (text_item);
1702
1703   switch (type)
1704     {
1705     case TEXT_ITEM_PAGE_TITLE:
1706       break;
1707
1708     case TEXT_ITEM_BLANK_LINE:
1709       if (xr->y > 0)
1710         xr->y += xr->char_height;
1711       break;
1712
1713     case TEXT_ITEM_EJECT_PAGE:
1714       if (xr->y > 0)
1715         return xr_render_eject ();
1716       break;
1717
1718     default:
1719       return xr_create_text_renderer (xr, text_item);
1720     }
1721
1722   return NULL;
1723 }
1724
1725 static struct xr_render_fsm *
1726 xr_render_message (struct xr_driver *xr,
1727                    const struct message_item *message_item)
1728 {
1729   char *s = msg_to_string (message_item_get_msg (message_item));
1730   struct text_item *item = text_item_create (TEXT_ITEM_PARAGRAPH, s);
1731   free (s);
1732   struct xr_render_fsm *fsm = xr_create_text_renderer (xr, item);
1733   text_item_unref (item);
1734
1735   return fsm;
1736 }
1737
1738 static struct xr_render_fsm *
1739 xr_render_output_item (struct xr_driver *xr,
1740                        const struct output_item *output_item)
1741 {
1742   if (is_table_item (output_item))
1743     return xr_render_table (xr, to_table_item (output_item));
1744   else if (is_chart_item (output_item))
1745     return xr_render_chart (to_chart_item (output_item));
1746   else if (is_text_item (output_item))
1747     return xr_render_text (xr, to_text_item (output_item));
1748   else if (is_message_item (output_item))
1749     return xr_render_message (xr, to_message_item (output_item));
1750   else
1751     return NULL;
1752 }