work on docs
[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_ex *ex = pivot_value_ex (cell->value);
746   if (ex->n_footnotes || ex->n_subscripts)
747     {
748       size_t subscript_ofs = ds_length (&body);
749       for (size_t i = 0; i < ex->n_subscripts; i++)
750         {
751           if (i)
752             ds_put_byte (&body, ',');
753           ds_put_cstr (&body, ex->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 < ex->n_footnotes; i++)
759         {
760           const struct pivot_footnote *f
761             = pt->footnotes[ex->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 (ex->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 (ex->n_subscripts)
808         add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
809                   footnote_ofs - subscript_ofs);
810       if (ex->n_footnotes)
811         {
812           bool superscript = pt->look->footnote_marker_superscripts;
813           add_attr (attrs, pango_attr_rise_new (superscript ? 3000 : -3000),
814                     footnote_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
815         }
816     }
817
818   /* Set the attributes, if any. */
819   if (attrs)
820     {
821       pango_layout_set_attributes (layout, attrs);
822       pango_attr_list_unref (attrs);
823     }
824
825   /* Set the text. */
826   pango_layout_set_text (layout, ds_cstr (&body), ds_length (&body));
827
828   pango_layout_set_alignment (layout,
829                               (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
830                                : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
831                                : PANGO_ALIGN_CENTER));
832   pango_layout_set_width (
833     layout,
834     bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
835   pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
836
837   int size[TABLE_N_AXES];
838   pango_layout_get_size (layout, &size[H], &size[V]);
839
840   if (clip[H][0] != clip[H][1])
841     {
842       cairo_save (xr->cairo);
843       if (!(options & TAB_ROTATE))
844         xr_clip (xr, clip);
845       if (options & TAB_ROTATE)
846         {
847           int extra = bb[H][1] - bb[H][0] - size[V];
848           int halign_offset = extra > 0 ? extra / 2 : 0;
849           cairo_translate (xr->cairo,
850                            xr_to_pt (bb[H][0] + halign_offset),
851                            xr_to_pt (bb[V][1]));
852           cairo_rotate (xr->cairo, -M_PI_2);
853         }
854       else
855         cairo_translate (xr->cairo,
856                          xr_to_pt (bb[H][0]),
857                          xr_to_pt (bb[V][0]));
858       pango_cairo_show_layout (xr->cairo, layout);
859
860       /* If enabled, this draws a blue rectangle around the extents of each
861          line of text, which can be rather useful for debugging layout
862          issues. */
863       if (0)
864         {
865           PangoLayoutIter *iter;
866           iter = pango_layout_get_iter (layout);
867           do
868             {
869               PangoRectangle extents;
870
871               pango_layout_iter_get_line_extents (iter, &extents, NULL);
872               cairo_save (xr->cairo);
873               cairo_set_source_rgb (xr->cairo, 1, 0, 0);
874               xr_draw_rectangle (
875                 xr,
876                 pango_to_xr (extents.x),
877                 pango_to_xr (extents.y),
878                 pango_to_xr (extents.x + extents.width),
879                 pango_to_xr (extents.y + extents.height));
880               cairo_restore (xr->cairo);
881             }
882           while (pango_layout_iter_next_line (iter));
883           pango_layout_iter_free (iter);
884         }
885
886       cairo_restore (xr->cairo);
887     }
888
889   int w = pango_to_xr (size[X]);
890   int h = pango_to_xr (size[Y]);
891   if (w > *widthp)
892     *widthp = w;
893   if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
894     {
895       PangoLayoutIter *iter;
896       int best = 0;
897
898       /* Choose a breakpoint between lines instead of in the middle of one. */
899       iter = pango_layout_get_iter (layout);
900       do
901         {
902           PangoRectangle extents;
903           int y0, y1;
904           int bottom;
905
906           pango_layout_iter_get_line_extents (iter, NULL, &extents);
907           pango_layout_iter_get_line_yrange (iter, &y0, &y1);
908           extents.x = pango_to_xr (extents.x);
909           extents.y = pango_to_xr (y0);
910           extents.width = pango_to_xr (extents.width);
911           extents.height = pango_to_xr (y1 - y0);
912           bottom = bb[V][0] + extents.y + extents.height;
913           if (bottom < bb[V][1])
914             {
915               if (brk && clip[H][0] != clip[H][1])
916                 best = bottom;
917               if (brk)
918                 *brk = bottom;
919             }
920           else
921             break;
922         }
923       while (pango_layout_iter_next_line (iter));
924       pango_layout_iter_free (iter);
925
926       /* If enabled, draws a green line across the chosen breakpoint, which can
927          be useful for debugging issues with breaking.  */
928       if (0)
929         {
930           if (best)
931             xr_draw_line (xr, 0, best,
932                           xr->style->size[H], best,
933                           RENDER_LINE_SINGLE,
934                           &(struct cell_color) CELL_COLOR (0, 255, 0));
935         }
936     }
937
938   pango_layout_set_attributes (layout, NULL);
939
940   if (desc != xr->style->font)
941     pango_font_description_free (desc);
942   g_object_unref (G_OBJECT (layout));
943   ds_destroy (&body);
944
945   return h;
946 }
947
948 static void
949 xr_layout_cell (struct xr_fsm *xr, const struct table_cell *cell,
950                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
951                 int *width, int *height, int *brk)
952 {
953   *width = 0;
954   *height = 0;
955
956   /* If enabled, draws a blue rectangle around the cell extents, which can be
957      useful for debugging layout. */
958   if (0)
959     {
960       if (clip[H][0] != clip[H][1])
961         {
962           cairo_save (xr->cairo);
963           cairo_set_source_rgb (xr->cairo, 0, 0, 1);
964           xr_draw_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
965           cairo_restore (xr->cairo);
966         }
967     }
968
969   if (brk)
970     *brk = bb[V][0];
971   *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
972 }
973
974 #define CHART_WIDTH 500
975 #define CHART_HEIGHT 375
976
977 static struct xr_fsm *
978 xr_fsm_create (const struct output_item *item_,
979                const struct xr_fsm_style *style, cairo_t *cr,
980                bool print)
981 {
982   struct output_item *item;
983
984   switch (item_->type)
985     {
986     case OUTPUT_ITEM_CHART:
987     case OUTPUT_ITEM_IMAGE:
988     case OUTPUT_ITEM_PAGE_BREAK:
989     case OUTPUT_ITEM_TABLE:
990       item = output_item_ref (item_);
991       break;
992
993     case OUTPUT_ITEM_GROUP:
994       return NULL;
995
996     case OUTPUT_ITEM_MESSAGE:
997       item = text_item_to_table_item (message_item_to_text_item (
998                                         output_item_ref (item_)));
999       break;
1000
1001     case OUTPUT_ITEM_TEXT:
1002       if (item_->text.subtype == TEXT_ITEM_PAGE_TITLE)
1003         return NULL;
1004
1005       item = text_item_to_table_item (output_item_ref (item_));
1006       break;
1007
1008     default:
1009       NOT_REACHED ();
1010     }
1011
1012   assert (item->type == OUTPUT_ITEM_TABLE
1013           || item->type == OUTPUT_ITEM_CHART
1014           || item->type == OUTPUT_ITEM_IMAGE
1015           || item->type == OUTPUT_ITEM_PAGE_BREAK);
1016
1017   size_t *layer_indexes = NULL;
1018   if (item->type == OUTPUT_ITEM_TABLE)
1019     {
1020       layer_indexes = pivot_output_next_layer (item->table, NULL, print);
1021       if (!layer_indexes)
1022         return NULL;
1023     }
1024
1025   static const struct render_ops xrr_render_ops = {
1026     .measure_cell_width = xrr_measure_cell_width,
1027     .measure_cell_height = xrr_measure_cell_height,
1028     .adjust_break = xrr_adjust_break,
1029     .draw_line = xrr_draw_line,
1030     .draw_cell = xrr_draw_cell,
1031     .scale = xrr_scale,
1032   };
1033
1034   enum { LW = XR_LINE_WIDTH, LS = XR_LINE_SPACE };
1035   static const int xr_line_widths[RENDER_N_LINES] =
1036     {
1037       [RENDER_LINE_NONE] = 0,
1038       [RENDER_LINE_SINGLE] = LW,
1039       [RENDER_LINE_DASHED] = LW,
1040       [RENDER_LINE_THICK] = LW * 2,
1041       [RENDER_LINE_THIN] = LW / 2,
1042       [RENDER_LINE_DOUBLE] = 2 * LW + LS,
1043     };
1044
1045   struct xr_fsm *fsm = xmalloc (sizeof *fsm);
1046   *fsm = (struct xr_fsm) {
1047     .style = xr_fsm_style_ref (style),
1048     .item = item,
1049     .print = print,
1050     .layer_indexes = layer_indexes,
1051     .rp = {
1052       .ops = &xrr_render_ops,
1053       .aux = fsm,
1054       .size = { [H] = style->size[H], [V] = style->size[V] },
1055       .line_widths = xr_line_widths,
1056       .min_break = { [H] = style->min_break[H], [V] = style->min_break[V] },
1057       .supports_margins = true,
1058       .rtl = render_direction_rtl (),
1059       .printing = print,
1060     }
1061   };
1062
1063   /* Get font size. */
1064   PangoContext *context = pango_cairo_create_context (cr);
1065   pango_cairo_context_set_resolution (context, style->font_resolution);
1066   PangoLayout *layout = pango_layout_new (context);
1067   g_object_unref (context);
1068
1069   pango_layout_set_font_description (layout, style->font);
1070
1071   pango_layout_set_text (layout, "0", 1);
1072
1073   int char_size[TABLE_N_AXES];
1074   pango_layout_get_size (layout, &char_size[H], &char_size[V]);
1075   for (int a = 0; a < TABLE_N_AXES; a++)
1076     {
1077       int csa = pango_to_xr (char_size[a]);
1078       fsm->rp.font_size[a] = MAX (fsm->rp.font_size[a], csa);
1079     }
1080
1081   g_object_unref (G_OBJECT (layout));
1082
1083   if (item->type == OUTPUT_ITEM_TABLE)
1084     {
1085       fsm->cairo = cr;
1086       fsm->p = render_pager_create (&fsm->rp, item->table, fsm->layer_indexes);
1087       fsm->cairo = NULL;
1088     }
1089
1090   return fsm;
1091 }
1092
1093 struct xr_fsm *
1094 xr_fsm_create_for_printing (const struct output_item *item,
1095                             const struct xr_fsm_style *style, cairo_t *cr)
1096 {
1097   return xr_fsm_create (item, style, cr, true);
1098 }
1099
1100 void
1101 xr_fsm_destroy (struct xr_fsm *fsm)
1102 {
1103   if (fsm)
1104     {
1105       xr_fsm_style_unref (fsm->style);
1106       output_item_unref (fsm->item);
1107       free (fsm->layer_indexes);
1108       render_pager_destroy (fsm->p);
1109       assert (!fsm->cairo);
1110       free (fsm);
1111     }
1112 }
1113 \f
1114 /* Scrolling API. */
1115
1116 struct xr_fsm *
1117 xr_fsm_create_for_scrolling (const struct output_item *item,
1118                              const struct xr_fsm_style *style, cairo_t *cr)
1119 {
1120   return xr_fsm_create (item, style, cr, false);
1121 }
1122
1123 void
1124 xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp)
1125 {
1126   assert (!fsm->print);
1127
1128   int w, h;
1129
1130   switch (fsm->item->type)
1131     {
1132     case OUTPUT_ITEM_CHART:
1133       w = CHART_WIDTH;
1134       h = CHART_HEIGHT;
1135       break;
1136
1137     case OUTPUT_ITEM_IMAGE:
1138       w = cairo_image_surface_get_width (fsm->item->image);
1139       h = cairo_image_surface_get_height (fsm->item->image);
1140       break;
1141
1142     case OUTPUT_ITEM_TABLE:
1143       fsm->cairo = cr;
1144       w = render_pager_get_size (fsm->p, H) / XR_POINT;
1145       h = render_pager_get_size (fsm->p, V) / XR_POINT;
1146       fsm->cairo = NULL;
1147       break;
1148
1149     case OUTPUT_ITEM_GROUP:
1150     case OUTPUT_ITEM_MESSAGE:
1151     case OUTPUT_ITEM_PAGE_BREAK:
1152     case OUTPUT_ITEM_TEXT:
1153     default:
1154       NOT_REACHED ();
1155     }
1156
1157   if (wp)
1158     *wp = w;
1159   if (hp)
1160     *hp = h;
1161 }
1162
1163 void
1164 xr_fsm_draw_all (struct xr_fsm *fsm, cairo_t *cr)
1165 {
1166   assert (!fsm->print);
1167   xr_fsm_draw_region (fsm, cr, 0, 0, INT_MAX, INT_MAX);
1168 }
1169
1170 static int
1171 mul_XR_POINT (int x)
1172 {
1173   return (x >= INT_MAX / XR_POINT ? INT_MAX
1174           : x <= INT_MIN / XR_POINT ? INT_MIN
1175           : x * XR_POINT);
1176 }
1177
1178 static void
1179 draw_image (cairo_surface_t *image, cairo_t *cr)
1180 {
1181   cairo_save (cr);
1182   cairo_set_source_surface (cr, image, 0, 0);
1183   cairo_rectangle (cr, 0, 0, cairo_image_surface_get_width (image),
1184                    cairo_image_surface_get_height (image));
1185   cairo_clip (cr);
1186   cairo_paint (cr);
1187   cairo_restore (cr);
1188 }
1189
1190 void
1191 xr_fsm_draw_region (struct xr_fsm *fsm, cairo_t *cr,
1192                     int x, int y, int w, int h)
1193 {
1194   assert (!fsm->print);
1195   switch (fsm->item->type)
1196     {
1197     case OUTPUT_ITEM_CHART:
1198       xr_draw_chart (fsm->item->chart, cr, CHART_WIDTH, CHART_HEIGHT);
1199       break;
1200
1201     case OUTPUT_ITEM_IMAGE:
1202       draw_image (fsm->item->image, cr);
1203       break;
1204
1205     case OUTPUT_ITEM_TABLE:
1206       fsm->cairo = cr;
1207       render_pager_draw_region (fsm->p, mul_XR_POINT (x), mul_XR_POINT (y),
1208                                 mul_XR_POINT (w), mul_XR_POINT (h));
1209       fsm->cairo = NULL;
1210       break;
1211
1212     case OUTPUT_ITEM_GROUP:
1213     case OUTPUT_ITEM_MESSAGE:
1214     case OUTPUT_ITEM_PAGE_BREAK:
1215     case OUTPUT_ITEM_TEXT:
1216       NOT_REACHED ();
1217     }
1218 }
1219 \f
1220 /* Printing API. */
1221
1222 static int
1223 xr_fsm_draw_table (struct xr_fsm *fsm, int space)
1224 {
1225   int used = render_pager_draw_next (fsm->p, space);
1226   if (!render_pager_has_next (fsm->p))
1227     {
1228       render_pager_destroy (fsm->p);
1229
1230       fsm->layer_indexes = pivot_output_next_layer (fsm->item->table,
1231                                                     fsm->layer_indexes, true);
1232       if (fsm->layer_indexes)
1233         {
1234           fsm->p = render_pager_create (&fsm->rp, fsm->item->table,
1235                                         fsm->layer_indexes);
1236           if (fsm->item->table->look->paginate_layers)
1237             used = space;
1238           else
1239             used += fsm->style->object_spacing;
1240         }
1241       else
1242         {
1243           fsm->p = NULL;
1244           fsm->done = true;
1245         }
1246     }
1247   return MIN (used, space);
1248 }
1249
1250 static int
1251 xr_fsm_draw_chart (struct xr_fsm *fsm, int space)
1252 {
1253   const int chart_height = 0.8 * MIN (fsm->rp.size[H], fsm->rp.size[V]);
1254   if (space < chart_height)
1255     return 0;
1256
1257   fsm->done = true;
1258   xr_draw_chart (fsm->item->chart, fsm->cairo,
1259                  xr_to_pt (fsm->rp.size[H]), xr_to_pt (chart_height));
1260   return chart_height;
1261 }
1262
1263 static int
1264 xr_fsm_draw_image (struct xr_fsm *fsm, int space)
1265 {
1266   cairo_surface_t *image = fsm->item->image;
1267   int width = cairo_image_surface_get_width (image) * XR_POINT;
1268   int height = cairo_image_surface_get_height (image) * XR_POINT;
1269   if (!width || !height)
1270     goto error;
1271
1272   if (height > fsm->rp.size[V])
1273     {
1274       double scale = fsm->rp.size[V] / (double) height;
1275       width *= scale;
1276       height *= scale;
1277       if (!width || !height)
1278         goto error;
1279
1280       cairo_scale (fsm->cairo, scale, scale);
1281     }
1282
1283   if (width > fsm->rp.size[H])
1284     {
1285       double scale = fsm->rp.size[H] / (double) width;
1286       width *= scale;
1287       height *= scale;
1288       if (!width || !height)
1289         goto error;
1290
1291       cairo_scale (fsm->cairo, scale, scale);
1292     }
1293
1294   if (space < height)
1295     return 0;
1296
1297   draw_image (image, fsm->cairo);
1298   fsm->done = true;
1299   return height;
1300
1301 error:
1302   fsm->done = true;
1303   return 0;
1304 }
1305
1306 static int
1307 xr_fsm_draw_page_break (struct xr_fsm *fsm, int space)
1308 {
1309   if (space >= fsm->rp.size[V])
1310     fsm->done = true;
1311   return 0;
1312 }
1313
1314 int
1315 xr_fsm_draw_slice (struct xr_fsm *fsm, cairo_t *cr, int space)
1316 {
1317   assert (fsm->print);
1318
1319   if (fsm->done || space <= 0)
1320     return 0;
1321
1322   cairo_save (cr);
1323   fsm->cairo = cr;
1324   int used;
1325   switch (fsm->item->type)
1326     {
1327     case OUTPUT_ITEM_CHART:
1328       used = xr_fsm_draw_chart (fsm, space);
1329       break;
1330
1331     case OUTPUT_ITEM_IMAGE:
1332       used = xr_fsm_draw_image (fsm, space);
1333       break;
1334
1335     case OUTPUT_ITEM_PAGE_BREAK:
1336       used = xr_fsm_draw_page_break (fsm, space);
1337       break;
1338
1339     case OUTPUT_ITEM_TABLE:
1340       used = xr_fsm_draw_table (fsm, space);
1341       break;
1342
1343     case OUTPUT_ITEM_GROUP:
1344     case OUTPUT_ITEM_MESSAGE:
1345     case OUTPUT_ITEM_TEXT:
1346     default:
1347       NOT_REACHED ();
1348     }
1349   fsm->cairo = NULL;
1350   cairo_restore (cr);
1351
1352   return used;
1353 }
1354
1355 bool
1356 xr_fsm_is_empty (const struct xr_fsm *fsm)
1357 {
1358   assert (fsm->print);
1359
1360   return fsm->done;
1361 }