1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "output/cairo-fsm.h"
22 #include <pango/pango-layout.h>
23 #include <pango/pango.h>
24 #include <pango/pangocairo.h>
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"
44 #include "gl/c-ctype.h"
45 #include "gl/c-strcase.h"
46 #include "gl/xalloc.h"
48 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
53 xr_fsm_style_ref (const struct xr_fsm_style *style_)
55 assert (style_->ref_cnt > 0);
57 struct xr_fsm_style *style = CONST_CAST (struct xr_fsm_style *, style_);
63 xr_fsm_style_unshare (struct xr_fsm_style *old)
65 assert (old->ref_cnt > 0);
66 if (old->ref_cnt == 1)
69 xr_fsm_style_unref (old);
71 struct xr_fsm_style *new = xmemdup (old, sizeof *old);
74 new->font = pango_font_description_copy (old->font);
80 xr_fsm_style_unref (struct xr_fsm_style *style)
84 assert (style->ref_cnt > 0);
85 if (!--style->ref_cnt)
87 pango_font_description_free (style->font);
94 xr_fsm_style_equals (const struct xr_fsm_style *a,
95 const struct xr_fsm_style *b)
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)
110 /* Renders a single output_item to an output device in one of two ways:
112 - 'print == true': Broken across multiple pages if necessary.
114 - 'print == false': In a single region that the user may scroll around if
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.
125 struct xr_fsm_style *style;
126 struct output_item *item;
129 /* Print mode only. */
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?! */
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
143 /* Conversions to and from points. */
147 return x / (double) XR_POINT;
150 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
154 return x * (PANGO_SCALE * 72 / 96);
158 pango_to_xr (int pango)
160 return (XR_POINT != PANGO_SCALE
161 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
168 return (XR_POINT != PANGO_SCALE
169 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
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. */
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);
183 xr_set_source_rgba (cairo_t *cairo, const struct cell_color color)
185 cairo_set_source_rgba (cairo,
186 color.r / 255., color.g / 255., color.b / 255.,
191 xr_draw_line (struct xr_fsm *xr, int x0, int y0, int x1, int y1, int style,
192 const struct cell_color color)
194 cairo_new_path (xr->cairo);
195 cairo_set_line_width (
197 xr_to_pt (style == TABLE_STROKE_THICK ? XR_LINE_WIDTH * 2
198 : style == TABLE_STROKE_THIN ? XR_LINE_WIDTH / 2
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));
203 if (!xr->style->use_system_colors)
204 xr_set_source_rgba (xr->cairo, color);
205 if (style == TABLE_STROKE_DASHED)
206 cairo_set_dash (xr->cairo, (double[]) { 2 }, 1, 0);
207 cairo_stroke (xr->cairo);
208 if (style == TABLE_STROKE_DASHED)
209 cairo_set_dash (xr->cairo, NULL, 0, 0);
213 xr_draw_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
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));
226 fill_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
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);
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. */
241 xr_draw_horz_line (struct xr_fsm *xr, int x0, int x1, int x2, int x3, int y,
242 enum table_stroke left, enum table_stroke right,
243 const struct cell_color left_color,
244 const struct cell_color right_color,
247 if (left != TABLE_STROKE_NONE && right != TABLE_STROKE_NONE && !shorten
248 && cell_color_equal (left_color, right_color))
249 xr_draw_line (xr, x0, y, x3, y, left, left_color);
252 if (left != TABLE_STROKE_NONE)
253 xr_draw_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
254 if (right != TABLE_STROKE_NONE)
255 xr_draw_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
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. */
264 xr_draw_vert_line (struct xr_fsm *xr, int y0, int y1, int y2, int y3, int x,
265 enum table_stroke top, enum table_stroke bottom,
266 const struct cell_color top_color,
267 const struct cell_color bottom_color,
270 if (top != TABLE_STROKE_NONE && bottom != TABLE_STROKE_NONE && !shorten
271 && cell_color_equal (top_color, bottom_color))
272 xr_draw_line (xr, x, y0, x, y3, top, top_color);
275 if (top != TABLE_STROKE_NONE)
276 xr_draw_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
277 if (bottom != TABLE_STROKE_NONE)
278 xr_draw_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
283 xrr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
284 const struct table_border_style styles[TABLE_N_AXES][2])
286 const int x0 = bb[H][0];
287 const int y0 = bb[V][0];
288 const int x3 = bb[H][1];
289 const int y3 = bb[V][1];
290 const enum table_stroke top = styles[H][0].stroke;
291 const enum table_stroke bottom = styles[H][1].stroke;
293 int start_side = render_direction_rtl();
294 int end_side = !start_side;
295 const int start_of_line = styles[V][start_side].stroke;
296 const int end_of_line = styles[V][end_side].stroke;
297 const struct cell_color top_color = styles[H][0].color;
298 const struct cell_color bottom_color = styles[H][1].color;
299 const struct cell_color start_color = styles[V][start_side].color;
300 const struct cell_color end_color = styles[V][end_side].color;
302 /* The algorithm here is somewhat subtle, to allow it to handle
303 all the kinds of intersections that we need.
305 Three additional ordinates are assigned along the x axis. The
306 first is xc, midway between x0 and x3. The others are x1 and
307 x2; for a single vertical line these are equal to xc, and for
308 a double vertical line they are the ordinates of the left and
309 right half of the double line.
311 yc, y1, and y2 are assigned similarly along the y axis.
313 The following diagram shows the coordinate system and output
314 for double top and bottom lines, single left line, and no
318 y0 ________________________
324 y1 = y2 = yc |######### # |
329 y3 |________#_____#_______|
331 struct xr_fsm *xr = xr_;
333 /* Offset from center of each line in a pair of double lines. */
334 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
336 /* Are the lines along each axis single or double?
337 (It doesn't make sense to have different kinds of line on the
338 same axis, so we don't try to gracefully handle that case.) */
339 bool double_vert = top == TABLE_STROKE_DOUBLE || bottom == TABLE_STROKE_DOUBLE;
340 bool double_horz = start_of_line == TABLE_STROKE_DOUBLE || end_of_line == TABLE_STROKE_DOUBLE;
342 /* When horizontal lines are doubled,
343 the left-side line along y1 normally runs from x0 to x2,
344 and the right-side line along y1 from x3 to x1.
345 If the top-side line is also doubled, we shorten the y1 lines,
346 so that the left-side line runs only to x1,
347 and the right-side line only to x2.
348 Otherwise, the horizontal line at y = y1 below would cut off
349 the intersection, which looks ugly:
351 y0 ________________________
356 y1 |######### ########|
359 y2 |######################|
362 y3 |______________________|
363 It is more of a judgment call when the horizontal line is
364 single. We actually choose to cut off the line anyhow, as
365 shown in the first diagram above.
367 bool shorten_y1_lines = top == TABLE_STROKE_DOUBLE;
368 bool shorten_y2_lines = bottom == TABLE_STROKE_DOUBLE;
369 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
370 int horz_line_ofs = double_vert ? double_line_ofs : 0;
371 int xc = (x0 + x3) / 2;
372 int x1 = xc - horz_line_ofs;
373 int x2 = xc + horz_line_ofs;
375 bool shorten_x1_lines = start_of_line == TABLE_STROKE_DOUBLE;
376 bool shorten_x2_lines = end_of_line == TABLE_STROKE_DOUBLE;
377 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
378 int vert_line_ofs = double_horz ? double_line_ofs : 0;
379 int yc = (y0 + y3) / 2;
380 int y1 = yc - vert_line_ofs;
381 int y2 = yc + vert_line_ofs;
384 xr_draw_horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
385 start_color, end_color, shorten_yc_line);
388 xr_draw_horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
389 start_color, end_color, shorten_y1_lines);
390 xr_draw_horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
391 start_color, end_color, shorten_y2_lines);
395 xr_draw_vert_line (xr, y0, y1, y2, y3, xc, top, bottom,
396 top_color, bottom_color, shorten_xc_line);
399 xr_draw_vert_line (xr, y0, y1, y2, y3, x1, top, bottom,
400 top_color, bottom_color, shorten_x1_lines);
401 xr_draw_vert_line (xr, y0, y1, y2, y3, x2, top, bottom,
402 top_color, bottom_color, shorten_x2_lines);
407 xrr_measure_cell_width (void *xr_, const struct table_cell *cell,
408 int *min_width, int *max_width)
410 if (cell->options & TABLE_CELL_PADDING)
412 *min_width = *max_width = XR_LINE_WIDTH;
416 struct xr_fsm *xr = xr_;
417 int bb[TABLE_N_AXES][2];
418 int clip[TABLE_N_AXES][2];
425 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
426 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
429 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
431 const int (*margin)[2] = cell->cell_style->margin;
433 *min_width += px_to_xr (margin[H][0] + margin[H][1]);
435 *max_width += px_to_xr (margin[H][0] + margin[H][1]);
439 xrr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
441 if (cell->options & TABLE_CELL_PADDING)
442 return XR_LINE_WIDTH;
444 struct xr_fsm *xr = xr_;
446 const int (*margin)[2] = cell->cell_style->margin;
448 int bb[TABLE_N_AXES][2] = {
450 [H][1] = width - px_to_xr (margin[H][0] + margin[H][1]),
455 int clip[TABLE_N_AXES][2] = { { 0, 0 }, { 0, 0 } };
458 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
459 h += px_to_xr (margin[V][0] + margin[V][1]);
463 static void xr_clip (struct xr_fsm *, int clip[TABLE_N_AXES][2]);
466 xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
467 int bb[TABLE_N_AXES][2], int valign_offset,
468 int spill[TABLE_N_AXES][2],
469 int clip[TABLE_N_AXES][2])
471 struct xr_fsm *xr = xr_;
474 const struct cell_color bg = cell->font_style->bg[color_idx];
475 if ((bg.r != 255 || bg.g != 255 || bg.b != 255) && bg.alpha)
477 cairo_save (xr->cairo);
478 int bg_clip[TABLE_N_AXES][2];
479 for (int axis = 0; axis < TABLE_N_AXES; axis++)
481 bg_clip[axis][0] = clip[axis][0];
482 if (bb[axis][0] == clip[axis][0])
483 bg_clip[axis][0] -= spill[axis][0];
485 bg_clip[axis][1] = clip[axis][1];
486 if (bb[axis][1] == clip[axis][1])
487 bg_clip[axis][1] += spill[axis][1];
489 xr_clip (xr, bg_clip);
490 xr_set_source_rgba (xr->cairo, bg);
492 bb[H][0] - spill[H][0],
493 bb[V][0] - spill[V][0],
494 bb[H][1] + spill[H][1],
495 bb[V][1] + spill[V][1]);
496 cairo_restore (xr->cairo);
498 cairo_save (xr->cairo);
499 if (!xr->style->use_system_colors)
500 xr_set_source_rgba (xr->cairo, cell->font_style->fg[color_idx]);
502 bb[V][0] += valign_offset;
504 for (int axis = 0; axis < TABLE_N_AXES; axis++)
506 bb[axis][0] += px_to_xr (cell->cell_style->margin[axis][0]);
507 bb[axis][1] -= px_to_xr (cell->cell_style->margin[axis][1]);
509 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
510 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
511 cairo_restore (xr->cairo);
515 xrr_adjust_break (void *xr_, const struct table_cell *cell,
516 int width, int height)
518 struct xr_fsm *xr = xr_;
520 if (xrr_measure_cell_height (xr_, cell, width) < height)
523 const int (*margin)[2] = cell->cell_style->margin;
525 int bb[TABLE_N_AXES][2] = {
528 [H][1] = width - px_to_xr (margin[H][0] + margin[H][1]),
529 [V][1] = height - px_to_xr (margin[V][0] + margin[V][1]),
534 int clip[TABLE_N_AXES][2] = { { 0, 0 }, { 0, 0 } };
537 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
542 xrr_scale (void *xr_, double scale)
544 struct xr_fsm *xr = xr_;
545 cairo_scale (xr->cairo, scale, scale);
549 xr_clip (struct xr_fsm *xr, int clip[TABLE_N_AXES][2])
551 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
553 double x0 = xr_to_pt (clip[H][0]);
554 double y0 = xr_to_pt (clip[V][0]);
555 double x1 = xr_to_pt (clip[H][1]);
556 double y1 = xr_to_pt (clip[V][1]);
558 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
559 cairo_clip (xr->cairo);
564 add_attr (PangoAttrList *list, PangoAttribute *attr,
565 guint start_index, guint end_index)
567 attr->start_index = start_index;
568 attr->end_index = end_index;
569 pango_attr_list_insert (list, attr);
573 markup_escape (struct string *out, bool markup, const char *in, size_t len)
577 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
589 ds_put_cstr (out, "&");
592 ds_put_cstr (out, "<");
595 ds_put_cstr (out, ">");
598 ds_put_byte (out, c);
605 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
607 int size[TABLE_N_AXES];
608 pango_layout_get_size (layout, &size[H], &size[V]);
612 static PangoFontDescription *
613 parse_font (const char *font, int default_size, bool bold, bool italic)
615 if (!c_strcasecmp (font, "Monospaced"))
618 PangoFontDescription *desc = pango_font_description_from_string (font);
622 /* If the font description didn't include an explicit font size, then set it
623 to DEFAULT_SIZE, which is in inch/72000 units. */
624 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
625 pango_font_description_set_size (desc,
626 (default_size / 1000.0) * PANGO_SCALE);
628 pango_font_description_set_weight (desc, (bold
630 : PANGO_WEIGHT_NORMAL));
631 pango_font_description_set_style (desc, (italic
633 : PANGO_STYLE_NORMAL));
639 xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell,
640 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
641 int *widthp, int *brk)
643 const struct pivot_table *pt = xr->item->table;
644 const struct font_style *font_style = cell->font_style;
645 const struct cell_style *cell_style = cell->cell_style;
646 unsigned int options = cell->options;
648 enum table_axis X = options & TABLE_CELL_ROTATE ? V : H;
649 enum table_axis Y = !X;
650 int R = options & TABLE_CELL_ROTATE ? 0 : 1;
652 PangoFontDescription *desc = NULL;
653 if (font_style->typeface)
655 font_style->typeface,
656 font_style->size ? font_style->size * 1000 : 10000,
657 font_style->bold, font_style->italic);
659 desc = xr->style->font;
662 PangoContext *context = pango_cairo_create_context (xr->cairo);
663 pango_cairo_context_set_resolution (context, xr->style->font_resolution);
664 PangoLayout *layout = pango_layout_new (context);
665 g_object_unref (context);
667 pango_layout_set_font_description (layout, desc);
669 struct string body = DS_EMPTY_INITIALIZER;
670 bool numeric = pivot_value_format_body (cell->value, pt, &body);
672 enum table_halign halign = table_halign_interpret (
673 cell->cell_style->halign, numeric);
675 if (cell_style->halign == TABLE_HALIGN_DECIMAL
676 && !(cell->options & TABLE_CELL_ROTATE))
678 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
680 const char *decimal = strrchr (ds_cstr (&body),
681 cell_style->decimal_char);
684 pango_layout_set_text (layout, decimal, strlen (decimal));
685 pango_layout_set_width (layout, -1);
686 margin_adjustment += get_layout_dimension (layout, H);
689 if (margin_adjustment < 0)
690 bb[H][1] += margin_adjustment;
693 PangoAttrList *attrs = NULL;
695 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
696 Pango's implementation of it): it will break after a period or a comma
697 that precedes a digit, e.g. in ".000" it will break after the period.
698 This code looks for such a situation and inserts a U+2060 WORD JOINER
699 to prevent the break.
701 This isn't necessary when the decimal point is between two digits
702 (e.g. "0.000" won't be broken) or when the display width is not limited so
703 that word wrapping won't happen.
705 It isn't necessary to look for more than one period or comma, as would
706 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
707 are present then there will always be a digit on both sides of every
709 bool markup = cell->font_style->markup;
712 PangoAttrList *new_attrs;
714 if (pango_parse_markup (ds_cstr (&body), -1, 0,
715 &new_attrs, &new_text, NULL, NULL))
719 body.ss = ss_cstr (new_text);
720 body.capacity = body.ss.length;
724 /* XXX should we report the error? */
727 else if (options & TABLE_CELL_ROTATE || bb[H][1] != INT_MAX)
729 const char *text = ds_cstr (&body);
730 const char *decimal = text + strcspn (text, ".,");
732 && c_isdigit (decimal[1])
733 && (decimal == text || !c_isdigit (decimal[-1])))
735 struct string tmp = DS_EMPTY_INITIALIZER;
736 ds_extend (&tmp, ds_length (&body) + 16);
737 markup_escape (&tmp, markup, text, decimal - text + 1);
738 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
739 markup_escape (&tmp, markup, decimal + 1, -1);
740 ds_swap (&tmp, &body);
745 if (font_style->underline)
748 attrs = pango_attr_list_new ();
749 pango_attr_list_insert (attrs, pango_attr_underline_new (
750 PANGO_UNDERLINE_SINGLE));
753 const struct pivot_value_ex *ex = pivot_value_ex (cell->value);
754 if (ex->n_footnotes || ex->n_subscripts)
756 size_t subscript_ofs = ds_length (&body);
757 for (size_t i = 0; i < ex->n_subscripts; i++)
760 ds_put_byte (&body, ',');
761 ds_put_cstr (&body, ex->subscripts[i]);
764 size_t footnote_ofs = ds_length (&body);
765 size_t n_footnotes = 0;
766 for (size_t i = 0; i < ex->n_footnotes; i++)
768 const struct pivot_footnote *f
769 = pt->footnotes[ex->footnote_indexes[i]];
773 ds_put_byte (&body, ',');
774 pivot_footnote_format_marker (f, pt, &body);
778 /* Allow footnote markers to occupy the right margin. That way, numbers
779 in the column are still aligned. */
780 if (ex->n_footnotes && halign == TABLE_HALIGN_RIGHT)
782 /* Measure the width of the footnote marker, so we know how much we
783 need to make room for. */
784 pango_layout_set_text (layout, ds_cstr (&body) + footnote_ofs,
785 ds_length (&body) - footnote_ofs);
787 PangoAttrList *fn_attrs = pango_attr_list_new ();
788 pango_attr_list_insert (
789 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
790 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
791 pango_layout_set_attributes (layout, fn_attrs);
792 pango_attr_list_unref (fn_attrs);
793 int footnote_width = get_layout_dimension (layout, X);
795 /* Bound the adjustment by the width of the right margin. */
796 int right_margin = px_to_xr (cell_style->margin[X][R]);
797 int footnote_adjustment = MIN (footnote_width, right_margin);
799 /* Adjust the bounding box. */
800 if (options & TABLE_CELL_ROTATE)
801 footnote_adjustment = -footnote_adjustment;
802 bb[X][R] += footnote_adjustment;
805 pango_layout_set_attributes (layout, NULL);
808 /* Set attributes. */
810 attrs = pango_attr_list_new ();
811 add_attr (attrs, pango_attr_font_desc_new (desc), subscript_ofs,
812 PANGO_ATTR_INDEX_TO_TEXT_END);
813 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
814 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
815 if (ex->n_subscripts)
816 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
817 footnote_ofs - subscript_ofs);
820 bool superscript = pt->look->footnote_marker_superscripts;
821 add_attr (attrs, pango_attr_rise_new (superscript ? 3000 : -3000),
822 footnote_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
826 /* Set the attributes, if any. */
829 pango_layout_set_attributes (layout, attrs);
830 pango_attr_list_unref (attrs);
834 pango_layout_set_text (layout, ds_cstr (&body), ds_length (&body));
836 pango_layout_set_alignment (layout,
837 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
838 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
839 : PANGO_ALIGN_CENTER));
840 pango_layout_set_width (
842 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
843 pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
845 int size[TABLE_N_AXES];
846 pango_layout_get_size (layout, &size[H], &size[V]);
848 if (clip[H][0] != clip[H][1])
850 cairo_save (xr->cairo);
851 if (!(options & TABLE_CELL_ROTATE))
853 if (options & TABLE_CELL_ROTATE)
855 int extra = bb[H][1] - bb[H][0] - size[V];
856 int halign_offset = extra > 0 ? extra / 2 : 0;
857 cairo_translate (xr->cairo,
858 xr_to_pt (bb[H][0] + halign_offset),
859 xr_to_pt (bb[V][1]));
860 cairo_rotate (xr->cairo, -M_PI_2);
863 cairo_translate (xr->cairo,
865 xr_to_pt (bb[V][0]));
866 pango_cairo_show_layout (xr->cairo, layout);
868 /* If enabled, this draws a blue rectangle around the extents of each
869 line of text, which can be rather useful for debugging layout
873 PangoLayoutIter *iter;
874 iter = pango_layout_get_iter (layout);
877 PangoRectangle extents;
879 pango_layout_iter_get_line_extents (iter, &extents, NULL);
880 cairo_save (xr->cairo);
881 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
884 pango_to_xr (extents.x),
885 pango_to_xr (extents.y),
886 pango_to_xr (extents.x + extents.width),
887 pango_to_xr (extents.y + extents.height));
888 cairo_restore (xr->cairo);
890 while (pango_layout_iter_next_line (iter));
891 pango_layout_iter_free (iter);
894 cairo_restore (xr->cairo);
897 int w = pango_to_xr (size[X]);
898 int h = pango_to_xr (size[Y]);
901 if (bb[V][0] + h >= bb[V][1] && !(options & TABLE_CELL_ROTATE))
903 PangoLayoutIter *iter;
906 /* Choose a breakpoint between lines instead of in the middle of one. */
907 iter = pango_layout_get_iter (layout);
910 PangoRectangle extents;
914 pango_layout_iter_get_line_extents (iter, NULL, &extents);
915 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
916 extents.x = pango_to_xr (extents.x);
917 extents.y = pango_to_xr (y0);
918 extents.width = pango_to_xr (extents.width);
919 extents.height = pango_to_xr (y1 - y0);
920 bottom = bb[V][0] + extents.y + extents.height;
921 if (bottom < bb[V][1])
923 if (brk && clip[H][0] != clip[H][1])
931 while (pango_layout_iter_next_line (iter));
932 pango_layout_iter_free (iter);
934 /* If enabled, draws a green line across the chosen breakpoint, which can
935 be useful for debugging issues with breaking. */
939 xr_draw_line (xr, 0, best,
940 xr->style->size[H], best,
942 (struct cell_color) CELL_COLOR (0, 255, 0));
946 pango_layout_set_attributes (layout, NULL);
948 if (desc != xr->style->font)
949 pango_font_description_free (desc);
950 g_object_unref (G_OBJECT (layout));
957 xr_layout_cell (struct xr_fsm *xr, const struct table_cell *cell,
958 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
959 int *width, int *height, int *brk)
964 /* If enabled, draws a blue rectangle around the cell extents, which can be
965 useful for debugging layout. */
968 if (clip[H][0] != clip[H][1])
970 cairo_save (xr->cairo);
971 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
972 xr_draw_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
973 cairo_restore (xr->cairo);
979 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
982 #define CHART_WIDTH 500
983 #define CHART_HEIGHT 375
985 static struct xr_fsm *
986 xr_fsm_create (const struct output_item *item_,
987 const struct xr_fsm_style *style, cairo_t *cr,
990 struct output_item *item;
994 case OUTPUT_ITEM_CHART:
995 case OUTPUT_ITEM_IMAGE:
996 case OUTPUT_ITEM_PAGE_BREAK:
997 case OUTPUT_ITEM_TABLE:
998 item = output_item_ref (item_);
1001 case OUTPUT_ITEM_GROUP:
1004 case OUTPUT_ITEM_MESSAGE:
1005 item = text_item_to_table_item (message_item_to_text_item (
1006 output_item_ref (item_)));
1009 case OUTPUT_ITEM_TEXT:
1010 if (item_->text.subtype == TEXT_ITEM_PAGE_TITLE)
1013 item = text_item_to_table_item (output_item_ref (item_));
1020 assert (item->type == OUTPUT_ITEM_TABLE
1021 || item->type == OUTPUT_ITEM_CHART
1022 || item->type == OUTPUT_ITEM_IMAGE
1023 || item->type == OUTPUT_ITEM_PAGE_BREAK);
1025 size_t *layer_indexes = NULL;
1026 if (item->type == OUTPUT_ITEM_TABLE)
1028 layer_indexes = pivot_output_next_layer (item->table, NULL, print);
1033 static const struct render_ops xrr_render_ops = {
1034 .measure_cell_width = xrr_measure_cell_width,
1035 .measure_cell_height = xrr_measure_cell_height,
1036 .adjust_break = xrr_adjust_break,
1037 .draw_line = xrr_draw_line,
1038 .draw_cell = xrr_draw_cell,
1042 enum { LW = XR_LINE_WIDTH, LS = XR_LINE_SPACE };
1043 static const int xr_line_widths[TABLE_N_STROKES] =
1045 [TABLE_STROKE_NONE] = 0,
1046 [TABLE_STROKE_SOLID] = LW,
1047 [TABLE_STROKE_DASHED] = LW,
1048 [TABLE_STROKE_THICK] = LW * 2,
1049 [TABLE_STROKE_THIN] = LW / 2,
1050 [TABLE_STROKE_DOUBLE] = 2 * LW + LS,
1053 struct xr_fsm *fsm = xmalloc (sizeof *fsm);
1054 *fsm = (struct xr_fsm) {
1055 .style = xr_fsm_style_ref (style),
1058 .layer_indexes = layer_indexes,
1060 .ops = &xrr_render_ops,
1062 .size = { [H] = style->size[H], [V] = style->size[V] },
1063 .line_widths = xr_line_widths,
1064 .min_break = { [H] = style->min_break[H], [V] = style->min_break[V] },
1065 .supports_margins = true,
1066 .rtl = render_direction_rtl (),
1071 /* Get font size. */
1072 PangoContext *context = pango_cairo_create_context (cr);
1073 pango_cairo_context_set_resolution (context, style->font_resolution);
1074 PangoLayout *layout = pango_layout_new (context);
1075 g_object_unref (context);
1077 pango_layout_set_font_description (layout, style->font);
1079 pango_layout_set_text (layout, "0", 1);
1081 int char_size[TABLE_N_AXES];
1082 pango_layout_get_size (layout, &char_size[H], &char_size[V]);
1083 for (int a = 0; a < TABLE_N_AXES; a++)
1085 int csa = pango_to_xr (char_size[a]);
1086 fsm->rp.font_size[a] = MAX (fsm->rp.font_size[a], csa);
1089 g_object_unref (G_OBJECT (layout));
1091 if (item->type == OUTPUT_ITEM_TABLE)
1094 fsm->p = render_pager_create (&fsm->rp, item->table, fsm->layer_indexes);
1102 xr_fsm_create_for_printing (const struct output_item *item,
1103 const struct xr_fsm_style *style, cairo_t *cr)
1105 return xr_fsm_create (item, style, cr, true);
1109 xr_fsm_destroy (struct xr_fsm *fsm)
1113 xr_fsm_style_unref (fsm->style);
1114 output_item_unref (fsm->item);
1115 free (fsm->layer_indexes);
1116 render_pager_destroy (fsm->p);
1117 assert (!fsm->cairo);
1122 /* Scrolling API. */
1125 xr_fsm_create_for_scrolling (const struct output_item *item,
1126 const struct xr_fsm_style *style, cairo_t *cr)
1128 return xr_fsm_create (item, style, cr, false);
1132 xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp)
1134 assert (!fsm->print);
1138 switch (fsm->item->type)
1140 case OUTPUT_ITEM_CHART:
1145 case OUTPUT_ITEM_IMAGE:
1146 w = cairo_image_surface_get_width (fsm->item->image);
1147 h = cairo_image_surface_get_height (fsm->item->image);
1150 case OUTPUT_ITEM_TABLE:
1152 w = render_pager_get_size (fsm->p, H) / XR_POINT;
1153 h = render_pager_get_size (fsm->p, V) / XR_POINT;
1157 case OUTPUT_ITEM_GROUP:
1158 case OUTPUT_ITEM_MESSAGE:
1159 case OUTPUT_ITEM_PAGE_BREAK:
1160 case OUTPUT_ITEM_TEXT:
1172 xr_fsm_draw_all (struct xr_fsm *fsm, cairo_t *cr)
1174 assert (!fsm->print);
1175 xr_fsm_draw_region (fsm, cr, 0, 0, INT_MAX, INT_MAX);
1179 mul_XR_POINT (int x)
1181 return (x >= INT_MAX / XR_POINT ? INT_MAX
1182 : x <= INT_MIN / XR_POINT ? INT_MIN
1187 draw_image (cairo_surface_t *image, cairo_t *cr)
1190 cairo_set_source_surface (cr, image, 0, 0);
1191 cairo_rectangle (cr, 0, 0, cairo_image_surface_get_width (image),
1192 cairo_image_surface_get_height (image));
1199 xr_fsm_draw_region (struct xr_fsm *fsm, cairo_t *cr,
1200 int x, int y, int w, int h)
1202 assert (!fsm->print);
1203 switch (fsm->item->type)
1205 case OUTPUT_ITEM_CHART:
1206 xr_draw_chart (fsm->item->chart, cr, CHART_WIDTH, CHART_HEIGHT);
1209 case OUTPUT_ITEM_IMAGE:
1210 draw_image (fsm->item->image, cr);
1213 case OUTPUT_ITEM_TABLE:
1215 render_pager_draw_region (fsm->p, mul_XR_POINT (x), mul_XR_POINT (y),
1216 mul_XR_POINT (w), mul_XR_POINT (h));
1220 case OUTPUT_ITEM_GROUP:
1221 case OUTPUT_ITEM_MESSAGE:
1222 case OUTPUT_ITEM_PAGE_BREAK:
1223 case OUTPUT_ITEM_TEXT:
1231 xr_fsm_draw_table (struct xr_fsm *fsm, int space)
1233 int used = render_pager_draw_next (fsm->p, space);
1234 if (!render_pager_has_next (fsm->p))
1236 render_pager_destroy (fsm->p);
1238 fsm->layer_indexes = pivot_output_next_layer (fsm->item->table,
1239 fsm->layer_indexes, true);
1240 if (fsm->layer_indexes)
1242 fsm->p = render_pager_create (&fsm->rp, fsm->item->table,
1243 fsm->layer_indexes);
1244 if (fsm->item->table->look->paginate_layers)
1247 used += fsm->style->object_spacing;
1255 return MIN (used, space);
1259 xr_fsm_draw_chart (struct xr_fsm *fsm, int space)
1261 const int chart_height = 0.8 * MIN (fsm->rp.size[H], fsm->rp.size[V]);
1262 if (space < chart_height)
1266 xr_draw_chart (fsm->item->chart, fsm->cairo,
1267 xr_to_pt (fsm->rp.size[H]), xr_to_pt (chart_height));
1268 return chart_height;
1272 xr_fsm_draw_image (struct xr_fsm *fsm, int space)
1274 cairo_surface_t *image = fsm->item->image;
1275 int width = cairo_image_surface_get_width (image) * XR_POINT;
1276 int height = cairo_image_surface_get_height (image) * XR_POINT;
1277 if (!width || !height)
1280 if (height > fsm->rp.size[V])
1282 double scale = fsm->rp.size[V] / (double) height;
1285 if (!width || !height)
1288 cairo_scale (fsm->cairo, scale, scale);
1291 if (width > fsm->rp.size[H])
1293 double scale = fsm->rp.size[H] / (double) width;
1296 if (!width || !height)
1299 cairo_scale (fsm->cairo, scale, scale);
1305 draw_image (image, fsm->cairo);
1315 xr_fsm_draw_page_break (struct xr_fsm *fsm, int space)
1317 if (space >= fsm->rp.size[V])
1323 xr_fsm_draw_slice (struct xr_fsm *fsm, cairo_t *cr, int space)
1325 assert (fsm->print);
1327 if (fsm->done || space <= 0)
1333 switch (fsm->item->type)
1335 case OUTPUT_ITEM_CHART:
1336 used = xr_fsm_draw_chart (fsm, space);
1339 case OUTPUT_ITEM_IMAGE:
1340 used = xr_fsm_draw_image (fsm, space);
1343 case OUTPUT_ITEM_PAGE_BREAK:
1344 used = xr_fsm_draw_page_break (fsm, space);
1347 case OUTPUT_ITEM_TABLE:
1348 used = xr_fsm_draw_table (fsm, space);
1351 case OUTPUT_ITEM_GROUP:
1352 case OUTPUT_ITEM_MESSAGE:
1353 case OUTPUT_ITEM_TEXT:
1364 xr_fsm_is_empty (const struct xr_fsm *fsm)
1366 assert (fsm->print);