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/image-item.h"
42 #include "output/message-item.h"
43 #include "output/page-eject-item.h"
44 #include "output/page-setup-item.h"
45 #include "output/pivot-output.h"
46 #include "output/pivot-table.h"
47 #include "output/render.h"
48 #include "output/table-item.h"
49 #include "output/text-item.h"
51 #include "gl/c-ctype.h"
52 #include "gl/c-strcase.h"
53 #include "gl/xalloc.h"
55 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
60 xr_fsm_style_ref (const struct xr_fsm_style *style_)
62 assert (style_->ref_cnt > 0);
64 struct xr_fsm_style *style = CONST_CAST (struct xr_fsm_style *, style_);
70 xr_fsm_style_unshare (struct xr_fsm_style *old)
72 assert (old->ref_cnt > 0);
73 if (old->ref_cnt == 1)
76 xr_fsm_style_unref (old);
78 struct xr_fsm_style *new = xmemdup (old, sizeof *old);
81 new->font = pango_font_description_copy (old->font);
87 xr_fsm_style_unref (struct xr_fsm_style *style)
91 assert (style->ref_cnt > 0);
92 if (!--style->ref_cnt)
94 pango_font_description_free (style->font);
101 xr_fsm_style_equals (const struct xr_fsm_style *a,
102 const struct xr_fsm_style *b)
104 if (a->size[H] != b->size[H]
105 || a->size[V] != b->size[V]
106 || a->min_break[H] != b->min_break[H]
107 || a->min_break[V] != b->min_break[V]
108 || !pango_font_description_equal (a->font, b->font)
109 || a->use_system_colors != b->use_system_colors
110 || a->object_spacing != b->object_spacing
111 || a->font_resolution != b->font_resolution)
117 /* Renders a single output_item to an output device in one of two ways:
119 - 'print == true': Broken across multiple pages if necessary.
121 - 'print == false': In a single region that the user may scroll around if
124 Normally 'output_item' corresponds to a single rendering. There is a
125 special case when 'print == true' and 'output_item' is a table_item with
126 multiple layers and 'item->pt->table_look->print_all_layers == true'. In
127 that case, each layer is rendered separately from the FSM's internal point
128 of view; from the client's point of view, it is all one operation.
132 struct xr_fsm_style *style;
133 struct output_item *item;
136 /* Print mode only. */
139 /* Table items only. */
140 size_t *layer_indexes;
141 struct render_params rp;
142 struct render_pager *p;
143 cairo_t *cairo; /* XXX should this be here?! */
146 /* The unit used for internal measurements is inch/(72 * XR_POINT).
147 (Thus, XR_POINT units represent one point.) */
148 #define XR_POINT PANGO_SCALE
150 /* Conversions to and from points. */
154 return x / (double) XR_POINT;
157 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
161 return x * (PANGO_SCALE * 72 / 96);
165 pango_to_xr (int pango)
167 return (XR_POINT != PANGO_SCALE
168 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
175 return (XR_POINT != PANGO_SCALE
176 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
180 /* Dimensions for drawing lines in tables. */
181 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
182 #define XR_LINE_SPACE XR_POINT /* Space between double lines. */
185 xr_layout_cell (struct xr_fsm *, const struct table_cell *,
186 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
187 int *width, int *height, int *brk);
190 xr_set_source_rgba (cairo_t *cairo, const struct cell_color *color)
192 cairo_set_source_rgba (cairo,
193 color->r / 255., color->g / 255., color->b / 255.,
194 color->alpha / 255.);
198 xr_draw_line (struct xr_fsm *xr, int x0, int y0, int x1, int y1, int style,
199 const struct cell_color *color)
201 cairo_new_path (xr->cairo);
202 cairo_set_line_width (
204 xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
205 : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
207 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0));
208 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1));
210 if (!xr->style->use_system_colors)
211 xr_set_source_rgba (xr->cairo, color);
212 if (style == RENDER_LINE_DASHED)
213 cairo_set_dash (xr->cairo, (double[]) { 2 }, 1, 0);
214 cairo_stroke (xr->cairo);
215 if (style == RENDER_LINE_DASHED)
216 cairo_set_dash (xr->cairo, NULL, 0, 0);
220 xr_draw_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
222 cairo_new_path (xr->cairo);
223 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
224 cairo_close_path (xr->cairo);
225 cairo_stroke (xr->cairo);
226 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0));
227 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y0));
228 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1));
229 cairo_line_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y1));
233 fill_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
235 cairo_new_path (xr->cairo);
236 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
237 cairo_rectangle (xr->cairo,
238 xr_to_pt (x0), xr_to_pt (y0),
239 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
240 cairo_fill (xr->cairo);
243 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
244 shortening it to X0...X1 if SHORTEN is true.
245 Draws a horizontal line X1...X3 at Y if RIGHT says so,
246 shortening it to X2...X3 if SHORTEN is true. */
248 xr_draw_horz_line (struct xr_fsm *xr, int x0, int x1, int x2, int x3, int y,
249 enum render_line_style left, enum render_line_style right,
250 const struct cell_color *left_color,
251 const struct cell_color *right_color,
254 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
255 && cell_color_equal (left_color, right_color))
256 xr_draw_line (xr, x0, y, x3, y, left, left_color);
259 if (left != RENDER_LINE_NONE)
260 xr_draw_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
261 if (right != RENDER_LINE_NONE)
262 xr_draw_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
266 /* Draws a vertical line Y0...Y2 at X if TOP says so,
267 shortening it to Y0...Y1 if SHORTEN is true.
268 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
269 shortening it to Y2...Y3 if SHORTEN is true. */
271 xr_draw_vert_line (struct xr_fsm *xr, int y0, int y1, int y2, int y3, int x,
272 enum render_line_style top, enum render_line_style bottom,
273 const struct cell_color *top_color,
274 const struct cell_color *bottom_color,
277 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
278 && cell_color_equal (top_color, bottom_color))
279 xr_draw_line (xr, x, y0, x, y3, top, top_color);
282 if (top != RENDER_LINE_NONE)
283 xr_draw_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
284 if (bottom != RENDER_LINE_NONE)
285 xr_draw_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
290 xrr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
291 enum render_line_style styles[TABLE_N_AXES][2],
292 struct cell_color colors[TABLE_N_AXES][2])
294 const int x0 = bb[H][0];
295 const int y0 = bb[V][0];
296 const int x3 = bb[H][1];
297 const int y3 = bb[V][1];
298 const int top = styles[H][0];
299 const int bottom = styles[H][1];
301 int start_side = render_direction_rtl();
302 int end_side = !start_side;
303 const int start_of_line = styles[V][start_side];
304 const int end_of_line = styles[V][end_side];
305 const struct cell_color *top_color = &colors[H][0];
306 const struct cell_color *bottom_color = &colors[H][1];
307 const struct cell_color *start_color = &colors[V][start_side];
308 const struct cell_color *end_color = &colors[V][end_side];
310 /* The algorithm here is somewhat subtle, to allow it to handle
311 all the kinds of intersections that we need.
313 Three additional ordinates are assigned along the x axis. The
314 first is xc, midway between x0 and x3. The others are x1 and
315 x2; for a single vertical line these are equal to xc, and for
316 a double vertical line they are the ordinates of the left and
317 right half of the double line.
319 yc, y1, and y2 are assigned similarly along the y axis.
321 The following diagram shows the coordinate system and output
322 for double top and bottom lines, single left line, and no
326 y0 ________________________
332 y1 = y2 = yc |######### # |
337 y3 |________#_____#_______|
339 struct xr_fsm *xr = xr_;
341 /* Offset from center of each line in a pair of double lines. */
342 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
344 /* Are the lines along each axis single or double?
345 (It doesn't make sense to have different kinds of line on the
346 same axis, so we don't try to gracefully handle that case.) */
347 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
348 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
350 /* When horizontal lines are doubled,
351 the left-side line along y1 normally runs from x0 to x2,
352 and the right-side line along y1 from x3 to x1.
353 If the top-side line is also doubled, we shorten the y1 lines,
354 so that the left-side line runs only to x1,
355 and the right-side line only to x2.
356 Otherwise, the horizontal line at y = y1 below would cut off
357 the intersection, which looks ugly:
359 y0 ________________________
364 y1 |######### ########|
367 y2 |######################|
370 y3 |______________________|
371 It is more of a judgment call when the horizontal line is
372 single. We actually choose to cut off the line anyhow, as
373 shown in the first diagram above.
375 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
376 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
377 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
378 int horz_line_ofs = double_vert ? double_line_ofs : 0;
379 int xc = (x0 + x3) / 2;
380 int x1 = xc - horz_line_ofs;
381 int x2 = xc + horz_line_ofs;
383 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
384 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
385 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
386 int vert_line_ofs = double_horz ? double_line_ofs : 0;
387 int yc = (y0 + y3) / 2;
388 int y1 = yc - vert_line_ofs;
389 int y2 = yc + vert_line_ofs;
392 xr_draw_horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
393 start_color, end_color, shorten_yc_line);
396 xr_draw_horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
397 start_color, end_color, shorten_y1_lines);
398 xr_draw_horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
399 start_color, end_color, shorten_y2_lines);
403 xr_draw_vert_line (xr, y0, y1, y2, y3, xc, top, bottom,
404 top_color, bottom_color, shorten_xc_line);
407 xr_draw_vert_line (xr, y0, y1, y2, y3, x1, top, bottom,
408 top_color, bottom_color, shorten_x1_lines);
409 xr_draw_vert_line (xr, y0, y1, y2, y3, x2, top, bottom,
410 top_color, bottom_color, shorten_x2_lines);
415 xrr_measure_cell_width (void *xr_, const struct table_cell *cell,
416 int *min_width, int *max_width)
418 struct xr_fsm *xr = xr_;
419 int bb[TABLE_N_AXES][2];
420 int clip[TABLE_N_AXES][2];
427 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
428 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
431 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
433 const int (*margin)[2] = cell->cell_style->margin;
435 *min_width += px_to_xr (margin[H][0] + margin[H][1]);
437 *max_width += px_to_xr (margin[H][0] + margin[H][1]);
441 xrr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
443 struct xr_fsm *xr = xr_;
445 const int (*margin)[2] = cell->cell_style->margin;
447 int bb[TABLE_N_AXES][2] = {
449 [H][1] = width - px_to_xr (margin[H][0] + margin[H][1]),
454 int clip[TABLE_N_AXES][2] = { { 0, 0 }, { 0, 0 } };
457 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
458 h += px_to_xr (margin[V][0] + margin[V][1]);
462 static void xr_clip (struct xr_fsm *, int clip[TABLE_N_AXES][2]);
465 xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
466 int bb[TABLE_N_AXES][2], int valign_offset,
467 int spill[TABLE_N_AXES][2],
468 int clip[TABLE_N_AXES][2])
470 struct xr_fsm *xr = xr_;
473 const struct cell_color *bg = &cell->font_style->bg[color_idx];
474 if ((bg->r != 255 || bg->g != 255 || bg->b != 255) && bg->alpha)
476 cairo_save (xr->cairo);
477 int bg_clip[TABLE_N_AXES][2];
478 for (int axis = 0; axis < TABLE_N_AXES; axis++)
480 bg_clip[axis][0] = clip[axis][0];
481 if (bb[axis][0] == clip[axis][0])
482 bg_clip[axis][0] -= spill[axis][0];
484 bg_clip[axis][1] = clip[axis][1];
485 if (bb[axis][1] == clip[axis][1])
486 bg_clip[axis][1] += spill[axis][1];
488 xr_clip (xr, bg_clip);
489 xr_set_source_rgba (xr->cairo, bg);
491 bb[H][0] - spill[H][0],
492 bb[V][0] - spill[V][0],
493 bb[H][1] + spill[H][1],
494 bb[V][1] + spill[V][1]);
495 cairo_restore (xr->cairo);
497 cairo_save (xr->cairo);
498 if (!xr->style->use_system_colors)
499 xr_set_source_rgba (xr->cairo, &cell->font_style->fg[color_idx]);
501 bb[V][0] += valign_offset;
503 for (int axis = 0; axis < TABLE_N_AXES; axis++)
505 bb[axis][0] += px_to_xr (cell->cell_style->margin[axis][0]);
506 bb[axis][1] -= px_to_xr (cell->cell_style->margin[axis][1]);
508 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
509 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
510 cairo_restore (xr->cairo);
514 xrr_adjust_break (void *xr_, const struct table_cell *cell,
515 int width, int height)
517 struct xr_fsm *xr = xr_;
519 if (xrr_measure_cell_height (xr_, cell, width) < height)
522 const int (*margin)[2] = cell->cell_style->margin;
524 int bb[TABLE_N_AXES][2] = {
527 [H][1] = width - px_to_xr (margin[H][0] + margin[H][1]),
528 [V][1] = height - px_to_xr (margin[V][0] + margin[V][1]),
533 int clip[TABLE_N_AXES][2] = { { 0, 0 }, { 0, 0 } };
536 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
541 xrr_scale (void *xr_, double scale)
543 struct xr_fsm *xr = xr_;
544 cairo_scale (xr->cairo, scale, scale);
548 xr_clip (struct xr_fsm *xr, int clip[TABLE_N_AXES][2])
550 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
552 double x0 = xr_to_pt (clip[H][0]);
553 double y0 = xr_to_pt (clip[V][0]);
554 double x1 = xr_to_pt (clip[H][1]);
555 double y1 = xr_to_pt (clip[V][1]);
557 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
558 cairo_clip (xr->cairo);
563 add_attr (PangoAttrList *list, PangoAttribute *attr,
564 guint start_index, guint end_index)
566 attr->start_index = start_index;
567 attr->end_index = end_index;
568 pango_attr_list_insert (list, attr);
572 markup_escape (struct string *out, bool markup, const char *in, size_t len)
576 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
588 ds_put_cstr (out, "&");
591 ds_put_cstr (out, "<");
594 ds_put_cstr (out, ">");
597 ds_put_byte (out, c);
604 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
606 int size[TABLE_N_AXES];
607 pango_layout_get_size (layout, &size[H], &size[V]);
611 static PangoFontDescription *
612 parse_font (const char *font, int default_size, bool bold, bool italic)
614 if (!c_strcasecmp (font, "Monospaced"))
617 PangoFontDescription *desc = pango_font_description_from_string (font);
621 /* If the font description didn't include an explicit font size, then set it
622 to DEFAULT_SIZE, which is in inch/72000 units. */
623 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
624 pango_font_description_set_size (desc,
625 (default_size / 1000.0) * PANGO_SCALE);
627 pango_font_description_set_weight (desc, (bold
629 : PANGO_WEIGHT_NORMAL));
630 pango_font_description_set_style (desc, (italic
632 : PANGO_STYLE_NORMAL));
638 xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell,
639 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
640 int *widthp, int *brk)
642 const struct pivot_table *pt = to_table_item (xr->item)->pt;
643 const struct font_style *font_style = cell->font_style;
644 const struct cell_style *cell_style = cell->cell_style;
645 unsigned int options = cell->options;
647 enum table_axis X = options & TAB_ROTATE ? V : H;
648 enum table_axis Y = !X;
649 int R = options & TAB_ROTATE ? 0 : 1;
651 PangoFontDescription *desc = NULL;
652 if (font_style->typeface)
654 font_style->typeface,
655 font_style->size ? font_style->size * 1000 : 10000,
656 font_style->bold, font_style->italic);
658 desc = xr->style->font;
661 PangoContext *context = pango_cairo_create_context (xr->cairo);
662 pango_cairo_context_set_resolution (context, xr->style->font_resolution);
663 PangoLayout *layout = pango_layout_new (context);
664 g_object_unref (context);
666 pango_layout_set_font_description (layout, desc);
668 struct string body = DS_EMPTY_INITIALIZER;
669 bool numeric = pivot_value_format_body (cell->value, pt, &body);
671 enum table_halign halign = table_halign_interpret (
672 cell->cell_style->halign, numeric);
674 if (cell_style->halign == TABLE_HALIGN_DECIMAL
675 && !(cell->options & TAB_ROTATE))
677 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
679 const char *decimal = strrchr (ds_cstr (&body),
680 cell_style->decimal_char);
683 pango_layout_set_text (layout, decimal, strlen (decimal));
684 pango_layout_set_width (layout, -1);
685 margin_adjustment += get_layout_dimension (layout, H);
688 if (margin_adjustment < 0)
689 bb[H][1] += margin_adjustment;
692 PangoAttrList *attrs = NULL;
694 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
695 Pango's implementation of it): it will break after a period or a comma
696 that precedes a digit, e.g. in ".000" it will break after the period.
697 This code looks for such a situation and inserts a U+2060 WORD JOINER
698 to prevent the break.
700 This isn't necessary when the decimal point is between two digits
701 (e.g. "0.000" won't be broken) or when the display width is not limited so
702 that word wrapping won't happen.
704 It isn't necessary to look for more than one period or comma, as would
705 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
706 are present then there will always be a digit on both sides of every
708 bool markup = cell->font_style->markup;
711 PangoAttrList *new_attrs;
713 if (pango_parse_markup (ds_cstr (&body), -1, 0,
714 &new_attrs, &new_text, NULL, NULL))
718 body.ss = ss_cstr (new_text);
719 body.capacity = body.ss.length;
723 /* XXX should we report the error? */
726 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
728 const char *text = ds_cstr (&body);
729 const char *decimal = text + strcspn (text, ".,");
731 && c_isdigit (decimal[1])
732 && (decimal == text || !c_isdigit (decimal[-1])))
734 struct string tmp = DS_EMPTY_INITIALIZER;
735 ds_extend (&tmp, ds_length (&body) + 16);
736 markup_escape (&tmp, markup, text, decimal - text + 1);
737 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
738 markup_escape (&tmp, markup, decimal + 1, -1);
739 ds_swap (&tmp, &body);
744 if (font_style->underline)
747 attrs = pango_attr_list_new ();
748 pango_attr_list_insert (attrs, pango_attr_underline_new (
749 PANGO_UNDERLINE_SINGLE));
752 const struct pivot_value *value = cell->value;
753 if (value->n_footnotes || value->n_subscripts)
755 size_t subscript_ofs = ds_length (&body);
756 for (size_t i = 0; i < value->n_subscripts; i++)
759 ds_put_byte (&body, ',');
760 ds_put_cstr (&body, value->subscripts[i]);
763 size_t footnote_ofs = ds_length (&body);
764 size_t n_footnotes = 0;
765 for (size_t i = 0; i < value->n_footnotes; i++)
767 const struct pivot_footnote *f
768 = pt->footnotes[value->footnote_indexes[i]];
772 ds_put_byte (&body, ',');
773 pivot_footnote_format_marker (f, pt, &body);
777 /* Allow footnote markers to occupy the right margin. That way, numbers
778 in the column are still aligned. */
779 if (value->n_footnotes && halign == TABLE_HALIGN_RIGHT)
781 /* Measure the width of the footnote marker, so we know how much we
782 need to make room for. */
783 pango_layout_set_text (layout, ds_cstr (&body) + footnote_ofs,
784 ds_length (&body) - footnote_ofs);
786 PangoAttrList *fn_attrs = pango_attr_list_new ();
787 pango_attr_list_insert (
788 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
789 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
790 pango_layout_set_attributes (layout, fn_attrs);
791 pango_attr_list_unref (fn_attrs);
792 int footnote_width = get_layout_dimension (layout, X);
794 /* Bound the adjustment by the width of the right margin. */
795 int right_margin = px_to_xr (cell_style->margin[X][R]);
796 int footnote_adjustment = MIN (footnote_width, right_margin);
798 /* Adjust the bounding box. */
799 if (options & TAB_ROTATE)
800 footnote_adjustment = -footnote_adjustment;
801 bb[X][R] += footnote_adjustment;
804 pango_layout_set_attributes (layout, NULL);
807 /* Set attributes. */
809 attrs = pango_attr_list_new ();
810 add_attr (attrs, pango_attr_font_desc_new (desc), subscript_ofs,
811 PANGO_ATTR_INDEX_TO_TEXT_END);
812 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
813 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
814 if (value->n_subscripts)
815 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
816 footnote_ofs - subscript_ofs);
817 if (value->n_footnotes)
819 bool superscript = pt->look->footnote_marker_superscripts;
820 add_attr (attrs, pango_attr_rise_new (superscript ? 3000 : -3000),
821 footnote_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
825 /* Set the attributes, if any. */
828 pango_layout_set_attributes (layout, attrs);
829 pango_attr_list_unref (attrs);
833 pango_layout_set_text (layout, ds_cstr (&body), ds_length (&body));
835 pango_layout_set_alignment (layout,
836 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
837 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
838 : PANGO_ALIGN_CENTER));
839 pango_layout_set_width (
841 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
842 pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
844 int size[TABLE_N_AXES];
845 pango_layout_get_size (layout, &size[H], &size[V]);
847 if (clip[H][0] != clip[H][1])
849 cairo_save (xr->cairo);
850 if (!(options & TAB_ROTATE))
852 if (options & TAB_ROTATE)
854 int extra = bb[H][1] - bb[H][0] - size[V];
855 int halign_offset = extra > 0 ? extra / 2 : 0;
856 cairo_translate (xr->cairo,
857 xr_to_pt (bb[H][0] + halign_offset),
858 xr_to_pt (bb[V][1]));
859 cairo_rotate (xr->cairo, -M_PI_2);
862 cairo_translate (xr->cairo,
864 xr_to_pt (bb[V][0]));
865 pango_cairo_show_layout (xr->cairo, layout);
867 /* If enabled, this draws a blue rectangle around the extents of each
868 line of text, which can be rather useful for debugging layout
872 PangoLayoutIter *iter;
873 iter = pango_layout_get_iter (layout);
876 PangoRectangle extents;
878 pango_layout_iter_get_line_extents (iter, &extents, NULL);
879 cairo_save (xr->cairo);
880 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
883 pango_to_xr (extents.x),
884 pango_to_xr (extents.y),
885 pango_to_xr (extents.x + extents.width),
886 pango_to_xr (extents.y + extents.height));
887 cairo_restore (xr->cairo);
889 while (pango_layout_iter_next_line (iter));
890 pango_layout_iter_free (iter);
893 cairo_restore (xr->cairo);
896 int w = pango_to_xr (size[X]);
897 int h = pango_to_xr (size[Y]);
900 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
902 PangoLayoutIter *iter;
905 /* Choose a breakpoint between lines instead of in the middle of one. */
906 iter = pango_layout_get_iter (layout);
909 PangoRectangle extents;
913 pango_layout_iter_get_line_extents (iter, NULL, &extents);
914 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
915 extents.x = pango_to_xr (extents.x);
916 extents.y = pango_to_xr (y0);
917 extents.width = pango_to_xr (extents.width);
918 extents.height = pango_to_xr (y1 - y0);
919 bottom = bb[V][0] + extents.y + extents.height;
920 if (bottom < bb[V][1])
922 if (brk && clip[H][0] != clip[H][1])
930 while (pango_layout_iter_next_line (iter));
931 pango_layout_iter_free (iter);
933 /* If enabled, draws a green line across the chosen breakpoint, which can
934 be useful for debugging issues with breaking. */
938 xr_draw_line (xr, 0, best,
939 xr->style->size[H], best,
941 &(struct cell_color) CELL_COLOR (0, 255, 0));
945 pango_layout_set_attributes (layout, NULL);
947 if (desc != xr->style->font)
948 pango_font_description_free (desc);
949 g_object_unref (G_OBJECT (layout));
956 xr_layout_cell (struct xr_fsm *xr, const struct table_cell *cell,
957 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
958 int *width, int *height, int *brk)
963 /* If enabled, draws a blue rectangle around the cell extents, which can be
964 useful for debugging layout. */
967 if (clip[H][0] != clip[H][1])
969 cairo_save (xr->cairo);
970 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
971 xr_draw_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
972 cairo_restore (xr->cairo);
978 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
981 #define CHART_WIDTH 500
982 #define CHART_HEIGHT 375
984 static struct xr_fsm *
985 xr_fsm_create (const struct output_item *item_,
986 const struct xr_fsm_style *style, cairo_t *cr,
989 if (is_page_setup_item (item_)
990 || is_group_open_item (item_)
991 || is_group_close_item (item_))
994 struct output_item *item;
995 if (is_table_item (item_)
996 || is_chart_item (item_)
997 || is_image_item (item_)
998 || is_page_eject_item (item_))
999 item = output_item_ref (item_);
1000 else if (is_message_item (item_))
1001 item = table_item_super (
1002 text_item_to_table_item (
1003 message_item_to_text_item (
1005 output_item_ref (item_)))));
1006 else if (is_text_item (item_))
1008 if (to_text_item (item_)->type == TEXT_ITEM_PAGE_TITLE)
1011 item = table_item_super (
1012 text_item_to_table_item (
1014 output_item_ref (item_))));
1016 else if (is_group_open_item (item_))
1018 item = table_item_super (
1019 text_item_to_table_item (
1020 text_item_create (TEXT_ITEM_TITLE,
1021 to_group_open_item (item_)->command_name,
1026 assert (is_table_item (item)
1027 || is_chart_item (item)
1028 || is_image_item (item)
1029 || is_page_eject_item (item));
1031 size_t *layer_indexes = NULL;
1032 if (is_table_item (item))
1034 const struct table_item *table_item = to_table_item (item);
1035 layer_indexes = pivot_output_next_layer (table_item->pt, NULL, print);
1040 static const struct render_ops xrr_render_ops = {
1041 .measure_cell_width = xrr_measure_cell_width,
1042 .measure_cell_height = xrr_measure_cell_height,
1043 .adjust_break = xrr_adjust_break,
1044 .draw_line = xrr_draw_line,
1045 .draw_cell = xrr_draw_cell,
1049 enum { LW = XR_LINE_WIDTH, LS = XR_LINE_SPACE };
1050 static const int xr_line_widths[RENDER_N_LINES] =
1052 [RENDER_LINE_NONE] = 0,
1053 [RENDER_LINE_SINGLE] = LW,
1054 [RENDER_LINE_DASHED] = LW,
1055 [RENDER_LINE_THICK] = LW * 2,
1056 [RENDER_LINE_THIN] = LW / 2,
1057 [RENDER_LINE_DOUBLE] = 2 * LW + LS,
1060 struct xr_fsm *fsm = xmalloc (sizeof *fsm);
1061 *fsm = (struct xr_fsm) {
1062 .style = xr_fsm_style_ref (style),
1065 .layer_indexes = layer_indexes,
1067 .ops = &xrr_render_ops,
1069 .size = { [H] = style->size[H], [V] = style->size[V] },
1070 .line_widths = xr_line_widths,
1071 .min_break = { [H] = style->min_break[H], [V] = style->min_break[V] },
1072 .supports_margins = true,
1073 .rtl = render_direction_rtl (),
1078 /* Get font size. */
1079 PangoContext *context = pango_cairo_create_context (cr);
1080 pango_cairo_context_set_resolution (context, style->font_resolution);
1081 PangoLayout *layout = pango_layout_new (context);
1082 g_object_unref (context);
1084 pango_layout_set_font_description (layout, style->font);
1086 pango_layout_set_text (layout, "0", 1);
1088 int char_size[TABLE_N_AXES];
1089 pango_layout_get_size (layout, &char_size[H], &char_size[V]);
1090 for (int a = 0; a < TABLE_N_AXES; a++)
1092 int csa = pango_to_xr (char_size[a]);
1093 fsm->rp.font_size[a] = MAX (fsm->rp.font_size[a], csa);
1096 g_object_unref (G_OBJECT (layout));
1098 if (is_table_item (item))
1100 struct table_item *table_item = to_table_item (item);
1103 fsm->p = render_pager_create (&fsm->rp, table_item, fsm->layer_indexes);
1111 xr_fsm_create_for_printing (const struct output_item *item,
1112 const struct xr_fsm_style *style, cairo_t *cr)
1114 return xr_fsm_create (item, style, cr, true);
1118 xr_fsm_destroy (struct xr_fsm *fsm)
1122 xr_fsm_style_unref (fsm->style);
1123 output_item_unref (fsm->item);
1124 free (fsm->layer_indexes);
1125 render_pager_destroy (fsm->p);
1126 assert (!fsm->cairo);
1131 /* Scrolling API. */
1134 xr_fsm_create_for_scrolling (const struct output_item *item,
1135 const struct xr_fsm_style *style, cairo_t *cr)
1137 return xr_fsm_create (item, style, cr, false);
1141 xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp)
1143 assert (!fsm->print);
1147 if (is_table_item (fsm->item))
1150 w = render_pager_get_size (fsm->p, H) / XR_POINT;
1151 h = render_pager_get_size (fsm->p, V) / XR_POINT;
1154 else if (is_chart_item (fsm->item))
1159 else if (is_image_item (fsm->item))
1161 cairo_surface_t *image = to_image_item (fsm->item)->image;
1162 w = cairo_image_surface_get_width (image);
1163 h = cairo_image_surface_get_height (image);
1175 xr_fsm_draw_all (struct xr_fsm *fsm, cairo_t *cr)
1177 assert (!fsm->print);
1178 xr_fsm_draw_region (fsm, cr, 0, 0, INT_MAX, INT_MAX);
1182 mul_XR_POINT (int x)
1184 return (x >= INT_MAX / XR_POINT ? INT_MAX
1185 : x <= INT_MIN / XR_POINT ? INT_MIN
1190 draw_image (cairo_surface_t *image, cairo_t *cr)
1193 cairo_set_source_surface (cr, image, 0, 0);
1194 cairo_rectangle (cr, 0, 0, cairo_image_surface_get_width (image),
1195 cairo_image_surface_get_height (image));
1202 xr_fsm_draw_region (struct xr_fsm *fsm, cairo_t *cr,
1203 int x, int y, int w, int h)
1205 assert (!fsm->print);
1206 if (is_table_item (fsm->item))
1209 render_pager_draw_region (fsm->p, mul_XR_POINT (x), mul_XR_POINT (y),
1210 mul_XR_POINT (w), mul_XR_POINT (h));
1213 else if (is_image_item (fsm->item))
1214 draw_image (to_image_item (fsm->item)->image, cr);
1215 else if (is_chart_item (fsm->item))
1216 xr_draw_chart (to_chart_item (fsm->item), cr, CHART_WIDTH, CHART_HEIGHT);
1217 else if (is_page_eject_item (fsm->item))
1219 /* Nothing to do. */
1228 xr_fsm_draw_table (struct xr_fsm *fsm, int space)
1230 struct table_item *table_item = to_table_item (fsm->item);
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 (table_item->pt,
1237 fsm->layer_indexes, true);
1238 if (fsm->layer_indexes)
1240 fsm->p = render_pager_create (&fsm->rp, table_item,
1241 fsm->layer_indexes);
1242 if (table_item->pt->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 (to_chart_item (fsm->item), 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 = to_image_item (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_eject (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)
1330 int used = (is_table_item (fsm->item) ? xr_fsm_draw_table (fsm, space)
1331 : is_chart_item (fsm->item) ? xr_fsm_draw_chart (fsm, space)
1332 : is_image_item (fsm->item) ? xr_fsm_draw_image (fsm, space)
1333 : is_page_eject_item (fsm->item) ? xr_fsm_draw_eject (fsm, space)
1342 xr_fsm_is_empty (const struct xr_fsm *fsm)
1344 assert (fsm->print);