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