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