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