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