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