1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "output/cairo-fsm.h"
22 #include <pango/pango-layout.h>
23 #include <pango/pango.h>
24 #include <pango/pangocairo.h>
26 #include "libpspp/assertion.h"
27 #include "libpspp/str.h"
28 #include "output/cairo-chart.h"
29 #include "output/chart-provider.h"
30 #include "output/charts/barchart.h"
31 #include "output/charts/boxplot.h"
32 #include "output/charts/np-plot.h"
33 #include "output/charts/piechart.h"
34 #include "output/charts/plot-hist.h"
35 #include "output/charts/roc-chart.h"
36 #include "output/charts/scatterplot.h"
37 #include "output/charts/scree.h"
38 #include "output/charts/spreadlevel-plot.h"
39 #include "output/pivot-output.h"
40 #include "output/pivot-table.h"
41 #include "output/render.h"
42 #include "output/output-item.h"
44 #include "gl/c-ctype.h"
45 #include "gl/c-strcase.h"
46 #include "gl/xalloc.h"
48 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
53 xr_fsm_style_ref (const struct xr_fsm_style *style_)
55 assert (style_->ref_cnt > 0);
57 struct xr_fsm_style *style = CONST_CAST (struct xr_fsm_style *, style_);
63 xr_fsm_style_unshare (struct xr_fsm_style *old)
65 assert (old->ref_cnt > 0);
66 if (old->ref_cnt == 1)
69 xr_fsm_style_unref (old);
71 struct xr_fsm_style *new = xmemdup (old, sizeof *old);
74 new->font = pango_font_description_copy (old->font);
80 xr_fsm_style_unref (struct xr_fsm_style *style)
84 assert (style->ref_cnt > 0);
85 if (!--style->ref_cnt)
87 pango_font_description_free (style->font);
94 xr_fsm_style_equals (const struct xr_fsm_style *a,
95 const struct xr_fsm_style *b)
97 if (a->size[H] != b->size[H]
98 || a->size[V] != b->size[V]
99 || a->min_break[H] != b->min_break[H]
100 || a->min_break[V] != b->min_break[V]
101 || !pango_font_description_equal (a->font, b->font)
102 || a->use_system_colors != b->use_system_colors
103 || a->object_spacing != b->object_spacing
104 || a->font_resolution != b->font_resolution)
110 /* Renders a single output_item to an output device in one of two ways:
112 - 'print == true': Broken across multiple pages if necessary.
114 - 'print == false': In a single region that the user may scroll around if
117 Normally 'output_item' corresponds to a single rendering. There is a
118 special case when 'print == true' and 'output_item' is a table_item with
119 multiple layers and 'item->pt->table_look->print_all_layers == true'. In
120 that case, each layer is rendered separately from the FSM's internal point
121 of view; from the client's point of view, it is all one operation.
125 struct xr_fsm_style *style;
126 struct output_item *item;
129 /* Print mode only. */
132 /* Table items only. */
133 size_t *layer_indexes;
134 struct render_params rp;
135 struct render_pager *p;
136 cairo_t *cairo; /* XXX should this be here?! */
139 /* The unit used for internal measurements is inch/(72 * XR_POINT).
140 (Thus, XR_POINT units represent one point.) */
141 #define XR_POINT PANGO_SCALE
143 /* Conversions to and from points. */
147 return x / (double) XR_POINT;
150 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
154 return x * (PANGO_SCALE * 72 / 96);
158 pango_to_xr (int pango)
160 return (XR_POINT != PANGO_SCALE
161 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
168 return (XR_POINT != PANGO_SCALE
169 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
173 /* Dimensions for drawing lines in tables. */
174 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
175 #define XR_LINE_SPACE XR_POINT /* Space between double lines. */
178 xr_layout_cell (struct xr_fsm *, const struct table_cell *,
179 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
180 int *width, int *height, int *brk);
183 xr_set_source_rgba (cairo_t *cairo, const struct cell_color color)
185 cairo_set_source_rgba (cairo,
186 color.r / 255., color.g / 255., color.b / 255.,
191 xr_draw_line (struct xr_fsm *xr, int x0, int y0, int x1, int y1, int style,
192 const struct cell_color color)
194 cairo_new_path (xr->cairo);
195 cairo_set_line_width (
197 xr_to_pt (style == TABLE_STROKE_THICK ? XR_LINE_WIDTH * 2
198 : style == TABLE_STROKE_THIN ? XR_LINE_WIDTH / 2
200 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0));
201 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1));
203 if (!xr->style->use_system_colors)
204 xr_set_source_rgba (xr->cairo, color);
205 if (style == TABLE_STROKE_DASHED)
206 cairo_set_dash (xr->cairo, (double[]) { 2 }, 1, 0);
207 cairo_stroke (xr->cairo);
208 if (style == TABLE_STROKE_DASHED)
209 cairo_set_dash (xr->cairo, NULL, 0, 0);
213 xr_draw_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
215 cairo_new_path (xr->cairo);
216 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
217 cairo_close_path (xr->cairo);
218 cairo_stroke (xr->cairo);
219 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0));
220 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y0));
221 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1));
222 cairo_line_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y1));
226 fill_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
228 cairo_new_path (xr->cairo);
229 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
230 cairo_rectangle (xr->cairo,
231 xr_to_pt (x0), xr_to_pt (y0),
232 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
233 cairo_fill (xr->cairo);
236 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
237 shortening it to X0...X1 if SHORTEN is true.
238 Draws a horizontal line X1...X3 at Y if RIGHT says so,
239 shortening it to X2...X3 if SHORTEN is true. */
241 xr_draw_horz_line (struct xr_fsm *xr, int x0, int x1, int x2, int x3, int y,
242 enum table_stroke left, enum table_stroke right,
243 const struct cell_color left_color,
244 const struct cell_color right_color,
247 if (left != TABLE_STROKE_NONE && right != TABLE_STROKE_NONE && !shorten
248 && cell_color_equal (left_color, right_color))
249 xr_draw_line (xr, x0, y, x3, y, left, left_color);
252 if (left != TABLE_STROKE_NONE)
253 xr_draw_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
254 if (right != TABLE_STROKE_NONE)
255 xr_draw_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
259 /* Draws a vertical line Y0...Y2 at X if TOP says so,
260 shortening it to Y0...Y1 if SHORTEN is true.
261 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
262 shortening it to Y2...Y3 if SHORTEN is true. */
264 xr_draw_vert_line (struct xr_fsm *xr, int y0, int y1, int y2, int y3, int x,
265 enum table_stroke top, enum table_stroke bottom,
266 const struct cell_color top_color,
267 const struct cell_color bottom_color,
270 if (top != TABLE_STROKE_NONE && bottom != TABLE_STROKE_NONE && !shorten
271 && cell_color_equal (top_color, bottom_color))
272 xr_draw_line (xr, x, y0, x, y3, top, top_color);
275 if (top != TABLE_STROKE_NONE)
276 xr_draw_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
277 if (bottom != TABLE_STROKE_NONE)
278 xr_draw_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
283 xrr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
284 const struct table_border_style styles[TABLE_N_AXES][2])
286 const int x0 = bb[H][0];
287 const int y0 = bb[V][0];
288 const int x3 = bb[H][1];
289 const int y3 = bb[V][1];
290 const enum table_stroke top = styles[H][0].stroke;
291 const enum table_stroke bottom = styles[H][1].stroke;
293 int start_side = render_direction_rtl();
294 int end_side = !start_side;
295 const int start_of_line = styles[V][start_side].stroke;
296 const int end_of_line = styles[V][end_side].stroke;
297 const struct cell_color top_color = styles[H][0].color;
298 const struct cell_color bottom_color = styles[H][1].color;
299 const struct cell_color start_color = styles[V][start_side].color;
300 const struct cell_color end_color = styles[V][end_side].color;
302 /* The algorithm here is somewhat subtle, to allow it to handle
303 all the kinds of intersections that we need.
305 Three additional ordinates are assigned along the x axis. The
306 first is xc, midway between x0 and x3. The others are x1 and
307 x2; for a single vertical line these are equal to xc, and for
308 a double vertical line they are the ordinates of the left and
309 right half of the double line.
311 yc, y1, and y2 are assigned similarly along the y axis.
313 The following diagram shows the coordinate system and output
314 for double top and bottom lines, single left line, and no
318 y0 ________________________
324 y1 = y2 = yc |######### # |
329 y3 |________#_____#_______|
331 struct xr_fsm *xr = xr_;
333 /* Offset from center of each line in a pair of double lines. */
334 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
336 /* Are the lines along each axis single or double?
337 (It doesn't make sense to have different kinds of line on the
338 same axis, so we don't try to gracefully handle that case.) */
339 bool double_vert = top == TABLE_STROKE_DOUBLE || bottom == TABLE_STROKE_DOUBLE;
340 bool double_horz = start_of_line == TABLE_STROKE_DOUBLE || end_of_line == TABLE_STROKE_DOUBLE;
342 /* When horizontal lines are doubled,
343 the left-side line along y1 normally runs from x0 to x2,
344 and the right-side line along y1 from x3 to x1.
345 If the top-side line is also doubled, we shorten the y1 lines,
346 so that the left-side line runs only to x1,
347 and the right-side line only to x2.
348 Otherwise, the horizontal line at y = y1 below would cut off
349 the intersection, which looks ugly:
351 y0 ________________________
356 y1 |######### ########|
359 y2 |######################|
362 y3 |______________________|
363 It is more of a judgment call when the horizontal line is
364 single. We actually choose to cut off the line anyhow, as
365 shown in the first diagram above.
367 bool shorten_y1_lines = top == TABLE_STROKE_DOUBLE;
368 bool shorten_y2_lines = bottom == TABLE_STROKE_DOUBLE;
369 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
370 int horz_line_ofs = double_vert ? double_line_ofs : 0;
371 int xc = (x0 + x3) / 2;
372 int x1 = xc - horz_line_ofs;
373 int x2 = xc + horz_line_ofs;
375 bool shorten_x1_lines = start_of_line == TABLE_STROKE_DOUBLE;
376 bool shorten_x2_lines = end_of_line == TABLE_STROKE_DOUBLE;
377 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
378 int vert_line_ofs = double_horz ? double_line_ofs : 0;
379 int yc = (y0 + y3) / 2;
380 int y1 = yc - vert_line_ofs;
381 int y2 = yc + vert_line_ofs;
384 xr_draw_horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
385 start_color, end_color, shorten_yc_line);
388 xr_draw_horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
389 start_color, end_color, shorten_y1_lines);
390 xr_draw_horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
391 start_color, end_color, shorten_y2_lines);
395 xr_draw_vert_line (xr, y0, y1, y2, y3, xc, top, bottom,
396 top_color, bottom_color, shorten_xc_line);
399 xr_draw_vert_line (xr, y0, y1, y2, y3, x1, top, bottom,
400 top_color, bottom_color, shorten_x1_lines);
401 xr_draw_vert_line (xr, y0, y1, y2, y3, x2, top, bottom,
402 top_color, bottom_color, shorten_x2_lines);
407 xrr_measure_cell_width (void *xr_, const struct table_cell *cell,
408 int *min_width, int *max_width)
410 struct xr_fsm *xr = xr_;
411 int bb[TABLE_N_AXES][2];
412 int clip[TABLE_N_AXES][2];
419 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
420 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
423 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
425 const int (*margin)[2] = cell->cell_style->margin;
427 *min_width += px_to_xr (margin[H][0] + margin[H][1]);
429 *max_width += px_to_xr (margin[H][0] + margin[H][1]);
433 xrr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
435 struct xr_fsm *xr = xr_;
437 const int (*margin)[2] = cell->cell_style->margin;
439 int bb[TABLE_N_AXES][2] = {
441 [H][1] = width - px_to_xr (margin[H][0] + margin[H][1]),
446 int clip[TABLE_N_AXES][2] = { { 0, 0 }, { 0, 0 } };
449 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
450 h += px_to_xr (margin[V][0] + margin[V][1]);
454 static void xr_clip (struct xr_fsm *, int clip[TABLE_N_AXES][2]);
457 xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
458 int bb[TABLE_N_AXES][2], int valign_offset,
459 int spill[TABLE_N_AXES][2],
460 int clip[TABLE_N_AXES][2])
462 struct xr_fsm *xr = xr_;
465 const struct cell_color bg = cell->font_style->bg[color_idx];
466 if ((bg.r != 255 || bg.g != 255 || bg.b != 255) && bg.alpha)
468 cairo_save (xr->cairo);
469 int bg_clip[TABLE_N_AXES][2];
470 for (int axis = 0; axis < TABLE_N_AXES; axis++)
472 bg_clip[axis][0] = clip[axis][0];
473 if (bb[axis][0] == clip[axis][0])
474 bg_clip[axis][0] -= spill[axis][0];
476 bg_clip[axis][1] = clip[axis][1];
477 if (bb[axis][1] == clip[axis][1])
478 bg_clip[axis][1] += spill[axis][1];
480 xr_clip (xr, bg_clip);
481 xr_set_source_rgba (xr->cairo, bg);
483 bb[H][0] - spill[H][0],
484 bb[V][0] - spill[V][0],
485 bb[H][1] + spill[H][1],
486 bb[V][1] + spill[V][1]);
487 cairo_restore (xr->cairo);
489 cairo_save (xr->cairo);
490 if (!xr->style->use_system_colors)
491 xr_set_source_rgba (xr->cairo, cell->font_style->fg[color_idx]);
493 bb[V][0] += valign_offset;
495 for (int axis = 0; axis < TABLE_N_AXES; axis++)
497 bb[axis][0] += px_to_xr (cell->cell_style->margin[axis][0]);
498 bb[axis][1] -= px_to_xr (cell->cell_style->margin[axis][1]);
500 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
501 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
502 cairo_restore (xr->cairo);
506 xrr_adjust_break (void *xr_, const struct table_cell *cell,
507 int width, int height)
509 struct xr_fsm *xr = xr_;
511 if (xrr_measure_cell_height (xr_, cell, width) < height)
514 const int (*margin)[2] = cell->cell_style->margin;
516 int bb[TABLE_N_AXES][2] = {
519 [H][1] = width - px_to_xr (margin[H][0] + margin[H][1]),
520 [V][1] = height - px_to_xr (margin[V][0] + margin[V][1]),
525 int clip[TABLE_N_AXES][2] = { { 0, 0 }, { 0, 0 } };
528 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
533 xrr_scale (void *xr_, double scale)
535 struct xr_fsm *xr = xr_;
536 cairo_scale (xr->cairo, scale, scale);
540 xr_clip (struct xr_fsm *xr, int clip[TABLE_N_AXES][2])
542 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
544 double x0 = xr_to_pt (clip[H][0]);
545 double y0 = xr_to_pt (clip[V][0]);
546 double x1 = xr_to_pt (clip[H][1]);
547 double y1 = xr_to_pt (clip[V][1]);
549 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
550 cairo_clip (xr->cairo);
555 add_attr (PangoAttrList *list, PangoAttribute *attr,
556 guint start_index, guint end_index)
558 attr->start_index = start_index;
559 attr->end_index = end_index;
560 pango_attr_list_insert (list, attr);
564 markup_escape (struct string *out, bool markup, const char *in, size_t len)
568 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
580 ds_put_cstr (out, "&");
583 ds_put_cstr (out, "<");
586 ds_put_cstr (out, ">");
589 ds_put_byte (out, c);
596 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
598 int size[TABLE_N_AXES];
599 pango_layout_get_size (layout, &size[H], &size[V]);
603 static PangoFontDescription *
604 parse_font (const char *font, int default_size, bool bold, bool italic)
606 if (!c_strcasecmp (font, "Monospaced"))
609 PangoFontDescription *desc = pango_font_description_from_string (font);
613 /* If the font description didn't include an explicit font size, then set it
614 to DEFAULT_SIZE, which is in inch/72000 units. */
615 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
616 pango_font_description_set_size (desc,
617 (default_size / 1000.0) * PANGO_SCALE);
619 pango_font_description_set_weight (desc, (bold
621 : PANGO_WEIGHT_NORMAL));
622 pango_font_description_set_style (desc, (italic
624 : PANGO_STYLE_NORMAL));
630 xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell,
631 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
632 int *widthp, int *brk)
634 const struct pivot_table *pt = xr->item->table;
635 const struct font_style *font_style = cell->font_style;
636 const struct cell_style *cell_style = cell->cell_style;
637 unsigned int options = cell->options;
639 enum table_axis X = options & TABLE_CELL_ROTATE ? V : H;
640 enum table_axis Y = !X;
641 int R = options & TABLE_CELL_ROTATE ? 0 : 1;
643 PangoFontDescription *desc = NULL;
644 if (font_style->typeface)
646 font_style->typeface,
647 font_style->size ? font_style->size * 1000 : 10000,
648 font_style->bold, font_style->italic);
650 desc = xr->style->font;
653 PangoContext *context = pango_cairo_create_context (xr->cairo);
654 pango_cairo_context_set_resolution (context, xr->style->font_resolution);
655 PangoLayout *layout = pango_layout_new (context);
656 g_object_unref (context);
658 pango_layout_set_font_description (layout, desc);
660 struct string body = DS_EMPTY_INITIALIZER;
661 bool numeric = pivot_value_format_body (cell->value, pt, &body);
663 enum table_halign halign = table_halign_interpret (
664 cell->cell_style->halign, numeric);
666 if (cell_style->halign == TABLE_HALIGN_DECIMAL
667 && !(cell->options & TABLE_CELL_ROTATE))
669 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
671 const char *decimal = strrchr (ds_cstr (&body),
672 cell_style->decimal_char);
675 pango_layout_set_text (layout, decimal, strlen (decimal));
676 pango_layout_set_width (layout, -1);
677 margin_adjustment += get_layout_dimension (layout, H);
680 if (margin_adjustment < 0)
681 bb[H][1] += margin_adjustment;
684 PangoAttrList *attrs = NULL;
686 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
687 Pango's implementation of it): it will break after a period or a comma
688 that precedes a digit, e.g. in ".000" it will break after the period.
689 This code looks for such a situation and inserts a U+2060 WORD JOINER
690 to prevent the break.
692 This isn't necessary when the decimal point is between two digits
693 (e.g. "0.000" won't be broken) or when the display width is not limited so
694 that word wrapping won't happen.
696 It isn't necessary to look for more than one period or comma, as would
697 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
698 are present then there will always be a digit on both sides of every
700 bool markup = cell->font_style->markup;
703 PangoAttrList *new_attrs;
705 if (pango_parse_markup (ds_cstr (&body), -1, 0,
706 &new_attrs, &new_text, NULL, NULL))
710 body.ss = ss_cstr (new_text);
711 body.capacity = body.ss.length;
715 /* XXX should we report the error? */
718 else if (options & TABLE_CELL_ROTATE || bb[H][1] != INT_MAX)
720 const char *text = ds_cstr (&body);
721 const char *decimal = text + strcspn (text, ".,");
723 && c_isdigit (decimal[1])
724 && (decimal == text || !c_isdigit (decimal[-1])))
726 struct string tmp = DS_EMPTY_INITIALIZER;
727 ds_extend (&tmp, ds_length (&body) + 16);
728 markup_escape (&tmp, markup, text, decimal - text + 1);
729 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
730 markup_escape (&tmp, markup, decimal + 1, -1);
731 ds_swap (&tmp, &body);
736 if (font_style->underline)
739 attrs = pango_attr_list_new ();
740 pango_attr_list_insert (attrs, pango_attr_underline_new (
741 PANGO_UNDERLINE_SINGLE));
744 const struct pivot_value_ex *ex = pivot_value_ex (cell->value);
745 if (ex->n_footnotes || ex->n_subscripts)
747 size_t subscript_ofs = ds_length (&body);
748 for (size_t i = 0; i < ex->n_subscripts; i++)
751 ds_put_byte (&body, ',');
752 ds_put_cstr (&body, ex->subscripts[i]);
755 size_t footnote_ofs = ds_length (&body);
756 size_t n_footnotes = 0;
757 for (size_t i = 0; i < ex->n_footnotes; i++)
759 const struct pivot_footnote *f
760 = pt->footnotes[ex->footnote_indexes[i]];
764 ds_put_byte (&body, ',');
765 pivot_footnote_format_marker (f, pt, &body);
769 /* Allow footnote markers to occupy the right margin. That way, numbers
770 in the column are still aligned. */
771 if (ex->n_footnotes && halign == TABLE_HALIGN_RIGHT)
773 /* Measure the width of the footnote marker, so we know how much we
774 need to make room for. */
775 pango_layout_set_text (layout, ds_cstr (&body) + footnote_ofs,
776 ds_length (&body) - footnote_ofs);
778 PangoAttrList *fn_attrs = pango_attr_list_new ();
779 pango_attr_list_insert (
780 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
781 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
782 pango_layout_set_attributes (layout, fn_attrs);
783 pango_attr_list_unref (fn_attrs);
784 int footnote_width = get_layout_dimension (layout, X);
786 /* Bound the adjustment by the width of the right margin. */
787 int right_margin = px_to_xr (cell_style->margin[X][R]);
788 int footnote_adjustment = MIN (footnote_width, right_margin);
790 /* Adjust the bounding box. */
791 if (options & TABLE_CELL_ROTATE)
792 footnote_adjustment = -footnote_adjustment;
793 bb[X][R] += footnote_adjustment;
796 pango_layout_set_attributes (layout, NULL);
799 /* Set attributes. */
801 attrs = pango_attr_list_new ();
802 add_attr (attrs, pango_attr_font_desc_new (desc), subscript_ofs,
803 PANGO_ATTR_INDEX_TO_TEXT_END);
804 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
805 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
806 if (ex->n_subscripts)
807 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
808 footnote_ofs - subscript_ofs);
811 bool superscript = pt->look->footnote_marker_superscripts;
812 add_attr (attrs, pango_attr_rise_new (superscript ? 3000 : -3000),
813 footnote_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
817 /* Set the attributes, if any. */
820 pango_layout_set_attributes (layout, attrs);
821 pango_attr_list_unref (attrs);
825 pango_layout_set_text (layout, ds_cstr (&body), ds_length (&body));
827 pango_layout_set_alignment (layout,
828 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
829 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
830 : PANGO_ALIGN_CENTER));
831 pango_layout_set_width (
833 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
834 pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
836 int size[TABLE_N_AXES];
837 pango_layout_get_size (layout, &size[H], &size[V]);
839 if (clip[H][0] != clip[H][1])
841 cairo_save (xr->cairo);
842 if (!(options & TABLE_CELL_ROTATE))
844 if (options & TABLE_CELL_ROTATE)
846 int extra = bb[H][1] - bb[H][0] - size[V];
847 int halign_offset = extra > 0 ? extra / 2 : 0;
848 cairo_translate (xr->cairo,
849 xr_to_pt (bb[H][0] + halign_offset),
850 xr_to_pt (bb[V][1]));
851 cairo_rotate (xr->cairo, -M_PI_2);
854 cairo_translate (xr->cairo,
856 xr_to_pt (bb[V][0]));
857 pango_cairo_show_layout (xr->cairo, layout);
859 /* If enabled, this draws a blue rectangle around the extents of each
860 line of text, which can be rather useful for debugging layout
864 PangoLayoutIter *iter;
865 iter = pango_layout_get_iter (layout);
868 PangoRectangle extents;
870 pango_layout_iter_get_line_extents (iter, &extents, NULL);
871 cairo_save (xr->cairo);
872 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
875 pango_to_xr (extents.x),
876 pango_to_xr (extents.y),
877 pango_to_xr (extents.x + extents.width),
878 pango_to_xr (extents.y + extents.height));
879 cairo_restore (xr->cairo);
881 while (pango_layout_iter_next_line (iter));
882 pango_layout_iter_free (iter);
885 cairo_restore (xr->cairo);
888 int w = pango_to_xr (size[X]);
889 int h = pango_to_xr (size[Y]);
892 if (bb[V][0] + h >= bb[V][1] && !(options & TABLE_CELL_ROTATE))
894 PangoLayoutIter *iter;
897 /* Choose a breakpoint between lines instead of in the middle of one. */
898 iter = pango_layout_get_iter (layout);
901 PangoRectangle extents;
905 pango_layout_iter_get_line_extents (iter, NULL, &extents);
906 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
907 extents.x = pango_to_xr (extents.x);
908 extents.y = pango_to_xr (y0);
909 extents.width = pango_to_xr (extents.width);
910 extents.height = pango_to_xr (y1 - y0);
911 bottom = bb[V][0] + extents.y + extents.height;
912 if (bottom < bb[V][1])
914 if (brk && clip[H][0] != clip[H][1])
922 while (pango_layout_iter_next_line (iter));
923 pango_layout_iter_free (iter);
925 /* If enabled, draws a green line across the chosen breakpoint, which can
926 be useful for debugging issues with breaking. */
930 xr_draw_line (xr, 0, best,
931 xr->style->size[H], best,
933 (struct cell_color) CELL_COLOR (0, 255, 0));
937 pango_layout_set_attributes (layout, NULL);
939 if (desc != xr->style->font)
940 pango_font_description_free (desc);
941 g_object_unref (G_OBJECT (layout));
948 xr_layout_cell (struct xr_fsm *xr, const struct table_cell *cell,
949 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
950 int *width, int *height, int *brk)
955 /* If enabled, draws a blue rectangle around the cell extents, which can be
956 useful for debugging layout. */
959 if (clip[H][0] != clip[H][1])
961 cairo_save (xr->cairo);
962 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
963 xr_draw_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
964 cairo_restore (xr->cairo);
970 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
973 #define CHART_WIDTH 500
974 #define CHART_HEIGHT 375
976 static struct xr_fsm *
977 xr_fsm_create (const struct output_item *item_,
978 const struct xr_fsm_style *style, cairo_t *cr,
981 struct output_item *item;
985 case OUTPUT_ITEM_CHART:
986 case OUTPUT_ITEM_IMAGE:
987 case OUTPUT_ITEM_PAGE_BREAK:
988 case OUTPUT_ITEM_TABLE:
989 item = output_item_ref (item_);
992 case OUTPUT_ITEM_GROUP:
995 case OUTPUT_ITEM_MESSAGE:
996 item = text_item_to_table_item (message_item_to_text_item (
997 output_item_ref (item_)));
1000 case OUTPUT_ITEM_TEXT:
1001 if (item_->text.subtype == TEXT_ITEM_PAGE_TITLE)
1004 item = text_item_to_table_item (output_item_ref (item_));
1011 assert (item->type == OUTPUT_ITEM_TABLE
1012 || item->type == OUTPUT_ITEM_CHART
1013 || item->type == OUTPUT_ITEM_IMAGE
1014 || item->type == OUTPUT_ITEM_PAGE_BREAK);
1016 size_t *layer_indexes = NULL;
1017 if (item->type == OUTPUT_ITEM_TABLE)
1019 layer_indexes = pivot_output_next_layer (item->table, NULL, print);
1024 static const struct render_ops xrr_render_ops = {
1025 .measure_cell_width = xrr_measure_cell_width,
1026 .measure_cell_height = xrr_measure_cell_height,
1027 .adjust_break = xrr_adjust_break,
1028 .draw_line = xrr_draw_line,
1029 .draw_cell = xrr_draw_cell,
1033 enum { LW = XR_LINE_WIDTH, LS = XR_LINE_SPACE };
1034 static const int xr_line_widths[TABLE_N_STROKES] =
1036 [TABLE_STROKE_NONE] = 0,
1037 [TABLE_STROKE_SOLID] = LW,
1038 [TABLE_STROKE_DASHED] = LW,
1039 [TABLE_STROKE_THICK] = LW * 2,
1040 [TABLE_STROKE_THIN] = LW / 2,
1041 [TABLE_STROKE_DOUBLE] = 2 * LW + LS,
1044 struct xr_fsm *fsm = xmalloc (sizeof *fsm);
1045 *fsm = (struct xr_fsm) {
1046 .style = xr_fsm_style_ref (style),
1049 .layer_indexes = layer_indexes,
1051 .ops = &xrr_render_ops,
1053 .size = { [H] = style->size[H], [V] = style->size[V] },
1054 .line_widths = xr_line_widths,
1055 .min_break = { [H] = style->min_break[H], [V] = style->min_break[V] },
1056 .px_size = px_to_xr (1),
1057 .supports_margins = true,
1058 .rtl = render_direction_rtl (),
1063 /* Get font size. */
1064 PangoContext *context = pango_cairo_create_context (cr);
1065 pango_cairo_context_set_resolution (context, style->font_resolution);
1066 PangoLayout *layout = pango_layout_new (context);
1067 g_object_unref (context);
1069 pango_layout_set_font_description (layout, style->font);
1071 pango_layout_set_text (layout, "0", 1);
1073 int char_size[TABLE_N_AXES];
1074 pango_layout_get_size (layout, &char_size[H], &char_size[V]);
1075 for (int a = 0; a < TABLE_N_AXES; a++)
1077 int csa = pango_to_xr (char_size[a]);
1078 fsm->rp.font_size[a] = MAX (fsm->rp.font_size[a], csa);
1081 g_object_unref (G_OBJECT (layout));
1083 if (item->type == OUTPUT_ITEM_TABLE)
1086 fsm->p = render_pager_create (&fsm->rp, item->table, fsm->layer_indexes);
1094 xr_fsm_create_for_printing (const struct output_item *item,
1095 const struct xr_fsm_style *style, cairo_t *cr)
1097 return xr_fsm_create (item, style, cr, true);
1101 xr_fsm_destroy (struct xr_fsm *fsm)
1105 xr_fsm_style_unref (fsm->style);
1106 output_item_unref (fsm->item);
1107 free (fsm->layer_indexes);
1108 render_pager_destroy (fsm->p);
1109 assert (!fsm->cairo);
1114 /* Scrolling API. */
1117 xr_fsm_create_for_scrolling (const struct output_item *item,
1118 const struct xr_fsm_style *style, cairo_t *cr)
1120 return xr_fsm_create (item, style, cr, false);
1124 xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp)
1126 assert (!fsm->print);
1130 switch (fsm->item->type)
1132 case OUTPUT_ITEM_CHART:
1137 case OUTPUT_ITEM_IMAGE:
1138 w = cairo_image_surface_get_width (fsm->item->image);
1139 h = cairo_image_surface_get_height (fsm->item->image);
1142 case OUTPUT_ITEM_TABLE:
1144 w = render_pager_get_size (fsm->p, H) / XR_POINT;
1145 h = render_pager_get_size (fsm->p, V) / XR_POINT;
1149 case OUTPUT_ITEM_GROUP:
1150 case OUTPUT_ITEM_MESSAGE:
1151 case OUTPUT_ITEM_PAGE_BREAK:
1152 case OUTPUT_ITEM_TEXT:
1164 xr_fsm_draw_all (struct xr_fsm *fsm, cairo_t *cr)
1166 assert (!fsm->print);
1167 xr_fsm_draw_region (fsm, cr, 0, 0, INT_MAX, INT_MAX);
1171 mul_XR_POINT (int x)
1173 return (x >= INT_MAX / XR_POINT ? INT_MAX
1174 : x <= INT_MIN / XR_POINT ? INT_MIN
1179 draw_image (cairo_surface_t *image, cairo_t *cr)
1182 cairo_set_source_surface (cr, image, 0, 0);
1183 cairo_rectangle (cr, 0, 0, cairo_image_surface_get_width (image),
1184 cairo_image_surface_get_height (image));
1191 xr_fsm_draw_region (struct xr_fsm *fsm, cairo_t *cr,
1192 int x, int y, int w, int h)
1194 assert (!fsm->print);
1195 switch (fsm->item->type)
1197 case OUTPUT_ITEM_CHART:
1198 xr_draw_chart (fsm->item->chart, cr, CHART_WIDTH, CHART_HEIGHT);
1201 case OUTPUT_ITEM_IMAGE:
1202 draw_image (fsm->item->image, cr);
1205 case OUTPUT_ITEM_TABLE:
1207 render_pager_draw_region (fsm->p, mul_XR_POINT (x), mul_XR_POINT (y),
1208 mul_XR_POINT (w), mul_XR_POINT (h));
1212 case OUTPUT_ITEM_GROUP:
1213 case OUTPUT_ITEM_MESSAGE:
1214 case OUTPUT_ITEM_PAGE_BREAK:
1215 case OUTPUT_ITEM_TEXT:
1223 xr_fsm_draw_table (struct xr_fsm *fsm, int space)
1225 int used = render_pager_draw_next (fsm->p, space);
1226 if (!render_pager_has_next (fsm->p))
1228 render_pager_destroy (fsm->p);
1230 fsm->layer_indexes = pivot_output_next_layer (fsm->item->table,
1231 fsm->layer_indexes, true);
1232 if (fsm->layer_indexes)
1234 fsm->p = render_pager_create (&fsm->rp, fsm->item->table,
1235 fsm->layer_indexes);
1236 if (fsm->item->table->look->paginate_layers)
1239 used += fsm->style->object_spacing;
1247 return MIN (used, space);
1251 xr_fsm_draw_chart (struct xr_fsm *fsm, int space)
1253 const int chart_height = 0.8 * MIN (fsm->rp.size[H], fsm->rp.size[V]);
1254 if (space < chart_height)
1258 xr_draw_chart (fsm->item->chart, fsm->cairo,
1259 xr_to_pt (fsm->rp.size[H]), xr_to_pt (chart_height));
1260 return chart_height;
1264 xr_fsm_draw_image (struct xr_fsm *fsm, int space)
1266 cairo_surface_t *image = fsm->item->image;
1267 int width = cairo_image_surface_get_width (image) * XR_POINT;
1268 int height = cairo_image_surface_get_height (image) * XR_POINT;
1269 if (!width || !height)
1272 if (height > fsm->rp.size[V])
1274 double scale = fsm->rp.size[V] / (double) height;
1277 if (!width || !height)
1280 cairo_scale (fsm->cairo, scale, scale);
1283 if (width > fsm->rp.size[H])
1285 double scale = fsm->rp.size[H] / (double) width;
1288 if (!width || !height)
1291 cairo_scale (fsm->cairo, scale, scale);
1297 draw_image (image, fsm->cairo);
1307 xr_fsm_draw_page_break (struct xr_fsm *fsm, int space)
1309 if (space >= fsm->rp.size[V])
1315 xr_fsm_draw_slice (struct xr_fsm *fsm, cairo_t *cr, int space)
1317 assert (fsm->print);
1319 if (fsm->done || space <= 0)
1325 switch (fsm->item->type)
1327 case OUTPUT_ITEM_CHART:
1328 used = xr_fsm_draw_chart (fsm, space);
1331 case OUTPUT_ITEM_IMAGE:
1332 used = xr_fsm_draw_image (fsm, space);
1335 case OUTPUT_ITEM_PAGE_BREAK:
1336 used = xr_fsm_draw_page_break (fsm, space);
1339 case OUTPUT_ITEM_TABLE:
1340 used = xr_fsm_draw_table (fsm, space);
1343 case OUTPUT_ITEM_GROUP:
1344 case OUTPUT_ITEM_MESSAGE:
1345 case OUTPUT_ITEM_TEXT:
1356 xr_fsm_is_empty (const struct xr_fsm *fsm)
1358 assert (fsm->print);