output: Make groups contain their subitems, and get rid of spv_item.
[pspp] / src / output / cairo-fsm.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-fsm.h"
20
21 #include <math.h>
22 #include <pango/pango-layout.h>
23 #include <pango/pango.h>
24 #include <pango/pangocairo.h>
25
26 #include "libpspp/assertion.h"
27 #include "libpspp/str.h"
28 #include "output/cairo-chart.h"
29 #include "output/chart-provider.h"
30 #include "output/charts/barchart.h"
31 #include "output/charts/boxplot.h"
32 #include "output/charts/np-plot.h"
33 #include "output/charts/piechart.h"
34 #include "output/charts/plot-hist.h"
35 #include "output/charts/roc-chart.h"
36 #include "output/charts/scatterplot.h"
37 #include "output/charts/scree.h"
38 #include "output/charts/spreadlevel-plot.h"
39 #include "output/pivot-output.h"
40 #include "output/pivot-table.h"
41 #include "output/render.h"
42 #include "output/output-item.h"
43
44 #include "gl/c-ctype.h"
45 #include "gl/c-strcase.h"
46 #include "gl/xalloc.h"
47
48 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
49 #define H TABLE_HORZ
50 #define V TABLE_VERT
51 \f
52 struct xr_fsm_style *
53 xr_fsm_style_ref (const struct xr_fsm_style *style_)
54 {
55   assert (style_->ref_cnt > 0);
56
57   struct xr_fsm_style *style = CONST_CAST (struct xr_fsm_style *, style_);
58   style->ref_cnt++;
59   return style;
60 }
61
62 struct xr_fsm_style *
63 xr_fsm_style_unshare (struct xr_fsm_style *old)
64 {
65   assert (old->ref_cnt > 0);
66   if (old->ref_cnt == 1)
67     return old;
68
69   xr_fsm_style_unref (old);
70
71   struct xr_fsm_style *new = xmemdup (old, sizeof *old);
72   new->ref_cnt = 1;
73   if (old->font)
74     new->font = pango_font_description_copy (old->font);
75
76   return new;
77 }
78
79 void
80 xr_fsm_style_unref (struct xr_fsm_style *style)
81 {
82   if (style)
83     {
84       assert (style->ref_cnt > 0);
85       if (!--style->ref_cnt)
86         {
87           pango_font_description_free (style->font);
88           free (style);
89         }
90     }
91 }
92
93 bool
94 xr_fsm_style_equals (const struct xr_fsm_style *a,
95                      const struct xr_fsm_style *b)
96 {
97   if (a->size[H] != b->size[H]
98       || a->size[V] != b->size[V]
99       || a->min_break[H] != b->min_break[H]
100       || a->min_break[V] != b->min_break[V]
101       || !pango_font_description_equal (a->font, b->font)
102       || a->use_system_colors != b->use_system_colors
103       || a->object_spacing != b->object_spacing
104       || a->font_resolution != b->font_resolution)
105     return false;
106
107   return true;
108 }
109 \f
110 /* Renders a single output_item to an output device in one of two ways:
111
112    - 'print == true': Broken across multiple pages if necessary.
113
114    - 'print == false': In a single region that the user may scroll around if
115      needed.
116
117    Normally 'output_item' corresponds to a single rendering.  There is a
118    special case when 'print == true' and 'output_item' is a table_item with
119    multiple layers and 'item->pt->table_look->print_all_layers == true'.  In
120    that case, each layer is rendered separately from the FSM's internal point
121    of view; from the client's point of view, it is all one operation.
122 */
123 struct xr_fsm
124   {
125     struct xr_fsm_style *style;
126     struct output_item *item;
127     bool print;
128
129     /* Print mode only. */
130     bool done;
131
132     /* Table items only. */
133     size_t *layer_indexes;
134     struct render_params rp;
135     struct render_pager *p;
136     cairo_t *cairo;             /* XXX should this be here?! */
137   };
138
139 /* The unit used for internal measurements is inch/(72 * XR_POINT).
140    (Thus, XR_POINT units represent one point.) */
141 #define XR_POINT PANGO_SCALE
142
143 /* Conversions to and from points. */
144 static double
145 xr_to_pt (int x)
146 {
147   return x / (double) XR_POINT;
148 }
149
150 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
151 static int
152 px_to_xr (int x)
153 {
154   return x * (PANGO_SCALE * 72 / 96);
155 }
156
157 static int
158 pango_to_xr (int pango)
159 {
160   return (XR_POINT != PANGO_SCALE
161           ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
162           : pango);
163 }
164
165 static int
166 xr_to_pango (int xr)
167 {
168   return (XR_POINT != PANGO_SCALE
169           ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
170           : xr);
171 }
172
173 /* Dimensions for drawing lines in tables. */
174 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
175 #define XR_LINE_SPACE XR_POINT       /* Space between double lines. */
176
177 static void
178 xr_layout_cell (struct xr_fsm *, const struct table_cell *,
179                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
180                 int *width, int *height, int *brk);
181
182 static void
183 xr_set_source_rgba (cairo_t *cairo, const struct cell_color *color)
184 {
185   cairo_set_source_rgba (cairo,
186                          color->r / 255., color->g / 255., color->b / 255.,
187                          color->alpha / 255.);
188 }
189
190 static void
191 xr_draw_line (struct xr_fsm *xr, int x0, int y0, int x1, int y1, int style,
192               const struct cell_color *color)
193 {
194   cairo_new_path (xr->cairo);
195   cairo_set_line_width (
196     xr->cairo,
197     xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
198               : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
199               : XR_LINE_WIDTH));
200   cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0));
201   cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1));
202
203   if (!xr->style->use_system_colors)
204     xr_set_source_rgba (xr->cairo, color);
205   if (style == RENDER_LINE_DASHED)
206     cairo_set_dash (xr->cairo, (double[]) { 2 }, 1, 0);
207   cairo_stroke (xr->cairo);
208   if (style == RENDER_LINE_DASHED)
209     cairo_set_dash (xr->cairo, NULL, 0, 0);
210 }
211
212 static void UNUSED
213 xr_draw_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
214 {
215   cairo_new_path (xr->cairo);
216   cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
217   cairo_close_path (xr->cairo);
218   cairo_stroke (xr->cairo);
219   cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0));
220   cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y0));
221   cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1));
222   cairo_line_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y1));
223 }
224
225 static void
226 fill_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
227 {
228   cairo_new_path (xr->cairo);
229   cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
230   cairo_rectangle (xr->cairo,
231                    xr_to_pt (x0), xr_to_pt (y0),
232                    xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
233   cairo_fill (xr->cairo);
234 }
235
236 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
237    shortening it to X0...X1 if SHORTEN is true.
238    Draws a horizontal line X1...X3 at Y if RIGHT says so,
239    shortening it to X2...X3 if SHORTEN is true. */
240 static void
241 xr_draw_horz_line (struct xr_fsm *xr, int x0, int x1, int x2, int x3, int y,
242                    enum render_line_style left, enum render_line_style right,
243                    const struct cell_color *left_color,
244                    const struct cell_color *right_color,
245                    bool shorten)
246 {
247   if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
248       && cell_color_equal (left_color, right_color))
249     xr_draw_line (xr, x0, y, x3, y, left, left_color);
250   else
251     {
252       if (left != RENDER_LINE_NONE)
253         xr_draw_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
254       if (right != RENDER_LINE_NONE)
255         xr_draw_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
256     }
257 }
258
259 /* Draws a vertical line Y0...Y2 at X if TOP says so,
260    shortening it to Y0...Y1 if SHORTEN is true.
261    Draws a vertical line Y1...Y3 at X if BOTTOM says so,
262    shortening it to Y2...Y3 if SHORTEN is true. */
263 static void
264 xr_draw_vert_line (struct xr_fsm *xr, int y0, int y1, int y2, int y3, int x,
265                    enum render_line_style top, enum render_line_style bottom,
266                    const struct cell_color *top_color,
267                    const struct cell_color *bottom_color,
268                    bool shorten)
269 {
270   if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
271       && cell_color_equal (top_color, bottom_color))
272     xr_draw_line (xr, x, y0, x, y3, top, top_color);
273   else
274     {
275       if (top != RENDER_LINE_NONE)
276         xr_draw_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
277       if (bottom != RENDER_LINE_NONE)
278         xr_draw_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
279     }
280 }
281
282 static void
283 xrr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
284                enum render_line_style styles[TABLE_N_AXES][2],
285                struct cell_color colors[TABLE_N_AXES][2])
286 {
287   const int x0 = bb[H][0];
288   const int y0 = bb[V][0];
289   const int x3 = bb[H][1];
290   const int y3 = bb[V][1];
291   const int top = styles[H][0];
292   const int bottom = styles[H][1];
293
294   int start_side = render_direction_rtl();
295   int end_side = !start_side;
296   const int start_of_line = styles[V][start_side];
297   const int end_of_line   = styles[V][end_side];
298   const struct cell_color *top_color = &colors[H][0];
299   const struct cell_color *bottom_color = &colors[H][1];
300   const struct cell_color *start_color = &colors[V][start_side];
301   const struct cell_color *end_color = &colors[V][end_side];
302
303   /* The algorithm here is somewhat subtle, to allow it to handle
304      all the kinds of intersections that we need.
305
306      Three additional ordinates are assigned along the x axis.  The
307      first is xc, midway between x0 and x3.  The others are x1 and
308      x2; for a single vertical line these are equal to xc, and for
309      a double vertical line they are the ordinates of the left and
310      right half of the double line.
311
312      yc, y1, and y2 are assigned similarly along the y axis.
313
314      The following diagram shows the coordinate system and output
315      for double top and bottom lines, single left line, and no
316      right line:
317
318                  x0       x1 xc  x2      x3
319                y0 ________________________
320                   |        #     #       |
321                   |        #     #       |
322                   |        #     #       |
323                   |        #     #       |
324                   |        #     #       |
325      y1 = y2 = yc |#########     #       |
326                   |        #     #       |
327                   |        #     #       |
328                   |        #     #       |
329                   |        #     #       |
330                y3 |________#_____#_______|
331   */
332   struct xr_fsm *xr = xr_;
333
334   /* Offset from center of each line in a pair of double lines. */
335   int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
336
337   /* Are the lines along each axis single or double?
338      (It doesn't make sense to have different kinds of line on the
339      same axis, so we don't try to gracefully handle that case.) */
340   bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
341   bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
342
343   /* When horizontal lines are doubled,
344      the left-side line along y1 normally runs from x0 to x2,
345      and the right-side line along y1 from x3 to x1.
346      If the top-side line is also doubled, we shorten the y1 lines,
347      so that the left-side line runs only to x1,
348      and the right-side line only to x2.
349      Otherwise, the horizontal line at y = y1 below would cut off
350      the intersection, which looks ugly:
351                x0       x1     x2      x3
352              y0 ________________________
353                 |        #     #       |
354                 |        #     #       |
355                 |        #     #       |
356                 |        #     #       |
357              y1 |#########     ########|
358                 |                      |
359                 |                      |
360              y2 |######################|
361                 |                      |
362                 |                      |
363              y3 |______________________|
364      It is more of a judgment call when the horizontal line is
365      single.  We actually choose to cut off the line anyhow, as
366      shown in the first diagram above.
367   */
368   bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
369   bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
370   bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
371   int horz_line_ofs = double_vert ? double_line_ofs : 0;
372   int xc = (x0 + x3) / 2;
373   int x1 = xc - horz_line_ofs;
374   int x2 = xc + horz_line_ofs;
375
376   bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
377   bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
378   bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
379   int vert_line_ofs = double_horz ? double_line_ofs : 0;
380   int yc = (y0 + y3) / 2;
381   int y1 = yc - vert_line_ofs;
382   int y2 = yc + vert_line_ofs;
383
384   if (!double_horz)
385     xr_draw_horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
386                        start_color, end_color, shorten_yc_line);
387   else
388     {
389       xr_draw_horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
390                          start_color, end_color, shorten_y1_lines);
391       xr_draw_horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
392                          start_color, end_color, shorten_y2_lines);
393     }
394
395   if (!double_vert)
396     xr_draw_vert_line (xr, y0, y1, y2, y3, xc, top, bottom,
397                        top_color, bottom_color, shorten_xc_line);
398   else
399     {
400       xr_draw_vert_line (xr, y0, y1, y2, y3, x1, top, bottom,
401                          top_color, bottom_color, shorten_x1_lines);
402       xr_draw_vert_line (xr, y0, y1, y2, y3, x2, top, bottom,
403                          top_color, bottom_color, shorten_x2_lines);
404     }
405 }
406
407 static void
408 xrr_measure_cell_width (void *xr_, const struct table_cell *cell,
409                         int *min_width, int *max_width)
410 {
411   struct xr_fsm *xr = xr_;
412   int bb[TABLE_N_AXES][2];
413   int clip[TABLE_N_AXES][2];
414   int h;
415
416   bb[H][0] = 0;
417   bb[H][1] = INT_MAX;
418   bb[V][0] = 0;
419   bb[V][1] = INT_MAX;
420   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
421   xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
422
423   bb[H][1] = 1;
424   xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
425
426   const int (*margin)[2] = cell->cell_style->margin;
427   if (*min_width > 0)
428     *min_width += px_to_xr (margin[H][0] + margin[H][1]);
429   if (*max_width > 0)
430     *max_width += px_to_xr (margin[H][0] + margin[H][1]);
431 }
432
433 static int
434 xrr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
435 {
436   struct xr_fsm *xr = xr_;
437
438   const int (*margin)[2] = cell->cell_style->margin;
439
440   int bb[TABLE_N_AXES][2] = {
441     [H][0] = 0,
442     [H][1] = width - px_to_xr (margin[H][0] + margin[H][1]),
443     [V][0] = 0,
444     [V][1] = INT_MAX,
445   };
446
447   int clip[TABLE_N_AXES][2] = { { 0, 0 }, { 0, 0 } };
448
449   int w, h;
450   xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
451   h += px_to_xr (margin[V][0] + margin[V][1]);
452   return h;
453 }
454
455 static void xr_clip (struct xr_fsm *, int clip[TABLE_N_AXES][2]);
456
457 static void
458 xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
459                int bb[TABLE_N_AXES][2], int valign_offset,
460                int spill[TABLE_N_AXES][2],
461                int clip[TABLE_N_AXES][2])
462 {
463   struct xr_fsm *xr = xr_;
464   int w, h, brk;
465
466   const struct cell_color *bg = &cell->font_style->bg[color_idx];
467   if ((bg->r != 255 || bg->g != 255 || bg->b != 255) && bg->alpha)
468     {
469       cairo_save (xr->cairo);
470       int bg_clip[TABLE_N_AXES][2];
471       for (int axis = 0; axis < TABLE_N_AXES; axis++)
472         {
473           bg_clip[axis][0] = clip[axis][0];
474           if (bb[axis][0] == clip[axis][0])
475             bg_clip[axis][0] -= spill[axis][0];
476
477           bg_clip[axis][1] = clip[axis][1];
478           if (bb[axis][1] == clip[axis][1])
479             bg_clip[axis][1] += spill[axis][1];
480         }
481       xr_clip (xr, bg_clip);
482       xr_set_source_rgba (xr->cairo, bg);
483       fill_rectangle (xr,
484                       bb[H][0] - spill[H][0],
485                       bb[V][0] - spill[V][0],
486                       bb[H][1] + spill[H][1],
487                       bb[V][1] + spill[V][1]);
488       cairo_restore (xr->cairo);
489     }
490   cairo_save (xr->cairo);
491   if (!xr->style->use_system_colors)
492     xr_set_source_rgba (xr->cairo, &cell->font_style->fg[color_idx]);
493
494   bb[V][0] += valign_offset;
495
496   for (int axis = 0; axis < TABLE_N_AXES; axis++)
497     {
498       bb[axis][0] += px_to_xr (cell->cell_style->margin[axis][0]);
499       bb[axis][1] -= px_to_xr (cell->cell_style->margin[axis][1]);
500     }
501   if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
502     xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
503   cairo_restore (xr->cairo);
504 }
505
506 static int
507 xrr_adjust_break (void *xr_, const struct table_cell *cell,
508                   int width, int height)
509 {
510   struct xr_fsm *xr = xr_;
511
512   if (xrr_measure_cell_height (xr_, cell, width) < height)
513     return -1;
514
515   const int (*margin)[2] = cell->cell_style->margin;
516
517   int bb[TABLE_N_AXES][2] = {
518     [H][0] = 0,
519     [V][0] = 0,
520     [H][1] = width - px_to_xr (margin[H][0] + margin[H][1]),
521     [V][1] = height - px_to_xr (margin[V][0] + margin[V][1]),
522   };
523   if (bb[H][1] <= 0)
524     return 0;
525
526   int clip[TABLE_N_AXES][2] = { { 0, 0 }, { 0, 0 } };
527
528   int w, h, brk;
529   xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
530   return brk;
531 }
532
533 static void
534 xrr_scale (void *xr_, double scale)
535 {
536   struct xr_fsm *xr = xr_;
537   cairo_scale (xr->cairo, scale, scale);
538 }
539 \f
540 static void
541 xr_clip (struct xr_fsm *xr, int clip[TABLE_N_AXES][2])
542 {
543   if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
544     {
545       double x0 = xr_to_pt (clip[H][0]);
546       double y0 = xr_to_pt (clip[V][0]);
547       double x1 = xr_to_pt (clip[H][1]);
548       double y1 = xr_to_pt (clip[V][1]);
549
550       cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
551       cairo_clip (xr->cairo);
552     }
553 }
554
555 static void
556 add_attr (PangoAttrList *list, PangoAttribute *attr,
557           guint start_index, guint end_index)
558 {
559   attr->start_index = start_index;
560   attr->end_index = end_index;
561   pango_attr_list_insert (list, attr);
562 }
563
564 static void
565 markup_escape (struct string *out, bool markup, const char *in, size_t len)
566 {
567   if (!markup)
568     {
569       ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
570       return;
571     }
572
573   while (len-- > 0)
574     {
575       int c = *in++;
576       switch (c)
577         {
578         case 0:
579           return;
580         case '&':
581           ds_put_cstr (out, "&amp;");
582           break;
583         case '<':
584           ds_put_cstr (out, "&lt;");
585           break;
586         case '>':
587           ds_put_cstr (out, "&gt;");
588           break;
589         default:
590           ds_put_byte (out, c);
591           break;
592         }
593     }
594 }
595
596 static int
597 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
598 {
599   int size[TABLE_N_AXES];
600   pango_layout_get_size (layout, &size[H], &size[V]);
601   return size[axis];
602 }
603
604 static PangoFontDescription *
605 parse_font (const char *font, int default_size, bool bold, bool italic)
606 {
607   if (!c_strcasecmp (font, "Monospaced"))
608     font = "Monospace";
609
610   PangoFontDescription *desc = pango_font_description_from_string (font);
611   if (desc == NULL)
612     return NULL;
613
614   /* If the font description didn't include an explicit font size, then set it
615      to DEFAULT_SIZE, which is in inch/72000 units. */
616   if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
617     pango_font_description_set_size (desc,
618                                      (default_size / 1000.0) * PANGO_SCALE);
619
620   pango_font_description_set_weight (desc, (bold
621                                             ? PANGO_WEIGHT_BOLD
622                                             : PANGO_WEIGHT_NORMAL));
623   pango_font_description_set_style (desc, (italic
624                                            ? PANGO_STYLE_ITALIC
625                                            : PANGO_STYLE_NORMAL));
626
627   return desc;
628 }
629
630 static int
631 xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell,
632                      int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
633                      int *widthp, int *brk)
634 {
635   const struct pivot_table *pt = xr->item->table;
636   const struct font_style *font_style = cell->font_style;
637   const struct cell_style *cell_style = cell->cell_style;
638   unsigned int options = cell->options;
639
640   enum table_axis X = options & TAB_ROTATE ? V : H;
641   enum table_axis Y = !X;
642   int R = options & TAB_ROTATE ? 0 : 1;
643
644   PangoFontDescription *desc = NULL;
645   if (font_style->typeface)
646       desc = parse_font (
647         font_style->typeface,
648         font_style->size ? font_style->size * 1000 : 10000,
649         font_style->bold, font_style->italic);
650   if (!desc)
651     desc = xr->style->font;
652
653   assert (xr->cairo);
654   PangoContext *context = pango_cairo_create_context (xr->cairo);
655   pango_cairo_context_set_resolution (context, xr->style->font_resolution);
656   PangoLayout *layout = pango_layout_new (context);
657   g_object_unref (context);
658
659   pango_layout_set_font_description (layout, desc);
660
661   struct string body = DS_EMPTY_INITIALIZER;
662   bool numeric = pivot_value_format_body (cell->value, pt, &body);
663
664   enum table_halign halign = table_halign_interpret (
665     cell->cell_style->halign, numeric);
666
667   if (cell_style->halign == TABLE_HALIGN_DECIMAL
668       && !(cell->options & TAB_ROTATE))
669     {
670       int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
671
672       const char *decimal = strrchr (ds_cstr (&body),
673                                      cell_style->decimal_char);
674       if (decimal)
675         {
676           pango_layout_set_text (layout, decimal, strlen (decimal));
677           pango_layout_set_width (layout, -1);
678           margin_adjustment += get_layout_dimension (layout, H);
679         }
680
681       if (margin_adjustment < 0)
682         bb[H][1] += margin_adjustment;
683     }
684
685   PangoAttrList *attrs = NULL;
686
687   /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
688      Pango's implementation of it): it will break after a period or a comma
689      that precedes a digit, e.g. in ".000" it will break after the period.
690      This code looks for such a situation and inserts a U+2060 WORD JOINER
691      to prevent the break.
692
693      This isn't necessary when the decimal point is between two digits
694      (e.g. "0.000" won't be broken) or when the display width is not limited so
695      that word wrapping won't happen.
696
697      It isn't necessary to look for more than one period or comma, as would
698      happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
699      are present then there will always be a digit on both sides of every
700      period and comma. */
701   bool markup = cell->font_style->markup;
702   if (markup)
703     {
704       PangoAttrList *new_attrs;
705       char *new_text;
706       if (pango_parse_markup (ds_cstr (&body), -1, 0,
707                               &new_attrs, &new_text, NULL, NULL))
708         {
709           attrs = new_attrs;
710           ds_destroy (&body);
711           body.ss = ss_cstr (new_text);
712           body.capacity = body.ss.length;
713         }
714       else
715         {
716           /* XXX should we report the error? */
717         }
718     }
719   else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
720     {
721       const char *text = ds_cstr (&body);
722       const char *decimal = text + strcspn (text, ".,");
723       if (decimal[0]
724           && c_isdigit (decimal[1])
725           && (decimal == text || !c_isdigit (decimal[-1])))
726         {
727           struct string tmp = DS_EMPTY_INITIALIZER;
728           ds_extend (&tmp, ds_length (&body) + 16);
729           markup_escape (&tmp, markup, text, decimal - text + 1);
730           ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
731           markup_escape (&tmp, markup, decimal + 1, -1);
732           ds_swap (&tmp, &body);
733           ds_destroy (&tmp);
734         }
735     }
736
737   if (font_style->underline)
738     {
739       if (!attrs)
740         attrs = pango_attr_list_new ();
741       pango_attr_list_insert (attrs, pango_attr_underline_new (
742                                 PANGO_UNDERLINE_SINGLE));
743     }
744
745   const struct pivot_value *value = cell->value;
746   if (value->n_footnotes || value->n_subscripts)
747     {
748       size_t subscript_ofs = ds_length (&body);
749       for (size_t i = 0; i < value->n_subscripts; i++)
750         {
751           if (i)
752             ds_put_byte (&body, ',');
753           ds_put_cstr (&body, value->subscripts[i]);
754         }
755
756       size_t footnote_ofs = ds_length (&body);
757       size_t n_footnotes = 0;
758       for (size_t i = 0; i < value->n_footnotes; i++)
759         {
760           const struct pivot_footnote *f
761             = pt->footnotes[value->footnote_indexes[i]];
762           if (f->show)
763             {
764               if (n_footnotes++)
765                 ds_put_byte (&body, ',');
766               pivot_footnote_format_marker (f, pt, &body);
767             }
768         }
769
770       /* Allow footnote markers to occupy the right margin.  That way, numbers
771          in the column are still aligned. */
772       if (value->n_footnotes && halign == TABLE_HALIGN_RIGHT)
773         {
774           /* Measure the width of the footnote marker, so we know how much we
775              need to make room for. */
776           pango_layout_set_text (layout, ds_cstr (&body) + footnote_ofs,
777                                  ds_length (&body) - footnote_ofs);
778
779           PangoAttrList *fn_attrs = pango_attr_list_new ();
780           pango_attr_list_insert (
781             fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
782           pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
783           pango_layout_set_attributes (layout, fn_attrs);
784           pango_attr_list_unref (fn_attrs);
785           int footnote_width = get_layout_dimension (layout, X);
786
787           /* Bound the adjustment by the width of the right margin. */
788           int right_margin = px_to_xr (cell_style->margin[X][R]);
789           int footnote_adjustment = MIN (footnote_width, right_margin);
790
791           /* Adjust the bounding box. */
792           if (options & TAB_ROTATE)
793             footnote_adjustment = -footnote_adjustment;
794           bb[X][R] += footnote_adjustment;
795
796           /* Clean up. */
797           pango_layout_set_attributes (layout, NULL);
798         }
799
800       /* Set attributes. */
801       if (!attrs)
802         attrs = pango_attr_list_new ();
803       add_attr (attrs, pango_attr_font_desc_new (desc), subscript_ofs,
804                 PANGO_ATTR_INDEX_TO_TEXT_END);
805       add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
806                 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
807       if (value->n_subscripts)
808         add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
809                   footnote_ofs - subscript_ofs);
810       if (value->n_footnotes)
811         {
812           bool superscript = pt->look->footnote_marker_superscripts;
813           add_attr (attrs, pango_attr_rise_new (superscript ? 3000 : -3000),
814                     footnote_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
815         }
816     }
817
818   /* Set the attributes, if any. */
819   if (attrs)
820     {
821       pango_layout_set_attributes (layout, attrs);
822       pango_attr_list_unref (attrs);
823     }
824
825   /* Set the text. */
826   pango_layout_set_text (layout, ds_cstr (&body), ds_length (&body));
827
828   pango_layout_set_alignment (layout,
829                               (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
830                                : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
831                                : PANGO_ALIGN_CENTER));
832   pango_layout_set_width (
833     layout,
834     bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
835   pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
836
837   int size[TABLE_N_AXES];
838   pango_layout_get_size (layout, &size[H], &size[V]);
839
840   if (clip[H][0] != clip[H][1])
841     {
842       cairo_save (xr->cairo);
843       if (!(options & TAB_ROTATE))
844         xr_clip (xr, clip);
845       if (options & TAB_ROTATE)
846         {
847           int extra = bb[H][1] - bb[H][0] - size[V];
848           int halign_offset = extra > 0 ? extra / 2 : 0;
849           cairo_translate (xr->cairo,
850                            xr_to_pt (bb[H][0] + halign_offset),
851                            xr_to_pt (bb[V][1]));
852           cairo_rotate (xr->cairo, -M_PI_2);
853         }
854       else
855         cairo_translate (xr->cairo,
856                          xr_to_pt (bb[H][0]),
857                          xr_to_pt (bb[V][0]));
858       pango_cairo_show_layout (xr->cairo, layout);
859
860       /* If enabled, this draws a blue rectangle around the extents of each
861          line of text, which can be rather useful for debugging layout
862          issues. */
863       if (0)
864         {
865           PangoLayoutIter *iter;
866           iter = pango_layout_get_iter (layout);
867           do
868             {
869               PangoRectangle extents;
870
871               pango_layout_iter_get_line_extents (iter, &extents, NULL);
872               cairo_save (xr->cairo);
873               cairo_set_source_rgb (xr->cairo, 1, 0, 0);
874               xr_draw_rectangle (
875                 xr,
876                 pango_to_xr (extents.x),
877                 pango_to_xr (extents.y),
878                 pango_to_xr (extents.x + extents.width),
879                 pango_to_xr (extents.y + extents.height));
880               cairo_restore (xr->cairo);
881             }
882           while (pango_layout_iter_next_line (iter));
883           pango_layout_iter_free (iter);
884         }
885
886       cairo_restore (xr->cairo);
887     }
888
889   int w = pango_to_xr (size[X]);
890   int h = pango_to_xr (size[Y]);
891   if (w > *widthp)
892     *widthp = w;
893   if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
894     {
895       PangoLayoutIter *iter;
896       int best = 0;
897
898       /* Choose a breakpoint between lines instead of in the middle of one. */
899       iter = pango_layout_get_iter (layout);
900       do
901         {
902           PangoRectangle extents;
903           int y0, y1;
904           int bottom;
905
906           pango_layout_iter_get_line_extents (iter, NULL, &extents);
907           pango_layout_iter_get_line_yrange (iter, &y0, &y1);
908           extents.x = pango_to_xr (extents.x);
909           extents.y = pango_to_xr (y0);
910           extents.width = pango_to_xr (extents.width);
911           extents.height = pango_to_xr (y1 - y0);
912           bottom = bb[V][0] + extents.y + extents.height;
913           if (bottom < bb[V][1])
914             {
915               if (brk && clip[H][0] != clip[H][1])
916                 best = bottom;
917               if (brk)
918                 *brk = bottom;
919             }
920           else
921             break;
922         }
923       while (pango_layout_iter_next_line (iter));
924       pango_layout_iter_free (iter);
925
926       /* If enabled, draws a green line across the chosen breakpoint, which can
927          be useful for debugging issues with breaking.  */
928       if (0)
929         {
930           if (best)
931             xr_draw_line (xr, 0, best,
932                           xr->style->size[H], best,
933                           RENDER_LINE_SINGLE,
934                           &(struct cell_color) CELL_COLOR (0, 255, 0));
935         }
936     }
937
938   pango_layout_set_attributes (layout, NULL);
939
940   if (desc != xr->style->font)
941     pango_font_description_free (desc);
942   g_object_unref (G_OBJECT (layout));
943   ds_destroy (&body);
944
945   return h;
946 }
947
948 static void
949 xr_layout_cell (struct xr_fsm *xr, const struct table_cell *cell,
950                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
951                 int *width, int *height, int *brk)
952 {
953   *width = 0;
954   *height = 0;
955
956   /* If enabled, draws a blue rectangle around the cell extents, which can be
957      useful for debugging layout. */
958   if (0)
959     {
960       if (clip[H][0] != clip[H][1])
961         {
962           cairo_save (xr->cairo);
963           cairo_set_source_rgb (xr->cairo, 0, 0, 1);
964           xr_draw_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
965           cairo_restore (xr->cairo);
966         }
967     }
968
969   if (brk)
970     *brk = bb[V][0];
971   *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
972 }
973
974 #define CHART_WIDTH 500
975 #define CHART_HEIGHT 375
976
977 static struct xr_fsm *
978 xr_fsm_create (const struct output_item *item_,
979                const struct xr_fsm_style *style, cairo_t *cr,
980                bool print)
981 {
982   struct output_item *item;
983
984   switch (item_->type)
985     {
986     case OUTPUT_ITEM_CHART:
987     case OUTPUT_ITEM_IMAGE:
988     case OUTPUT_ITEM_PAGE_BREAK:
989     case OUTPUT_ITEM_TABLE:
990       item = output_item_ref (item_);
991       break;
992
993     case OUTPUT_ITEM_GROUP:
994     case OUTPUT_ITEM_PAGE_SETUP:
995       return NULL;
996
997     case OUTPUT_ITEM_MESSAGE:
998       item = text_item_to_table_item (message_item_to_text_item (
999                                         output_item_ref (item_)));
1000       break;
1001
1002     case OUTPUT_ITEM_TEXT:
1003       if (item_->text.subtype == TEXT_ITEM_PAGE_TITLE)
1004         return NULL;
1005
1006       item = text_item_to_table_item (output_item_ref (item_));
1007       break;
1008
1009     default:
1010       NOT_REACHED ();
1011     }
1012
1013   assert (item->type == OUTPUT_ITEM_TABLE
1014           || item->type == OUTPUT_ITEM_CHART
1015           || item->type == OUTPUT_ITEM_IMAGE
1016           || item->type == OUTPUT_ITEM_PAGE_BREAK);
1017
1018   size_t *layer_indexes = NULL;
1019   if (item->type == OUTPUT_ITEM_TABLE)
1020     {
1021       layer_indexes = pivot_output_next_layer (item->table, NULL, print);
1022       if (!layer_indexes)
1023         return NULL;
1024     }
1025
1026   static const struct render_ops xrr_render_ops = {
1027     .measure_cell_width = xrr_measure_cell_width,
1028     .measure_cell_height = xrr_measure_cell_height,
1029     .adjust_break = xrr_adjust_break,
1030     .draw_line = xrr_draw_line,
1031     .draw_cell = xrr_draw_cell,
1032     .scale = xrr_scale,
1033   };
1034
1035   enum { LW = XR_LINE_WIDTH, LS = XR_LINE_SPACE };
1036   static const int xr_line_widths[RENDER_N_LINES] =
1037     {
1038       [RENDER_LINE_NONE] = 0,
1039       [RENDER_LINE_SINGLE] = LW,
1040       [RENDER_LINE_DASHED] = LW,
1041       [RENDER_LINE_THICK] = LW * 2,
1042       [RENDER_LINE_THIN] = LW / 2,
1043       [RENDER_LINE_DOUBLE] = 2 * LW + LS,
1044     };
1045
1046   struct xr_fsm *fsm = xmalloc (sizeof *fsm);
1047   *fsm = (struct xr_fsm) {
1048     .style = xr_fsm_style_ref (style),
1049     .item = item,
1050     .print = print,
1051     .layer_indexes = layer_indexes,
1052     .rp = {
1053       .ops = &xrr_render_ops,
1054       .aux = fsm,
1055       .size = { [H] = style->size[H], [V] = style->size[V] },
1056       .line_widths = xr_line_widths,
1057       .min_break = { [H] = style->min_break[H], [V] = style->min_break[V] },
1058       .supports_margins = true,
1059       .rtl = render_direction_rtl (),
1060       .printing = print,
1061     }
1062   };
1063
1064   /* Get font size. */
1065   PangoContext *context = pango_cairo_create_context (cr);
1066   pango_cairo_context_set_resolution (context, style->font_resolution);
1067   PangoLayout *layout = pango_layout_new (context);
1068   g_object_unref (context);
1069
1070   pango_layout_set_font_description (layout, style->font);
1071
1072   pango_layout_set_text (layout, "0", 1);
1073
1074   int char_size[TABLE_N_AXES];
1075   pango_layout_get_size (layout, &char_size[H], &char_size[V]);
1076   for (int a = 0; a < TABLE_N_AXES; a++)
1077     {
1078       int csa = pango_to_xr (char_size[a]);
1079       fsm->rp.font_size[a] = MAX (fsm->rp.font_size[a], csa);
1080     }
1081
1082   g_object_unref (G_OBJECT (layout));
1083
1084   if (item->type == OUTPUT_ITEM_TABLE)
1085     {
1086       fsm->cairo = cr;
1087       fsm->p = render_pager_create (&fsm->rp, item->table, fsm->layer_indexes);
1088       fsm->cairo = NULL;
1089     }
1090
1091   return fsm;
1092 }
1093
1094 struct xr_fsm *
1095 xr_fsm_create_for_printing (const struct output_item *item,
1096                             const struct xr_fsm_style *style, cairo_t *cr)
1097 {
1098   return xr_fsm_create (item, style, cr, true);
1099 }
1100
1101 void
1102 xr_fsm_destroy (struct xr_fsm *fsm)
1103 {
1104   if (fsm)
1105     {
1106       xr_fsm_style_unref (fsm->style);
1107       output_item_unref (fsm->item);
1108       free (fsm->layer_indexes);
1109       render_pager_destroy (fsm->p);
1110       assert (!fsm->cairo);
1111       free (fsm);
1112     }
1113 }
1114 \f
1115 /* Scrolling API. */
1116
1117 struct xr_fsm *
1118 xr_fsm_create_for_scrolling (const struct output_item *item,
1119                              const struct xr_fsm_style *style, cairo_t *cr)
1120 {
1121   return xr_fsm_create (item, style, cr, false);
1122 }
1123
1124 void
1125 xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp)
1126 {
1127   assert (!fsm->print);
1128
1129   int w, h;
1130
1131   switch (fsm->item->type)
1132     {
1133     case OUTPUT_ITEM_CHART:
1134       w = CHART_WIDTH;
1135       h = CHART_HEIGHT;
1136       break;
1137
1138     case OUTPUT_ITEM_IMAGE:
1139       w = cairo_image_surface_get_width (fsm->item->image);
1140       h = cairo_image_surface_get_height (fsm->item->image);
1141       break;
1142
1143     case OUTPUT_ITEM_TABLE:
1144       fsm->cairo = cr;
1145       w = render_pager_get_size (fsm->p, H) / XR_POINT;
1146       h = render_pager_get_size (fsm->p, V) / XR_POINT;
1147       fsm->cairo = NULL;
1148       break;
1149
1150     case OUTPUT_ITEM_GROUP:
1151     case OUTPUT_ITEM_MESSAGE:
1152     case OUTPUT_ITEM_PAGE_BREAK:
1153     case OUTPUT_ITEM_PAGE_SETUP:
1154     case OUTPUT_ITEM_TEXT:
1155     default:
1156       NOT_REACHED ();
1157     }
1158
1159   if (wp)
1160     *wp = w;
1161   if (hp)
1162     *hp = h;
1163 }
1164
1165 void
1166 xr_fsm_draw_all (struct xr_fsm *fsm, cairo_t *cr)
1167 {
1168   assert (!fsm->print);
1169   xr_fsm_draw_region (fsm, cr, 0, 0, INT_MAX, INT_MAX);
1170 }
1171
1172 static int
1173 mul_XR_POINT (int x)
1174 {
1175   return (x >= INT_MAX / XR_POINT ? INT_MAX
1176           : x <= INT_MIN / XR_POINT ? INT_MIN
1177           : x * XR_POINT);
1178 }
1179
1180 static void
1181 draw_image (cairo_surface_t *image, cairo_t *cr)
1182 {
1183   cairo_save (cr);
1184   cairo_set_source_surface (cr, image, 0, 0);
1185   cairo_rectangle (cr, 0, 0, cairo_image_surface_get_width (image),
1186                    cairo_image_surface_get_height (image));
1187   cairo_clip (cr);
1188   cairo_paint (cr);
1189   cairo_restore (cr);
1190 }
1191
1192 void
1193 xr_fsm_draw_region (struct xr_fsm *fsm, cairo_t *cr,
1194                     int x, int y, int w, int h)
1195 {
1196   assert (!fsm->print);
1197   switch (fsm->item->type)
1198     {
1199     case OUTPUT_ITEM_CHART:
1200       xr_draw_chart (fsm->item->chart, cr, CHART_WIDTH, CHART_HEIGHT);
1201       break;
1202
1203     case OUTPUT_ITEM_IMAGE:
1204       draw_image (fsm->item->image, cr);
1205       break;
1206
1207     case OUTPUT_ITEM_TABLE:
1208       fsm->cairo = cr;
1209       render_pager_draw_region (fsm->p, mul_XR_POINT (x), mul_XR_POINT (y),
1210                                 mul_XR_POINT (w), mul_XR_POINT (h));
1211       fsm->cairo = NULL;
1212       break;
1213
1214     case OUTPUT_ITEM_GROUP:
1215     case OUTPUT_ITEM_MESSAGE:
1216     case OUTPUT_ITEM_PAGE_BREAK:
1217     case OUTPUT_ITEM_PAGE_SETUP:
1218     case OUTPUT_ITEM_TEXT:
1219       NOT_REACHED ();
1220     }
1221 }
1222 \f
1223 /* Printing API. */
1224
1225 static int
1226 xr_fsm_draw_table (struct xr_fsm *fsm, int space)
1227 {
1228   int used = render_pager_draw_next (fsm->p, space);
1229   if (!render_pager_has_next (fsm->p))
1230     {
1231       render_pager_destroy (fsm->p);
1232
1233       fsm->layer_indexes = pivot_output_next_layer (fsm->item->table,
1234                                                     fsm->layer_indexes, true);
1235       if (fsm->layer_indexes)
1236         {
1237           fsm->p = render_pager_create (&fsm->rp, fsm->item->table,
1238                                         fsm->layer_indexes);
1239           if (fsm->item->table->look->paginate_layers)
1240             used = space;
1241           else
1242             used += fsm->style->object_spacing;
1243         }
1244       else
1245         {
1246           fsm->p = NULL;
1247           fsm->done = true;
1248         }
1249     }
1250   return MIN (used, space);
1251 }
1252
1253 static int
1254 xr_fsm_draw_chart (struct xr_fsm *fsm, int space)
1255 {
1256   const int chart_height = 0.8 * MIN (fsm->rp.size[H], fsm->rp.size[V]);
1257   if (space < chart_height)
1258     return 0;
1259
1260   fsm->done = true;
1261   xr_draw_chart (fsm->item->chart, fsm->cairo,
1262                  xr_to_pt (fsm->rp.size[H]), xr_to_pt (chart_height));
1263   return chart_height;
1264 }
1265
1266 static int
1267 xr_fsm_draw_image (struct xr_fsm *fsm, int space)
1268 {
1269   cairo_surface_t *image = fsm->item->image;
1270   int width = cairo_image_surface_get_width (image) * XR_POINT;
1271   int height = cairo_image_surface_get_height (image) * XR_POINT;
1272   if (!width || !height)
1273     goto error;
1274
1275   if (height > fsm->rp.size[V])
1276     {
1277       double scale = fsm->rp.size[V] / (double) height;
1278       width *= scale;
1279       height *= scale;
1280       if (!width || !height)
1281         goto error;
1282
1283       cairo_scale (fsm->cairo, scale, scale);
1284     }
1285
1286   if (width > fsm->rp.size[H])
1287     {
1288       double scale = fsm->rp.size[H] / (double) width;
1289       width *= scale;
1290       height *= scale;
1291       if (!width || !height)
1292         goto error;
1293
1294       cairo_scale (fsm->cairo, scale, scale);
1295     }
1296
1297   if (space < height)
1298     return 0;
1299
1300   draw_image (image, fsm->cairo);
1301   fsm->done = true;
1302   return height;
1303
1304 error:
1305   fsm->done = true;
1306   return 0;
1307 }
1308
1309 static int
1310 xr_fsm_draw_page_break (struct xr_fsm *fsm, int space)
1311 {
1312   if (space >= fsm->rp.size[V])
1313     fsm->done = true;
1314   return 0;
1315 }
1316
1317 int
1318 xr_fsm_draw_slice (struct xr_fsm *fsm, cairo_t *cr, int space)
1319 {
1320   assert (fsm->print);
1321
1322   if (fsm->done || space <= 0)
1323     return 0;
1324
1325   cairo_save (cr);
1326   fsm->cairo = cr;
1327   int used;
1328   switch (fsm->item->type)
1329     {
1330     case OUTPUT_ITEM_CHART:
1331       used = xr_fsm_draw_chart (fsm, space);
1332       break;
1333
1334     case OUTPUT_ITEM_IMAGE:
1335       used = xr_fsm_draw_image (fsm, space);
1336       break;
1337
1338     case OUTPUT_ITEM_PAGE_BREAK:
1339       used = xr_fsm_draw_page_break (fsm, space);
1340       break;
1341
1342     case OUTPUT_ITEM_TABLE:
1343       used = xr_fsm_draw_table (fsm, space);
1344       break;
1345
1346     case OUTPUT_ITEM_GROUP:
1347     case OUTPUT_ITEM_MESSAGE:
1348     case OUTPUT_ITEM_PAGE_SETUP:
1349     case OUTPUT_ITEM_TEXT:
1350     default:
1351       NOT_REACHED ();
1352     }
1353   fsm->cairo = NULL;
1354   cairo_restore (cr);
1355
1356   return used;
1357 }
1358
1359 bool
1360 xr_fsm_is_empty (const struct xr_fsm *fsm)
1361 {
1362   assert (fsm->print);
1363
1364   return fsm->done;
1365 }