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-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"
48 #include "gl/c-ctype.h"
49 #include "gl/c-strcase.h"
50 #include "gl/xalloc.h"
52 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
57 xr_fsm_style_ref (const struct xr_fsm_style *style_)
59 assert (style_->ref_cnt > 0);
61 struct xr_fsm_style *style = CONST_CAST (struct xr_fsm_style *, style_);
67 xr_fsm_style_unref (struct xr_fsm_style *style)
71 assert (style->ref_cnt > 0);
72 if (!--style->ref_cnt)
74 for (size_t i = 0; i < XR_N_FONTS; i++)
75 pango_font_description_free (style->fonts[i]);
82 xr_fsm_style_equals (const struct xr_fsm_style *a,
83 const struct xr_fsm_style *b)
85 if (a->size[H] != b->size[H]
86 || a->size[V] != b->size[V]
87 || a->min_break[H] != b->min_break[H]
88 || a->min_break[V] != b->min_break[V]
89 || a->use_system_colors != b->use_system_colors
90 || a->transparent != b->transparent
91 || a->font_scale != b->font_scale)
94 for (size_t i = 0; i < XR_N_FONTS; i++)
95 if (!pango_font_description_equal (a->fonts[i], b->fonts[i]))
103 struct xr_fsm_style *style;
104 struct output_item *item;
106 /* Table items only. */
107 struct render_params rp;
108 struct render_pager *p;
109 cairo_t *cairo; /* XXX should this be here?! */
111 /* Chart and page-eject items only. */
115 /* The unit used for internal measurements is inch/(72 * XR_POINT).
116 (Thus, XR_POINT units represent one point.) */
117 #define XR_POINT PANGO_SCALE
119 /* Conversions to and from points. */
123 return x / (double) XR_POINT;
126 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
130 return x * (PANGO_SCALE * 72 / 96);
134 pango_to_xr (int pango)
136 return (XR_POINT != PANGO_SCALE
137 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
144 return (XR_POINT != PANGO_SCALE
145 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
149 /* Dimensions for drawing lines in tables. */
150 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
151 #define XR_LINE_SPACE XR_POINT /* Space between double lines. */
154 xr_layout_cell (struct xr_fsm *, const struct table_cell *,
155 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
156 int *width, int *height, int *brk);
159 xr_set_source_rgba (cairo_t *cairo, const struct cell_color *color)
161 cairo_set_source_rgba (cairo,
162 color->r / 255., color->g / 255., color->b / 255.,
163 color->alpha / 255.);
167 xr_draw_line (struct xr_fsm *xr, int x0, int y0, int x1, int y1, int style,
168 const struct cell_color *color)
170 cairo_new_path (xr->cairo);
171 if (!xr->style->use_system_colors)
172 xr_set_source_rgba (xr->cairo, color);
173 cairo_set_line_width (
175 xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
176 : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
178 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0));
179 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1));
180 cairo_stroke (xr->cairo);
184 xr_draw_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
186 cairo_new_path (xr->cairo);
187 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
188 cairo_close_path (xr->cairo);
189 cairo_stroke (xr->cairo);
190 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0));
191 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y0));
192 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1));
193 cairo_line_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y1));
197 fill_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
199 cairo_new_path (xr->cairo);
200 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
201 cairo_rectangle (xr->cairo,
202 xr_to_pt (x0), xr_to_pt (y0),
203 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
204 cairo_fill (xr->cairo);
207 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
208 shortening it to X0...X1 if SHORTEN is true.
209 Draws a horizontal line X1...X3 at Y if RIGHT says so,
210 shortening it to X2...X3 if SHORTEN is true. */
212 xr_draw_horz_line (struct xr_fsm *xr, int x0, int x1, int x2, int x3, int y,
213 enum render_line_style left, enum render_line_style right,
214 const struct cell_color *left_color,
215 const struct cell_color *right_color,
218 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
219 && cell_color_equal (left_color, right_color))
220 xr_draw_line (xr, x0, y, x3, y, left, left_color);
223 if (left != RENDER_LINE_NONE)
224 xr_draw_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
225 if (right != RENDER_LINE_NONE)
226 xr_draw_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
230 /* Draws a vertical line Y0...Y2 at X if TOP says so,
231 shortening it to Y0...Y1 if SHORTEN is true.
232 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
233 shortening it to Y2...Y3 if SHORTEN is true. */
235 xr_draw_vert_line (struct xr_fsm *xr, int y0, int y1, int y2, int y3, int x,
236 enum render_line_style top, enum render_line_style bottom,
237 const struct cell_color *top_color,
238 const struct cell_color *bottom_color,
241 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
242 && cell_color_equal (top_color, bottom_color))
243 xr_draw_line (xr, x, y0, x, y3, top, top_color);
246 if (top != RENDER_LINE_NONE)
247 xr_draw_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
248 if (bottom != RENDER_LINE_NONE)
249 xr_draw_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
254 xrr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
255 enum render_line_style styles[TABLE_N_AXES][2],
256 struct cell_color colors[TABLE_N_AXES][2])
258 const int x0 = bb[H][0];
259 const int y0 = bb[V][0];
260 const int x3 = bb[H][1];
261 const int y3 = bb[V][1];
262 const int top = styles[H][0];
263 const int bottom = styles[H][1];
265 int start_side = render_direction_rtl();
266 int end_side = !start_side;
267 const int start_of_line = styles[V][start_side];
268 const int end_of_line = styles[V][end_side];
269 const struct cell_color *top_color = &colors[H][0];
270 const struct cell_color *bottom_color = &colors[H][1];
271 const struct cell_color *start_color = &colors[V][start_side];
272 const struct cell_color *end_color = &colors[V][end_side];
274 /* The algorithm here is somewhat subtle, to allow it to handle
275 all the kinds of intersections that we need.
277 Three additional ordinates are assigned along the x axis. The
278 first is xc, midway between x0 and x3. The others are x1 and
279 x2; for a single vertical line these are equal to xc, and for
280 a double vertical line they are the ordinates of the left and
281 right half of the double line.
283 yc, y1, and y2 are assigned similarly along the y axis.
285 The following diagram shows the coordinate system and output
286 for double top and bottom lines, single left line, and no
290 y0 ________________________
296 y1 = y2 = yc |######### # |
301 y3 |________#_____#_______|
303 struct xr_fsm *xr = xr_;
305 /* Offset from center of each line in a pair of double lines. */
306 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
308 /* Are the lines along each axis single or double?
309 (It doesn't make sense to have different kinds of line on the
310 same axis, so we don't try to gracefully handle that case.) */
311 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
312 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
314 /* When horizontal lines are doubled,
315 the left-side line along y1 normally runs from x0 to x2,
316 and the right-side line along y1 from x3 to x1.
317 If the top-side line is also doubled, we shorten the y1 lines,
318 so that the left-side line runs only to x1,
319 and the right-side line only to x2.
320 Otherwise, the horizontal line at y = y1 below would cut off
321 the intersection, which looks ugly:
323 y0 ________________________
328 y1 |######### ########|
331 y2 |######################|
334 y3 |______________________|
335 It is more of a judgment call when the horizontal line is
336 single. We actually choose to cut off the line anyhow, as
337 shown in the first diagram above.
339 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
340 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
341 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
342 int horz_line_ofs = double_vert ? double_line_ofs : 0;
343 int xc = (x0 + x3) / 2;
344 int x1 = xc - horz_line_ofs;
345 int x2 = xc + horz_line_ofs;
347 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
348 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
349 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
350 int vert_line_ofs = double_horz ? double_line_ofs : 0;
351 int yc = (y0 + y3) / 2;
352 int y1 = yc - vert_line_ofs;
353 int y2 = yc + vert_line_ofs;
356 xr_draw_horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
357 start_color, end_color, shorten_yc_line);
360 xr_draw_horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
361 start_color, end_color, shorten_y1_lines);
362 xr_draw_horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
363 start_color, end_color, shorten_y2_lines);
367 xr_draw_vert_line (xr, y0, y1, y2, y3, xc, top, bottom,
368 top_color, bottom_color, shorten_xc_line);
371 xr_draw_vert_line (xr, y0, y1, y2, y3, x1, top, bottom,
372 top_color, bottom_color, shorten_x1_lines);
373 xr_draw_vert_line (xr, y0, y1, y2, y3, x2, top, bottom,
374 top_color, bottom_color, shorten_x2_lines);
379 xrr_measure_cell_width (void *xr_, const struct table_cell *cell,
380 int *min_width, int *max_width)
382 struct xr_fsm *xr = xr_;
383 int bb[TABLE_N_AXES][2];
384 int clip[TABLE_N_AXES][2];
391 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
392 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
395 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
398 *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
399 + cell->style->cell_style.margin[H][1]);
401 *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
402 + cell->style->cell_style.margin[H][1]);
406 xrr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
408 struct xr_fsm *xr = xr_;
409 int bb[TABLE_N_AXES][2];
410 int clip[TABLE_N_AXES][2];
414 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
415 + cell->style->cell_style.margin[H][1]);
418 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
419 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
420 h += px_to_xr (cell->style->cell_style.margin[V][0]
421 + cell->style->cell_style.margin[V][1]);
425 static void xr_clip (struct xr_fsm *, int clip[TABLE_N_AXES][2]);
428 xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
429 int bb[TABLE_N_AXES][2], int valign_offset,
430 int spill[TABLE_N_AXES][2],
431 int clip[TABLE_N_AXES][2])
433 struct xr_fsm *xr = xr_;
436 if (!xr->style->transparent)
438 cairo_save (xr->cairo);
439 int bg_clip[TABLE_N_AXES][2];
440 for (int axis = 0; axis < TABLE_N_AXES; axis++)
442 bg_clip[axis][0] = clip[axis][0];
443 if (bb[axis][0] == clip[axis][0])
444 bg_clip[axis][0] -= spill[axis][0];
446 bg_clip[axis][1] = clip[axis][1];
447 if (bb[axis][1] == clip[axis][1])
448 bg_clip[axis][1] += spill[axis][1];
450 xr_clip (xr, bg_clip);
451 xr_set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]);
453 bb[H][0] - spill[H][0],
454 bb[V][0] - spill[V][0],
455 bb[H][1] + spill[H][1],
456 bb[V][1] + spill[V][1]);
457 cairo_restore (xr->cairo);
459 cairo_save (xr->cairo);
460 if (!xr->style->use_system_colors)
461 xr_set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]);
463 bb[V][0] += valign_offset;
465 for (int axis = 0; axis < TABLE_N_AXES; axis++)
467 bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
468 bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
470 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
471 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
472 cairo_restore (xr->cairo);
476 xrr_adjust_break (void *xr_, const struct table_cell *cell,
477 int width, int height)
479 struct xr_fsm *xr = xr_;
480 int bb[TABLE_N_AXES][2];
481 int clip[TABLE_N_AXES][2];
484 if (xrr_measure_cell_height (xr_, cell, width) < height)
488 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
489 + cell->style->cell_style.margin[H][1]);
493 bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
494 + cell->style->cell_style.margin[V][1]);
495 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
496 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
501 xr_clip (struct xr_fsm *xr, int clip[TABLE_N_AXES][2])
503 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
505 double x0 = xr_to_pt (clip[H][0]);
506 double y0 = xr_to_pt (clip[V][0]);
507 double x1 = xr_to_pt (clip[H][1]);
508 double y1 = xr_to_pt (clip[V][1]);
510 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
511 cairo_clip (xr->cairo);
516 add_attr (PangoAttrList *list, PangoAttribute *attr,
517 guint start_index, guint end_index)
519 attr->start_index = start_index;
520 attr->end_index = end_index;
521 pango_attr_list_insert (list, attr);
525 markup_escape (struct string *out, unsigned int options,
526 const char *in, size_t len)
528 if (!(options & TAB_MARKUP))
530 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
542 ds_put_cstr (out, "&");
545 ds_put_cstr (out, "<");
548 ds_put_cstr (out, ">");
551 ds_put_byte (out, c);
558 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
560 int size[TABLE_N_AXES];
561 pango_layout_get_size (layout, &size[H], &size[V]);
565 static PangoFontDescription *
566 parse_font (const char *font, int default_size, bool bold, bool italic)
568 if (!c_strcasecmp (font, "Monospaced"))
571 PangoFontDescription *desc = pango_font_description_from_string (font);
575 /* If the font description didn't include an explicit font size, then set it
576 to DEFAULT_SIZE, which is in inch/72000 units. */
577 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
578 pango_font_description_set_size (desc,
579 (default_size / 1000.0) * PANGO_SCALE);
581 pango_font_description_set_weight (desc, (bold
583 : PANGO_WEIGHT_NORMAL));
584 pango_font_description_set_style (desc, (italic
586 : PANGO_STYLE_NORMAL));
592 xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell,
593 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
594 int *widthp, int *brk)
596 const struct font_style *font_style = &cell->style->font_style;
597 const struct cell_style *cell_style = &cell->style->cell_style;
598 unsigned int options = cell->options;
600 enum table_axis X = options & TAB_ROTATE ? V : H;
601 enum table_axis Y = !X;
602 int R = options & TAB_ROTATE ? 0 : 1;
604 enum xr_font_type font_type = (options & TAB_FIX
606 : XR_FONT_PROPORTIONAL);
607 PangoFontDescription *desc = NULL;
608 if (font_style->typeface)
610 font_style->typeface,
611 font_style->size ? font_style->size * 1000 * xr->style->font_scale : 10000,
612 font_style->bold, font_style->italic);
614 desc = xr->style->fonts[font_type];
617 PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
618 pango_layout_set_font_description (layout, desc);
620 const char *text = cell->text;
621 enum table_halign halign = table_halign_interpret (
622 cell_style->halign, cell->options & TAB_NUMERIC);
623 if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
625 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
627 const char *decimal = strrchr (text, cell_style->decimal_char);
630 pango_layout_set_text (layout, decimal, strlen (decimal));
631 pango_layout_set_width (layout, -1);
632 margin_adjustment += get_layout_dimension (layout, H);
635 if (margin_adjustment < 0)
636 bb[H][1] += margin_adjustment;
639 struct string tmp = DS_EMPTY_INITIALIZER;
640 PangoAttrList *attrs = NULL;
642 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
643 Pango's implementation of it): it will break after a period or a comma
644 that precedes a digit, e.g. in ".000" it will break after the period.
645 This code looks for such a situation and inserts a U+2060 WORD JOINER
646 to prevent the break.
648 This isn't necessary when the decimal point is between two digits
649 (e.g. "0.000" won't be broken) or when the display width is not limited so
650 that word wrapping won't happen.
652 It isn't necessary to look for more than one period or comma, as would
653 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
654 are present then there will always be a digit on both sides of every
656 if (options & TAB_MARKUP)
658 PangoAttrList *new_attrs;
660 if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
663 tmp.ss = ss_cstr (new_text);
664 tmp.capacity = tmp.ss.length;
668 /* XXX should we report the error? */
669 ds_put_cstr (&tmp, text);
672 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
674 const char *decimal = text + strcspn (text, ".,");
676 && c_isdigit (decimal[1])
677 && (decimal == text || !c_isdigit (decimal[-1])))
679 ds_extend (&tmp, strlen (text) + 16);
680 markup_escape (&tmp, options, text, decimal - text + 1);
681 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
682 markup_escape (&tmp, options, decimal + 1, -1);
686 if (font_style->underline)
689 attrs = pango_attr_list_new ();
690 pango_attr_list_insert (attrs, pango_attr_underline_new (
691 PANGO_UNDERLINE_SINGLE));
694 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
696 /* If we haven't already put TEXT into tmp, do it now. */
697 if (ds_is_empty (&tmp))
699 ds_extend (&tmp, strlen (text) + 16);
700 markup_escape (&tmp, options, text, -1);
703 size_t subscript_ofs = ds_length (&tmp);
704 for (size_t i = 0; i < cell->n_subscripts; i++)
707 ds_put_byte (&tmp, ',');
708 ds_put_cstr (&tmp, cell->subscripts[i]);
711 size_t superscript_ofs = ds_length (&tmp);
712 if (cell->superscript)
713 ds_put_cstr (&tmp, cell->superscript);
715 size_t footnote_ofs = ds_length (&tmp);
716 for (size_t i = 0; i < cell->n_footnotes; i++)
719 ds_put_byte (&tmp, ',');
720 ds_put_cstr (&tmp, cell->footnotes[i]->marker);
723 /* Allow footnote markers to occupy the right margin. That way, numbers
724 in the column are still aligned. */
725 if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
727 /* Measure the width of the footnote marker, so we know how much we
728 need to make room for. */
729 pango_layout_set_text (layout, ds_cstr (&tmp) + footnote_ofs,
730 ds_length (&tmp) - footnote_ofs);
732 PangoAttrList *fn_attrs = pango_attr_list_new ();
733 pango_attr_list_insert (
734 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
735 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
736 pango_layout_set_attributes (layout, fn_attrs);
737 pango_attr_list_unref (fn_attrs);
738 int footnote_width = get_layout_dimension (layout, X);
740 /* Bound the adjustment by the width of the right margin. */
741 int right_margin = px_to_xr (cell_style->margin[X][R]);
742 int footnote_adjustment = MIN (footnote_width, right_margin);
744 /* Adjust the bounding box. */
745 if (options & TAB_ROTATE)
746 footnote_adjustment = -footnote_adjustment;
747 bb[X][R] += footnote_adjustment;
750 pango_layout_set_attributes (layout, NULL);
753 /* Set attributes. */
755 attrs = pango_attr_list_new ();
756 add_attr (attrs, pango_attr_font_desc_new (desc), subscript_ofs,
757 PANGO_ATTR_INDEX_TO_TEXT_END);
758 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
759 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
760 if (cell->n_subscripts)
761 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
762 superscript_ofs - subscript_ofs);
763 if (cell->superscript || cell->n_footnotes)
764 add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
765 PANGO_ATTR_INDEX_TO_TEXT_END);
768 /* Set the attributes, if any. */
771 pango_layout_set_attributes (layout, attrs);
772 pango_attr_list_unref (attrs);
776 if (ds_is_empty (&tmp))
777 pango_layout_set_text (layout, text, -1);
779 pango_layout_set_text (layout, ds_cstr (&tmp), ds_length (&tmp));
782 pango_layout_set_alignment (layout,
783 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
784 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
785 : PANGO_ALIGN_CENTER));
786 pango_layout_set_width (
788 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
789 pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
791 if (clip[H][0] != clip[H][1])
793 cairo_save (xr->cairo);
794 if (!(options & TAB_ROTATE))
796 if (options & TAB_ROTATE)
798 cairo_translate (xr->cairo,
800 xr_to_pt (bb[V][1]));
801 cairo_rotate (xr->cairo, -M_PI_2);
804 cairo_translate (xr->cairo,
806 xr_to_pt (bb[V][0]));
807 pango_cairo_show_layout (xr->cairo, layout);
809 /* If enabled, this draws a blue rectangle around the extents of each
810 line of text, which can be rather useful for debugging layout
814 PangoLayoutIter *iter;
815 iter = pango_layout_get_iter (layout);
818 PangoRectangle extents;
820 pango_layout_iter_get_line_extents (iter, &extents, NULL);
821 cairo_save (xr->cairo);
822 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
825 pango_to_xr (extents.x),
826 pango_to_xr (extents.y),
827 pango_to_xr (extents.x + extents.width),
828 pango_to_xr (extents.y + extents.height));
829 cairo_restore (xr->cairo);
831 while (pango_layout_iter_next_line (iter));
832 pango_layout_iter_free (iter);
835 cairo_restore (xr->cairo);
838 int size[TABLE_N_AXES];
839 pango_layout_get_size (layout, &size[H], &size[V]);
840 int w = pango_to_xr (size[X]);
841 int h = pango_to_xr (size[Y]);
844 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
846 PangoLayoutIter *iter;
849 /* Choose a breakpoint between lines instead of in the middle of one. */
850 iter = pango_layout_get_iter (layout);
853 PangoRectangle extents;
857 pango_layout_iter_get_line_extents (iter, NULL, &extents);
858 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
859 extents.x = pango_to_xr (extents.x);
860 extents.y = pango_to_xr (y0);
861 extents.width = pango_to_xr (extents.width);
862 extents.height = pango_to_xr (y1 - y0);
863 bottom = bb[V][0] + extents.y + extents.height;
864 if (bottom < bb[V][1])
866 if (brk && clip[H][0] != clip[H][1])
874 while (pango_layout_iter_next_line (iter));
875 pango_layout_iter_free (iter);
877 /* If enabled, draws a green line across the chosen breakpoint, which can
878 be useful for debugging issues with breaking. */
882 xr_draw_line (xr, 0, best,
883 xr->style->size[H], best,
885 &(struct cell_color) CELL_COLOR (0, 255, 0));
889 pango_layout_set_attributes (layout, NULL);
891 if (desc != xr->style->fonts[font_type])
892 pango_font_description_free (desc);
893 g_object_unref (G_OBJECT (layout));
899 xr_layout_cell (struct xr_fsm *xr, const struct table_cell *cell,
900 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
901 int *width, int *height, int *brk)
906 /* If enabled, draws a blue rectangle around the cell extents, which can be
907 useful for debugging layout. */
910 if (clip[H][0] != clip[H][1])
912 cairo_save (xr->cairo);
913 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
914 xr_draw_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
915 cairo_restore (xr->cairo);
921 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
926 xr_table_render (struct xr_render_fsm *fsm, struct xr_fsm *xr)
928 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
930 while (render_pager_has_next (ts->p))
934 used = render_pager_draw_next (ts->p, xr->length);
947 xr_table_destroy (struct xr_render_fsm *fsm)
949 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
951 render_pager_destroy (ts->p);
955 static struct xr_render_fsm *
956 xr_render_table (struct xr_fsm *xr, struct table_item *table_item)
958 struct xr_table_state *ts;
960 ts = xmalloc (sizeof *ts);
961 ts->fsm.render = xr_table_render;
962 ts->fsm.destroy = xr_table_destroy;
965 xr->y += xr->char_height;
967 ts->p = render_pager_create (xr->params, table_item);
968 table_item_unref (table_item);
974 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_fsm *xr)
980 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
985 static struct xr_render_fsm *
986 xr_render_eject (void)
988 static struct xr_render_fsm eject_renderer =
994 return &eject_renderer;
997 #define CHART_WIDTH 500
998 #define CHART_HEIGHT 375
1000 static struct xr_render_fsm *
1001 xr_render_text (struct xr_fsm *xr, const struct text_item *text_item)
1003 enum text_item_type type = text_item_get_type (text_item);
1007 case TEXT_ITEM_PAGE_TITLE:
1010 case TEXT_ITEM_EJECT_PAGE:
1012 return xr_render_eject ();
1016 return xr_render_table (
1017 xr, text_item_to_table_item (text_item_ref (text_item)));
1024 #define CHART_WIDTH 500
1025 #define CHART_HEIGHT 375
1028 xr_fsm_create (const struct output_item *item_,
1029 const struct xr_fsm_style *style, cairo_t *cr)
1031 if (is_page_setup_item (item_)
1032 || is_group_open_item (item_)
1033 || is_group_close_item (item_))
1036 struct output_item *item;
1037 if (is_table_item (item_)
1038 || is_chart_item (item_)
1039 || is_page_eject_item (item_))
1040 item = output_item_ref (item_);
1041 else if (is_message_item (item_))
1042 item = table_item_super (
1043 text_item_to_table_item (
1044 message_item_to_text_item (
1046 output_item_ref (item_)))));
1047 else if (is_text_item (item_))
1049 if (to_text_item (item_)->type == TEXT_ITEM_PAGE_TITLE)
1052 item = table_item_super (
1053 text_item_to_table_item (
1055 output_item_ref (item_))));
1057 else if (is_group_open_item (item_))
1059 item = table_item_super (
1060 text_item_to_table_item (
1061 text_item_create (TEXT_ITEM_TITLE,
1062 to_group_open_item (item_)->command_name)));
1066 assert (is_table_item (item)
1067 || is_chart_item (item)
1068 || is_page_eject_item (item));
1070 static const struct render_ops xrr_render_ops = {
1071 .measure_cell_width = xrr_measure_cell_width,
1072 .measure_cell_height = xrr_measure_cell_height,
1073 .adjust_break = xrr_adjust_break,
1074 .draw_line = xrr_draw_line,
1075 .draw_cell = xrr_draw_cell,
1078 enum { LW = XR_LINE_WIDTH, LS = XR_LINE_SPACE };
1079 static const int xr_line_widths[RENDER_N_LINES] =
1081 [RENDER_LINE_NONE] = 0,
1082 [RENDER_LINE_SINGLE] = LW,
1083 [RENDER_LINE_DASHED] = LW,
1084 [RENDER_LINE_THICK] = LW * 2,
1085 [RENDER_LINE_THIN] = LW / 2,
1086 [RENDER_LINE_DOUBLE] = 2 * LW + LS,
1089 struct xr_fsm *fsm = xmalloc (sizeof *fsm);
1090 *fsm = (struct xr_fsm) {
1091 .style = xr_fsm_style_ref (style),
1094 .ops = &xrr_render_ops,
1096 .size = { [H] = style->size[H], [V] = style->size[V] },
1098 .line_widths = xr_line_widths,
1099 .min_break = { [H] = style->min_break[H], [V] = style->min_break[V] },
1100 .supports_margins = true,
1101 .rtl = render_direction_rtl (),
1105 if (is_table_item (item))
1108 fsm->p = render_pager_create (&fsm->rp, to_table_item (item));
1112 for (int i = 0; i < XR_N_FONTS; i++)
1114 PangoLayout *layout = pango_cairo_create_layout (cr);
1115 pango_layout_set_font_description (layout, style->fonts[i]);
1117 pango_layout_set_text (layout, "0", 1);
1119 int char_size[TABLE_N_AXES];
1120 pango_layout_get_size (layout, &char_size[H], &char_size[V]);
1121 for (int j = 0; j < TABLE_N_AXES; j++)
1123 int csj = pango_to_xr (char_size[j]);
1124 fsm->rp.font_size[j] = MAX (fsm->rp.font_size[j], csj);
1127 g_object_unref (G_OBJECT (layout));
1134 xr_fsm_destroy (struct xr_fsm *fsm)
1138 xr_fsm_style_unref (fsm->style);
1139 output_item_unref (fsm->item);
1140 render_pager_destroy (fsm->p);
1141 assert (!fsm->cairo);
1147 /* This is primarily meant for use with screen rendering since the result is a
1148 fixed value for charts. */
1150 xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp)
1154 if (is_table_item (fsm->item))
1157 w = render_pager_get_size (fsm->p, H) / XR_POINT;
1158 h = render_pager_get_size (fsm->p, V) / XR_POINT;
1161 else if (is_chart_item (fsm->item))
1176 xr_fsm_draw_table (struct xr_fsm *fsm, int space)
1179 while (render_pager_has_next (fsm->p))
1181 int chunk = render_pager_draw_next (fsm->p, space - used);
1186 cairo_translate (fsm->cairo, 0, chunk);
1192 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1193 double width, double height)
1195 struct xrchart_geometry geom;
1197 cairo_translate (cr, 0, height);
1198 cairo_scale (cr, 1.0, -1.0);
1199 xrchart_geometry_init (cr, &geom, width, height);
1200 if (is_boxplot (chart_item))
1201 xrchart_draw_boxplot (chart_item, cr, &geom);
1202 else if (is_histogram_chart (chart_item))
1203 xrchart_draw_histogram (chart_item, cr, &geom);
1204 else if (is_np_plot_chart (chart_item))
1205 xrchart_draw_np_plot (chart_item, cr, &geom);
1206 else if (is_piechart (chart_item))
1207 xrchart_draw_piechart (chart_item, cr, &geom);
1208 else if (is_barchart (chart_item))
1209 xrchart_draw_barchart (chart_item, cr, &geom);
1210 else if (is_roc_chart (chart_item))
1211 xrchart_draw_roc (chart_item, cr, &geom);
1212 else if (is_scree (chart_item))
1213 xrchart_draw_scree (chart_item, cr, &geom);
1214 else if (is_spreadlevel_plot_chart (chart_item))
1215 xrchart_draw_spreadlevel (chart_item, cr, &geom);
1216 else if (is_scatterplot_chart (chart_item))
1217 xrchart_draw_scatterplot (chart_item, cr, &geom);
1220 xrchart_geometry_free (cr, &geom);
1224 xr_fsm_draw_chart (struct xr_fsm *fsm, int space)
1226 const int chart_height = 0.8 * MIN (fsm->rp.size[H], fsm->rp.size[V]);
1227 if (space < chart_height)
1231 xr_draw_chart (to_chart_item (fsm->item), fsm->cairo,
1232 xr_to_pt (fsm->rp.size[H]), xr_to_pt (chart_height));
1233 return chart_height;
1237 xr_fsm_draw_eject (struct xr_fsm *fsm, int space)
1239 if (space >= fsm->rp.size[V])
1245 xr_fsm_draw_all (struct xr_fsm *fsm, cairo_t *cr)
1247 xr_fsm_draw_region (fsm, cr, 0, 0, INT_MAX, INT_MAX);
1251 mul_XR_POINT (int x)
1253 return (x >= INT_MAX / XR_POINT ? INT_MAX
1254 : x <= INT_MIN / XR_POINT ? INT_MIN
1259 xr_fsm_draw_region (struct xr_fsm *fsm, cairo_t *cr,
1260 int x, int y, int w, int h)
1262 if (is_table_item (fsm->item))
1265 render_pager_draw_region (fsm->p, mul_XR_POINT (x), mul_XR_POINT (y),
1266 mul_XR_POINT (w), mul_XR_POINT (h));
1269 else if (is_chart_item (fsm->item))
1270 xr_draw_chart (to_chart_item (fsm->item), cr, CHART_WIDTH, CHART_HEIGHT);
1271 else if (is_page_eject_item (fsm->item))
1273 /* Nothing to do. */
1280 xr_fsm_draw_slice (struct xr_fsm *fsm, cairo_t *cr, int space)
1282 if (xr_fsm_is_empty (fsm))
1287 int used = (is_table_item (fsm->item) ? xr_fsm_draw_table (fsm, space)
1288 : is_chart_item (fsm->item) ? xr_fsm_draw_chart (fsm, space)
1289 : is_page_eject_item (fsm->item) ? xr_fsm_draw_eject (fsm, space)
1299 xr_fsm_is_empty (const struct xr_fsm *fsm)
1301 return (is_table_item (fsm->item)
1302 ? !render_pager_has_next (fsm->p)