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.,
187 color->alpha / 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 == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
198 : style == RENDER_LINE_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 == RENDER_LINE_DASHED)
206 cairo_set_dash (xr->cairo, (double[]) { 2 }, 1, 0);
207 cairo_stroke (xr->cairo);
208 if (style == RENDER_LINE_DASHED)
209 cairo_set_dash (xr->cairo, NULL, 0, 0);
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 render_line_style left, enum render_line_style right,
243 const struct cell_color *left_color,
244 const struct cell_color *right_color,
247 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
248 && cell_color_equal (left_color, right_color))
249 xr_draw_line (xr, x0, y, x3, y, left, left_color);
252 if (left != RENDER_LINE_NONE)
253 xr_draw_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
254 if (right != RENDER_LINE_NONE)
255 xr_draw_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
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 render_line_style top, enum render_line_style bottom,
266 const struct cell_color *top_color,
267 const struct cell_color *bottom_color,
270 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
271 && cell_color_equal (top_color, bottom_color))
272 xr_draw_line (xr, x, y0, x, y3, top, top_color);
275 if (top != RENDER_LINE_NONE)
276 xr_draw_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
277 if (bottom != RENDER_LINE_NONE)
278 xr_draw_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
283 xrr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
284 enum render_line_style styles[TABLE_N_AXES][2],
285 struct cell_color colors[TABLE_N_AXES][2])
287 const int x0 = bb[H][0];
288 const int y0 = bb[V][0];
289 const int x3 = bb[H][1];
290 const int y3 = bb[V][1];
291 const int top = styles[H][0];
292 const int bottom = styles[H][1];
294 int start_side = render_direction_rtl();
295 int end_side = !start_side;
296 const int start_of_line = styles[V][start_side];
297 const int end_of_line = styles[V][end_side];
298 const struct cell_color *top_color = &colors[H][0];
299 const struct cell_color *bottom_color = &colors[H][1];
300 const struct cell_color *start_color = &colors[V][start_side];
301 const struct cell_color *end_color = &colors[V][end_side];
303 /* The algorithm here is somewhat subtle, to allow it to handle
304 all the kinds of intersections that we need.
306 Three additional ordinates are assigned along the x axis. The
307 first is xc, midway between x0 and x3. The others are x1 and
308 x2; for a single vertical line these are equal to xc, and for
309 a double vertical line they are the ordinates of the left and
310 right half of the double line.
312 yc, y1, and y2 are assigned similarly along the y axis.
314 The following diagram shows the coordinate system and output
315 for double top and bottom lines, single left line, and no
319 y0 ________________________
325 y1 = y2 = yc |######### # |
330 y3 |________#_____#_______|
332 struct xr_fsm *xr = xr_;
334 /* Offset from center of each line in a pair of double lines. */
335 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
337 /* Are the lines along each axis single or double?
338 (It doesn't make sense to have different kinds of line on the
339 same axis, so we don't try to gracefully handle that case.) */
340 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
341 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
343 /* When horizontal lines are doubled,
344 the left-side line along y1 normally runs from x0 to x2,
345 and the right-side line along y1 from x3 to x1.
346 If the top-side line is also doubled, we shorten the y1 lines,
347 so that the left-side line runs only to x1,
348 and the right-side line only to x2.
349 Otherwise, the horizontal line at y = y1 below would cut off
350 the intersection, which looks ugly:
352 y0 ________________________
357 y1 |######### ########|
360 y2 |######################|
363 y3 |______________________|
364 It is more of a judgment call when the horizontal line is
365 single. We actually choose to cut off the line anyhow, as
366 shown in the first diagram above.
368 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
369 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
370 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
371 int horz_line_ofs = double_vert ? double_line_ofs : 0;
372 int xc = (x0 + x3) / 2;
373 int x1 = xc - horz_line_ofs;
374 int x2 = xc + horz_line_ofs;
376 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
377 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
378 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
379 int vert_line_ofs = double_horz ? double_line_ofs : 0;
380 int yc = (y0 + y3) / 2;
381 int y1 = yc - vert_line_ofs;
382 int y2 = yc + vert_line_ofs;
385 xr_draw_horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
386 start_color, end_color, shorten_yc_line);
389 xr_draw_horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
390 start_color, end_color, shorten_y1_lines);
391 xr_draw_horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
392 start_color, end_color, shorten_y2_lines);
396 xr_draw_vert_line (xr, y0, y1, y2, y3, xc, top, bottom,
397 top_color, bottom_color, shorten_xc_line);
400 xr_draw_vert_line (xr, y0, y1, y2, y3, x1, top, bottom,
401 top_color, bottom_color, shorten_x1_lines);
402 xr_draw_vert_line (xr, y0, y1, y2, y3, x2, top, bottom,
403 top_color, bottom_color, shorten_x2_lines);
408 xrr_measure_cell_width (void *xr_, const struct table_cell *cell,
409 int *min_width, int *max_width)
411 struct xr_fsm *xr = xr_;
412 int bb[TABLE_N_AXES][2];
413 int clip[TABLE_N_AXES][2];
420 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
421 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
424 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
426 const int (*margin)[2] = cell->cell_style->margin;
428 *min_width += px_to_xr (margin[H][0] + margin[H][1]);
430 *max_width += px_to_xr (margin[H][0] + margin[H][1]);
434 xrr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
436 struct xr_fsm *xr = xr_;
438 const int (*margin)[2] = cell->cell_style->margin;
440 int bb[TABLE_N_AXES][2] = {
442 [H][1] = width - px_to_xr (margin[H][0] + margin[H][1]),
447 int clip[TABLE_N_AXES][2] = { { 0, 0 }, { 0, 0 } };
450 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
451 h += px_to_xr (margin[V][0] + margin[V][1]);
455 static void xr_clip (struct xr_fsm *, int clip[TABLE_N_AXES][2]);
458 xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
459 int bb[TABLE_N_AXES][2], int valign_offset,
460 int spill[TABLE_N_AXES][2],
461 int clip[TABLE_N_AXES][2])
463 struct xr_fsm *xr = xr_;
466 const struct cell_color *bg = &cell->font_style->bg[color_idx];
467 if ((bg->r != 255 || bg->g != 255 || bg->b != 255) && bg->alpha)
469 cairo_save (xr->cairo);
470 int bg_clip[TABLE_N_AXES][2];
471 for (int axis = 0; axis < TABLE_N_AXES; axis++)
473 bg_clip[axis][0] = clip[axis][0];
474 if (bb[axis][0] == clip[axis][0])
475 bg_clip[axis][0] -= spill[axis][0];
477 bg_clip[axis][1] = clip[axis][1];
478 if (bb[axis][1] == clip[axis][1])
479 bg_clip[axis][1] += spill[axis][1];
481 xr_clip (xr, bg_clip);
482 xr_set_source_rgba (xr->cairo, bg);
484 bb[H][0] - spill[H][0],
485 bb[V][0] - spill[V][0],
486 bb[H][1] + spill[H][1],
487 bb[V][1] + spill[V][1]);
488 cairo_restore (xr->cairo);
490 cairo_save (xr->cairo);
491 if (!xr->style->use_system_colors)
492 xr_set_source_rgba (xr->cairo, &cell->font_style->fg[color_idx]);
494 bb[V][0] += valign_offset;
496 for (int axis = 0; axis < TABLE_N_AXES; axis++)
498 bb[axis][0] += px_to_xr (cell->cell_style->margin[axis][0]);
499 bb[axis][1] -= px_to_xr (cell->cell_style->margin[axis][1]);
501 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
502 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
503 cairo_restore (xr->cairo);
507 xrr_adjust_break (void *xr_, const struct table_cell *cell,
508 int width, int height)
510 struct xr_fsm *xr = xr_;
512 if (xrr_measure_cell_height (xr_, cell, width) < height)
515 const int (*margin)[2] = cell->cell_style->margin;
517 int bb[TABLE_N_AXES][2] = {
520 [H][1] = width - px_to_xr (margin[H][0] + margin[H][1]),
521 [V][1] = height - px_to_xr (margin[V][0] + margin[V][1]),
526 int clip[TABLE_N_AXES][2] = { { 0, 0 }, { 0, 0 } };
529 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
534 xrr_scale (void *xr_, double scale)
536 struct xr_fsm *xr = xr_;
537 cairo_scale (xr->cairo, scale, scale);
541 xr_clip (struct xr_fsm *xr, int clip[TABLE_N_AXES][2])
543 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
545 double x0 = xr_to_pt (clip[H][0]);
546 double y0 = xr_to_pt (clip[V][0]);
547 double x1 = xr_to_pt (clip[H][1]);
548 double y1 = xr_to_pt (clip[V][1]);
550 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
551 cairo_clip (xr->cairo);
556 add_attr (PangoAttrList *list, PangoAttribute *attr,
557 guint start_index, guint end_index)
559 attr->start_index = start_index;
560 attr->end_index = end_index;
561 pango_attr_list_insert (list, attr);
565 markup_escape (struct string *out, bool markup, const char *in, size_t len)
569 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
581 ds_put_cstr (out, "&");
584 ds_put_cstr (out, "<");
587 ds_put_cstr (out, ">");
590 ds_put_byte (out, c);
597 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
599 int size[TABLE_N_AXES];
600 pango_layout_get_size (layout, &size[H], &size[V]);
604 static PangoFontDescription *
605 parse_font (const char *font, int default_size, bool bold, bool italic)
607 if (!c_strcasecmp (font, "Monospaced"))
610 PangoFontDescription *desc = pango_font_description_from_string (font);
614 /* If the font description didn't include an explicit font size, then set it
615 to DEFAULT_SIZE, which is in inch/72000 units. */
616 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
617 pango_font_description_set_size (desc,
618 (default_size / 1000.0) * PANGO_SCALE);
620 pango_font_description_set_weight (desc, (bold
622 : PANGO_WEIGHT_NORMAL));
623 pango_font_description_set_style (desc, (italic
625 : PANGO_STYLE_NORMAL));
631 xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell,
632 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
633 int *widthp, int *brk)
635 const struct pivot_table *pt = xr->item->table;
636 const struct font_style *font_style = cell->font_style;
637 const struct cell_style *cell_style = cell->cell_style;
638 unsigned int options = cell->options;
640 enum table_axis X = options & TAB_ROTATE ? V : H;
641 enum table_axis Y = !X;
642 int R = options & TAB_ROTATE ? 0 : 1;
644 PangoFontDescription *desc = NULL;
645 if (font_style->typeface)
647 font_style->typeface,
648 font_style->size ? font_style->size * 1000 : 10000,
649 font_style->bold, font_style->italic);
651 desc = xr->style->font;
654 PangoContext *context = pango_cairo_create_context (xr->cairo);
655 pango_cairo_context_set_resolution (context, xr->style->font_resolution);
656 PangoLayout *layout = pango_layout_new (context);
657 g_object_unref (context);
659 pango_layout_set_font_description (layout, desc);
661 struct string body = DS_EMPTY_INITIALIZER;
662 bool numeric = pivot_value_format_body (cell->value, pt, &body);
664 enum table_halign halign = table_halign_interpret (
665 cell->cell_style->halign, numeric);
667 if (cell_style->halign == TABLE_HALIGN_DECIMAL
668 && !(cell->options & TAB_ROTATE))
670 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
672 const char *decimal = strrchr (ds_cstr (&body),
673 cell_style->decimal_char);
676 pango_layout_set_text (layout, decimal, strlen (decimal));
677 pango_layout_set_width (layout, -1);
678 margin_adjustment += get_layout_dimension (layout, H);
681 if (margin_adjustment < 0)
682 bb[H][1] += margin_adjustment;
685 PangoAttrList *attrs = NULL;
687 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
688 Pango's implementation of it): it will break after a period or a comma
689 that precedes a digit, e.g. in ".000" it will break after the period.
690 This code looks for such a situation and inserts a U+2060 WORD JOINER
691 to prevent the break.
693 This isn't necessary when the decimal point is between two digits
694 (e.g. "0.000" won't be broken) or when the display width is not limited so
695 that word wrapping won't happen.
697 It isn't necessary to look for more than one period or comma, as would
698 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
699 are present then there will always be a digit on both sides of every
701 bool markup = cell->font_style->markup;
704 PangoAttrList *new_attrs;
706 if (pango_parse_markup (ds_cstr (&body), -1, 0,
707 &new_attrs, &new_text, NULL, NULL))
711 body.ss = ss_cstr (new_text);
712 body.capacity = body.ss.length;
716 /* XXX should we report the error? */
719 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
721 const char *text = ds_cstr (&body);
722 const char *decimal = text + strcspn (text, ".,");
724 && c_isdigit (decimal[1])
725 && (decimal == text || !c_isdigit (decimal[-1])))
727 struct string tmp = DS_EMPTY_INITIALIZER;
728 ds_extend (&tmp, ds_length (&body) + 16);
729 markup_escape (&tmp, markup, text, decimal - text + 1);
730 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
731 markup_escape (&tmp, markup, decimal + 1, -1);
732 ds_swap (&tmp, &body);
737 if (font_style->underline)
740 attrs = pango_attr_list_new ();
741 pango_attr_list_insert (attrs, pango_attr_underline_new (
742 PANGO_UNDERLINE_SINGLE));
745 const struct pivot_value *value = cell->value;
746 if (value->n_footnotes || value->n_subscripts)
748 size_t subscript_ofs = ds_length (&body);
749 for (size_t i = 0; i < value->n_subscripts; i++)
752 ds_put_byte (&body, ',');
753 ds_put_cstr (&body, value->subscripts[i]);
756 size_t footnote_ofs = ds_length (&body);
757 size_t n_footnotes = 0;
758 for (size_t i = 0; i < value->n_footnotes; i++)
760 const struct pivot_footnote *f
761 = pt->footnotes[value->footnote_indexes[i]];
765 ds_put_byte (&body, ',');
766 pivot_footnote_format_marker (f, pt, &body);
770 /* Allow footnote markers to occupy the right margin. That way, numbers
771 in the column are still aligned. */
772 if (value->n_footnotes && halign == TABLE_HALIGN_RIGHT)
774 /* Measure the width of the footnote marker, so we know how much we
775 need to make room for. */
776 pango_layout_set_text (layout, ds_cstr (&body) + footnote_ofs,
777 ds_length (&body) - footnote_ofs);
779 PangoAttrList *fn_attrs = pango_attr_list_new ();
780 pango_attr_list_insert (
781 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
782 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
783 pango_layout_set_attributes (layout, fn_attrs);
784 pango_attr_list_unref (fn_attrs);
785 int footnote_width = get_layout_dimension (layout, X);
787 /* Bound the adjustment by the width of the right margin. */
788 int right_margin = px_to_xr (cell_style->margin[X][R]);
789 int footnote_adjustment = MIN (footnote_width, right_margin);
791 /* Adjust the bounding box. */
792 if (options & TAB_ROTATE)
793 footnote_adjustment = -footnote_adjustment;
794 bb[X][R] += footnote_adjustment;
797 pango_layout_set_attributes (layout, NULL);
800 /* Set attributes. */
802 attrs = pango_attr_list_new ();
803 add_attr (attrs, pango_attr_font_desc_new (desc), subscript_ofs,
804 PANGO_ATTR_INDEX_TO_TEXT_END);
805 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
806 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
807 if (value->n_subscripts)
808 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
809 footnote_ofs - subscript_ofs);
810 if (value->n_footnotes)
812 bool superscript = pt->look->footnote_marker_superscripts;
813 add_attr (attrs, pango_attr_rise_new (superscript ? 3000 : -3000),
814 footnote_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
818 /* Set the attributes, if any. */
821 pango_layout_set_attributes (layout, attrs);
822 pango_attr_list_unref (attrs);
826 pango_layout_set_text (layout, ds_cstr (&body), ds_length (&body));
828 pango_layout_set_alignment (layout,
829 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
830 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
831 : PANGO_ALIGN_CENTER));
832 pango_layout_set_width (
834 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
835 pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
837 int size[TABLE_N_AXES];
838 pango_layout_get_size (layout, &size[H], &size[V]);
840 if (clip[H][0] != clip[H][1])
842 cairo_save (xr->cairo);
843 if (!(options & TAB_ROTATE))
845 if (options & TAB_ROTATE)
847 int extra = bb[H][1] - bb[H][0] - size[V];
848 int halign_offset = extra > 0 ? extra / 2 : 0;
849 cairo_translate (xr->cairo,
850 xr_to_pt (bb[H][0] + halign_offset),
851 xr_to_pt (bb[V][1]));
852 cairo_rotate (xr->cairo, -M_PI_2);
855 cairo_translate (xr->cairo,
857 xr_to_pt (bb[V][0]));
858 pango_cairo_show_layout (xr->cairo, layout);
860 /* If enabled, this draws a blue rectangle around the extents of each
861 line of text, which can be rather useful for debugging layout
865 PangoLayoutIter *iter;
866 iter = pango_layout_get_iter (layout);
869 PangoRectangle extents;
871 pango_layout_iter_get_line_extents (iter, &extents, NULL);
872 cairo_save (xr->cairo);
873 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
876 pango_to_xr (extents.x),
877 pango_to_xr (extents.y),
878 pango_to_xr (extents.x + extents.width),
879 pango_to_xr (extents.y + extents.height));
880 cairo_restore (xr->cairo);
882 while (pango_layout_iter_next_line (iter));
883 pango_layout_iter_free (iter);
886 cairo_restore (xr->cairo);
889 int w = pango_to_xr (size[X]);
890 int h = pango_to_xr (size[Y]);
893 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
895 PangoLayoutIter *iter;
898 /* Choose a breakpoint between lines instead of in the middle of one. */
899 iter = pango_layout_get_iter (layout);
902 PangoRectangle extents;
906 pango_layout_iter_get_line_extents (iter, NULL, &extents);
907 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
908 extents.x = pango_to_xr (extents.x);
909 extents.y = pango_to_xr (y0);
910 extents.width = pango_to_xr (extents.width);
911 extents.height = pango_to_xr (y1 - y0);
912 bottom = bb[V][0] + extents.y + extents.height;
913 if (bottom < bb[V][1])
915 if (brk && clip[H][0] != clip[H][1])
923 while (pango_layout_iter_next_line (iter));
924 pango_layout_iter_free (iter);
926 /* If enabled, draws a green line across the chosen breakpoint, which can
927 be useful for debugging issues with breaking. */
931 xr_draw_line (xr, 0, best,
932 xr->style->size[H], best,
934 &(struct cell_color) CELL_COLOR (0, 255, 0));
938 pango_layout_set_attributes (layout, NULL);
940 if (desc != xr->style->font)
941 pango_font_description_free (desc);
942 g_object_unref (G_OBJECT (layout));
949 xr_layout_cell (struct xr_fsm *xr, const struct table_cell *cell,
950 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
951 int *width, int *height, int *brk)
956 /* If enabled, draws a blue rectangle around the cell extents, which can be
957 useful for debugging layout. */
960 if (clip[H][0] != clip[H][1])
962 cairo_save (xr->cairo);
963 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
964 xr_draw_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
965 cairo_restore (xr->cairo);
971 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
974 #define CHART_WIDTH 500
975 #define CHART_HEIGHT 375
977 static struct xr_fsm *
978 xr_fsm_create (const struct output_item *item_,
979 const struct xr_fsm_style *style, cairo_t *cr,
982 struct output_item *item;
986 case OUTPUT_ITEM_CHART:
987 case OUTPUT_ITEM_IMAGE:
988 case OUTPUT_ITEM_PAGE_BREAK:
989 case OUTPUT_ITEM_TABLE:
990 item = output_item_ref (item_);
993 case OUTPUT_ITEM_GROUP_OPEN:
994 case OUTPUT_ITEM_GROUP_CLOSE:
995 case OUTPUT_ITEM_PAGE_SETUP:
998 case OUTPUT_ITEM_MESSAGE:
999 item = text_item_to_table_item (message_item_to_text_item (
1000 output_item_ref (item_)));
1003 case OUTPUT_ITEM_TEXT:
1004 if (item_->text.subtype == TEXT_ITEM_PAGE_TITLE)
1007 item = text_item_to_table_item (output_item_ref (item_));
1014 assert (item->type == OUTPUT_ITEM_TABLE
1015 || item->type == OUTPUT_ITEM_CHART
1016 || item->type == OUTPUT_ITEM_IMAGE
1017 || item->type == OUTPUT_ITEM_PAGE_BREAK);
1019 size_t *layer_indexes = NULL;
1020 if (item->type == OUTPUT_ITEM_TABLE)
1022 layer_indexes = pivot_output_next_layer (item->table, NULL, print);
1027 static const struct render_ops xrr_render_ops = {
1028 .measure_cell_width = xrr_measure_cell_width,
1029 .measure_cell_height = xrr_measure_cell_height,
1030 .adjust_break = xrr_adjust_break,
1031 .draw_line = xrr_draw_line,
1032 .draw_cell = xrr_draw_cell,
1036 enum { LW = XR_LINE_WIDTH, LS = XR_LINE_SPACE };
1037 static const int xr_line_widths[RENDER_N_LINES] =
1039 [RENDER_LINE_NONE] = 0,
1040 [RENDER_LINE_SINGLE] = LW,
1041 [RENDER_LINE_DASHED] = LW,
1042 [RENDER_LINE_THICK] = LW * 2,
1043 [RENDER_LINE_THIN] = LW / 2,
1044 [RENDER_LINE_DOUBLE] = 2 * LW + LS,
1047 struct xr_fsm *fsm = xmalloc (sizeof *fsm);
1048 *fsm = (struct xr_fsm) {
1049 .style = xr_fsm_style_ref (style),
1052 .layer_indexes = layer_indexes,
1054 .ops = &xrr_render_ops,
1056 .size = { [H] = style->size[H], [V] = style->size[V] },
1057 .line_widths = xr_line_widths,
1058 .min_break = { [H] = style->min_break[H], [V] = style->min_break[V] },
1059 .supports_margins = true,
1060 .rtl = render_direction_rtl (),
1065 /* Get font size. */
1066 PangoContext *context = pango_cairo_create_context (cr);
1067 pango_cairo_context_set_resolution (context, style->font_resolution);
1068 PangoLayout *layout = pango_layout_new (context);
1069 g_object_unref (context);
1071 pango_layout_set_font_description (layout, style->font);
1073 pango_layout_set_text (layout, "0", 1);
1075 int char_size[TABLE_N_AXES];
1076 pango_layout_get_size (layout, &char_size[H], &char_size[V]);
1077 for (int a = 0; a < TABLE_N_AXES; a++)
1079 int csa = pango_to_xr (char_size[a]);
1080 fsm->rp.font_size[a] = MAX (fsm->rp.font_size[a], csa);
1083 g_object_unref (G_OBJECT (layout));
1085 if (item->type == OUTPUT_ITEM_TABLE)
1088 fsm->p = render_pager_create (&fsm->rp, item->table, fsm->layer_indexes);
1096 xr_fsm_create_for_printing (const struct output_item *item,
1097 const struct xr_fsm_style *style, cairo_t *cr)
1099 return xr_fsm_create (item, style, cr, true);
1103 xr_fsm_destroy (struct xr_fsm *fsm)
1107 xr_fsm_style_unref (fsm->style);
1108 output_item_unref (fsm->item);
1109 free (fsm->layer_indexes);
1110 render_pager_destroy (fsm->p);
1111 assert (!fsm->cairo);
1116 /* Scrolling API. */
1119 xr_fsm_create_for_scrolling (const struct output_item *item,
1120 const struct xr_fsm_style *style, cairo_t *cr)
1122 return xr_fsm_create (item, style, cr, false);
1126 xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp)
1128 assert (!fsm->print);
1132 switch (fsm->item->type)
1134 case OUTPUT_ITEM_CHART:
1139 case OUTPUT_ITEM_IMAGE:
1140 w = cairo_image_surface_get_width (fsm->item->image);
1141 h = cairo_image_surface_get_height (fsm->item->image);
1144 case OUTPUT_ITEM_TABLE:
1146 w = render_pager_get_size (fsm->p, H) / XR_POINT;
1147 h = render_pager_get_size (fsm->p, V) / XR_POINT;
1151 case OUTPUT_ITEM_GROUP_OPEN:
1152 case OUTPUT_ITEM_GROUP_CLOSE:
1153 case OUTPUT_ITEM_MESSAGE:
1154 case OUTPUT_ITEM_PAGE_BREAK:
1155 case OUTPUT_ITEM_PAGE_SETUP:
1156 case OUTPUT_ITEM_TEXT:
1168 xr_fsm_draw_all (struct xr_fsm *fsm, cairo_t *cr)
1170 assert (!fsm->print);
1171 xr_fsm_draw_region (fsm, cr, 0, 0, INT_MAX, INT_MAX);
1175 mul_XR_POINT (int x)
1177 return (x >= INT_MAX / XR_POINT ? INT_MAX
1178 : x <= INT_MIN / XR_POINT ? INT_MIN
1183 draw_image (cairo_surface_t *image, cairo_t *cr)
1186 cairo_set_source_surface (cr, image, 0, 0);
1187 cairo_rectangle (cr, 0, 0, cairo_image_surface_get_width (image),
1188 cairo_image_surface_get_height (image));
1195 xr_fsm_draw_region (struct xr_fsm *fsm, cairo_t *cr,
1196 int x, int y, int w, int h)
1198 assert (!fsm->print);
1199 switch (fsm->item->type)
1201 case OUTPUT_ITEM_CHART:
1202 xr_draw_chart (fsm->item->chart, cr, CHART_WIDTH, CHART_HEIGHT);
1205 case OUTPUT_ITEM_IMAGE:
1206 draw_image (fsm->item->image, cr);
1209 case OUTPUT_ITEM_TABLE:
1211 render_pager_draw_region (fsm->p, mul_XR_POINT (x), mul_XR_POINT (y),
1212 mul_XR_POINT (w), mul_XR_POINT (h));
1216 case OUTPUT_ITEM_GROUP_OPEN:
1217 case OUTPUT_ITEM_GROUP_CLOSE:
1218 case OUTPUT_ITEM_MESSAGE:
1219 case OUTPUT_ITEM_PAGE_BREAK:
1220 case OUTPUT_ITEM_PAGE_SETUP:
1221 case OUTPUT_ITEM_TEXT:
1229 xr_fsm_draw_table (struct xr_fsm *fsm, int space)
1231 int used = render_pager_draw_next (fsm->p, space);
1232 if (!render_pager_has_next (fsm->p))
1234 render_pager_destroy (fsm->p);
1236 fsm->layer_indexes = pivot_output_next_layer (fsm->item->table,
1237 fsm->layer_indexes, true);
1238 if (fsm->layer_indexes)
1240 fsm->p = render_pager_create (&fsm->rp, fsm->item->table,
1241 fsm->layer_indexes);
1242 if (fsm->item->table->look->paginate_layers)
1245 used += fsm->style->object_spacing;
1253 return MIN (used, space);
1257 xr_fsm_draw_chart (struct xr_fsm *fsm, int space)
1259 const int chart_height = 0.8 * MIN (fsm->rp.size[H], fsm->rp.size[V]);
1260 if (space < chart_height)
1264 xr_draw_chart (fsm->item->chart, fsm->cairo,
1265 xr_to_pt (fsm->rp.size[H]), xr_to_pt (chart_height));
1266 return chart_height;
1270 xr_fsm_draw_image (struct xr_fsm *fsm, int space)
1272 cairo_surface_t *image = fsm->item->image;
1273 int width = cairo_image_surface_get_width (image) * XR_POINT;
1274 int height = cairo_image_surface_get_height (image) * XR_POINT;
1275 if (!width || !height)
1278 if (height > fsm->rp.size[V])
1280 double scale = fsm->rp.size[V] / (double) height;
1283 if (!width || !height)
1286 cairo_scale (fsm->cairo, scale, scale);
1289 if (width > fsm->rp.size[H])
1291 double scale = fsm->rp.size[H] / (double) width;
1294 if (!width || !height)
1297 cairo_scale (fsm->cairo, scale, scale);
1303 draw_image (image, fsm->cairo);
1313 xr_fsm_draw_page_break (struct xr_fsm *fsm, int space)
1315 if (space >= fsm->rp.size[V])
1321 xr_fsm_draw_slice (struct xr_fsm *fsm, cairo_t *cr, int space)
1323 assert (fsm->print);
1325 if (fsm->done || space <= 0)
1331 switch (fsm->item->type)
1333 case OUTPUT_ITEM_CHART:
1334 used = xr_fsm_draw_chart (fsm, space);
1337 case OUTPUT_ITEM_IMAGE:
1338 used = xr_fsm_draw_image (fsm, space);
1341 case OUTPUT_ITEM_PAGE_BREAK:
1342 used = xr_fsm_draw_page_break (fsm, space);
1345 case OUTPUT_ITEM_TABLE:
1346 used = xr_fsm_draw_table (fsm, space);
1349 case OUTPUT_ITEM_GROUP_OPEN:
1350 case OUTPUT_ITEM_GROUP_CLOSE:
1351 case OUTPUT_ITEM_MESSAGE:
1352 case OUTPUT_ITEM_PAGE_SETUP:
1353 case OUTPUT_ITEM_TEXT:
1364 xr_fsm_is_empty (const struct xr_fsm *fsm)
1366 assert (fsm->print);