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