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