1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "output/cairo-fsm.h"
22 #include <pango/pango-layout.h>
23 #include <pango/pango.h>
24 #include <pango/pangocairo.h>
26 #include "libpspp/assertion.h"
27 #include "libpspp/str.h"
28 #include "output/cairo-chart.h"
29 #include "output/chart-item-provider.h"
30 #include "output/chart-item.h"
31 #include "output/charts/barchart.h"
32 #include "output/charts/boxplot.h"
33 #include "output/charts/np-plot.h"
34 #include "output/charts/piechart.h"
35 #include "output/charts/plot-hist.h"
36 #include "output/charts/roc-chart.h"
37 #include "output/charts/scatterplot.h"
38 #include "output/charts/scree.h"
39 #include "output/charts/spreadlevel-plot.h"
40 #include "output/group-item.h"
41 #include "output/message-item.h"
42 #include "output/page-eject-item.h"
43 #include "output/page-setup-item.h"
44 #include "output/render.h"
45 #include "output/table-item.h"
46 #include "output/text-item.h"
48 #include "gl/c-ctype.h"
49 #include "gl/c-strcase.h"
50 #include "gl/xalloc.h"
52 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
57 xr_fsm_style_ref (const struct xr_fsm_style *style_)
59 assert (style_->ref_cnt > 0);
61 struct xr_fsm_style *style = CONST_CAST (struct xr_fsm_style *, style_);
67 xr_fsm_style_unshare (struct xr_fsm_style *old)
69 assert (old->ref_cnt > 0);
70 if (old->ref_cnt == 1)
73 xr_fsm_style_unref (old);
75 struct xr_fsm_style *new = xmemdup (old, sizeof *old);
77 for (int i = 0; i < XR_N_FONTS; i++)
79 new->fonts[i] = pango_font_description_copy (old->fonts[i]);
85 xr_fsm_style_unref (struct xr_fsm_style *style)
89 assert (style->ref_cnt > 0);
90 if (!--style->ref_cnt)
92 for (size_t i = 0; i < XR_N_FONTS; i++)
93 pango_font_description_free (style->fonts[i]);
100 xr_fsm_style_equals (const struct xr_fsm_style *a,
101 const struct xr_fsm_style *b)
103 if (a->size[H] != b->size[H]
104 || a->size[V] != b->size[V]
105 || a->min_break[H] != b->min_break[H]
106 || a->min_break[V] != b->min_break[V]
107 || a->use_system_colors != b->use_system_colors
108 || a->font_resolution != b->font_resolution)
111 for (size_t i = 0; i < XR_N_FONTS; i++)
112 if (!pango_font_description_equal (a->fonts[i], b->fonts[i]))
120 struct xr_fsm_style *style;
121 struct output_item *item;
123 /* Table items only. */
124 struct render_params rp;
125 struct render_pager *p;
126 cairo_t *cairo; /* XXX should this be here?! */
128 /* Chart and page-eject items only. */
132 /* The unit used for internal measurements is inch/(72 * XR_POINT).
133 (Thus, XR_POINT units represent one point.) */
134 #define XR_POINT PANGO_SCALE
136 /* Conversions to and from points. */
140 return x / (double) XR_POINT;
143 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
147 return x * (PANGO_SCALE * 72 / 96);
151 pango_to_xr (int pango)
153 return (XR_POINT != PANGO_SCALE
154 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
161 return (XR_POINT != PANGO_SCALE
162 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
166 /* Dimensions for drawing lines in tables. */
167 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
168 #define XR_LINE_SPACE XR_POINT /* Space between double lines. */
171 xr_layout_cell (struct xr_fsm *, const struct table_cell *,
172 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
173 int *width, int *height, int *brk);
176 xr_set_source_rgba (cairo_t *cairo, const struct cell_color *color)
178 cairo_set_source_rgba (cairo,
179 color->r / 255., color->g / 255., color->b / 255.,
180 color->alpha / 255.);
184 xr_draw_line (struct xr_fsm *xr, int x0, int y0, int x1, int y1, int style,
185 const struct cell_color *color)
187 cairo_new_path (xr->cairo);
188 if (!xr->style->use_system_colors)
189 xr_set_source_rgba (xr->cairo, color);
190 cairo_set_line_width (
192 xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
193 : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
195 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0));
196 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1));
197 cairo_stroke (xr->cairo);
201 xr_draw_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
203 cairo_new_path (xr->cairo);
204 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
205 cairo_close_path (xr->cairo);
206 cairo_stroke (xr->cairo);
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 (y0));
209 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1));
210 cairo_line_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y1));
214 fill_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
216 cairo_new_path (xr->cairo);
217 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
218 cairo_rectangle (xr->cairo,
219 xr_to_pt (x0), xr_to_pt (y0),
220 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
221 cairo_fill (xr->cairo);
224 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
225 shortening it to X0...X1 if SHORTEN is true.
226 Draws a horizontal line X1...X3 at Y if RIGHT says so,
227 shortening it to X2...X3 if SHORTEN is true. */
229 xr_draw_horz_line (struct xr_fsm *xr, int x0, int x1, int x2, int x3, int y,
230 enum render_line_style left, enum render_line_style right,
231 const struct cell_color *left_color,
232 const struct cell_color *right_color,
235 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
236 && cell_color_equal (left_color, right_color))
237 xr_draw_line (xr, x0, y, x3, y, left, left_color);
240 if (left != RENDER_LINE_NONE)
241 xr_draw_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
242 if (right != RENDER_LINE_NONE)
243 xr_draw_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
247 /* Draws a vertical line Y0...Y2 at X if TOP says so,
248 shortening it to Y0...Y1 if SHORTEN is true.
249 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
250 shortening it to Y2...Y3 if SHORTEN is true. */
252 xr_draw_vert_line (struct xr_fsm *xr, int y0, int y1, int y2, int y3, int x,
253 enum render_line_style top, enum render_line_style bottom,
254 const struct cell_color *top_color,
255 const struct cell_color *bottom_color,
258 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
259 && cell_color_equal (top_color, bottom_color))
260 xr_draw_line (xr, x, y0, x, y3, top, top_color);
263 if (top != RENDER_LINE_NONE)
264 xr_draw_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
265 if (bottom != RENDER_LINE_NONE)
266 xr_draw_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
271 xrr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
272 enum render_line_style styles[TABLE_N_AXES][2],
273 struct cell_color colors[TABLE_N_AXES][2])
275 const int x0 = bb[H][0];
276 const int y0 = bb[V][0];
277 const int x3 = bb[H][1];
278 const int y3 = bb[V][1];
279 const int top = styles[H][0];
280 const int bottom = styles[H][1];
282 int start_side = render_direction_rtl();
283 int end_side = !start_side;
284 const int start_of_line = styles[V][start_side];
285 const int end_of_line = styles[V][end_side];
286 const struct cell_color *top_color = &colors[H][0];
287 const struct cell_color *bottom_color = &colors[H][1];
288 const struct cell_color *start_color = &colors[V][start_side];
289 const struct cell_color *end_color = &colors[V][end_side];
291 /* The algorithm here is somewhat subtle, to allow it to handle
292 all the kinds of intersections that we need.
294 Three additional ordinates are assigned along the x axis. The
295 first is xc, midway between x0 and x3. The others are x1 and
296 x2; for a single vertical line these are equal to xc, and for
297 a double vertical line they are the ordinates of the left and
298 right half of the double line.
300 yc, y1, and y2 are assigned similarly along the y axis.
302 The following diagram shows the coordinate system and output
303 for double top and bottom lines, single left line, and no
307 y0 ________________________
313 y1 = y2 = yc |######### # |
318 y3 |________#_____#_______|
320 struct xr_fsm *xr = xr_;
322 /* Offset from center of each line in a pair of double lines. */
323 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
325 /* Are the lines along each axis single or double?
326 (It doesn't make sense to have different kinds of line on the
327 same axis, so we don't try to gracefully handle that case.) */
328 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
329 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
331 /* When horizontal lines are doubled,
332 the left-side line along y1 normally runs from x0 to x2,
333 and the right-side line along y1 from x3 to x1.
334 If the top-side line is also doubled, we shorten the y1 lines,
335 so that the left-side line runs only to x1,
336 and the right-side line only to x2.
337 Otherwise, the horizontal line at y = y1 below would cut off
338 the intersection, which looks ugly:
340 y0 ________________________
345 y1 |######### ########|
348 y2 |######################|
351 y3 |______________________|
352 It is more of a judgment call when the horizontal line is
353 single. We actually choose to cut off the line anyhow, as
354 shown in the first diagram above.
356 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
357 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
358 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
359 int horz_line_ofs = double_vert ? double_line_ofs : 0;
360 int xc = (x0 + x3) / 2;
361 int x1 = xc - horz_line_ofs;
362 int x2 = xc + horz_line_ofs;
364 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
365 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
366 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
367 int vert_line_ofs = double_horz ? double_line_ofs : 0;
368 int yc = (y0 + y3) / 2;
369 int y1 = yc - vert_line_ofs;
370 int y2 = yc + vert_line_ofs;
373 xr_draw_horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
374 start_color, end_color, shorten_yc_line);
377 xr_draw_horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
378 start_color, end_color, shorten_y1_lines);
379 xr_draw_horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
380 start_color, end_color, shorten_y2_lines);
384 xr_draw_vert_line (xr, y0, y1, y2, y3, xc, top, bottom,
385 top_color, bottom_color, shorten_xc_line);
388 xr_draw_vert_line (xr, y0, y1, y2, y3, x1, top, bottom,
389 top_color, bottom_color, shorten_x1_lines);
390 xr_draw_vert_line (xr, y0, y1, y2, y3, x2, top, bottom,
391 top_color, bottom_color, shorten_x2_lines);
396 xrr_measure_cell_width (void *xr_, const struct table_cell *cell,
397 int *min_width, int *max_width)
399 struct xr_fsm *xr = xr_;
400 int bb[TABLE_N_AXES][2];
401 int clip[TABLE_N_AXES][2];
408 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
409 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
412 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
415 *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
416 + cell->style->cell_style.margin[H][1]);
418 *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
419 + cell->style->cell_style.margin[H][1]);
423 xrr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
425 struct xr_fsm *xr = xr_;
426 int bb[TABLE_N_AXES][2];
427 int clip[TABLE_N_AXES][2];
431 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
432 + cell->style->cell_style.margin[H][1]);
435 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
436 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
437 h += px_to_xr (cell->style->cell_style.margin[V][0]
438 + cell->style->cell_style.margin[V][1]);
442 static void xr_clip (struct xr_fsm *, int clip[TABLE_N_AXES][2]);
445 xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
446 int bb[TABLE_N_AXES][2], int valign_offset,
447 int spill[TABLE_N_AXES][2],
448 int clip[TABLE_N_AXES][2])
450 struct xr_fsm *xr = xr_;
453 const struct cell_color *bg = &cell->style->font_style.bg[color_idx];
454 if ((bg->r != 255 || bg->g != 255 || bg->b != 255) && bg->alpha)
456 cairo_save (xr->cairo);
457 int bg_clip[TABLE_N_AXES][2];
458 for (int axis = 0; axis < TABLE_N_AXES; axis++)
460 bg_clip[axis][0] = clip[axis][0];
461 if (bb[axis][0] == clip[axis][0])
462 bg_clip[axis][0] -= spill[axis][0];
464 bg_clip[axis][1] = clip[axis][1];
465 if (bb[axis][1] == clip[axis][1])
466 bg_clip[axis][1] += spill[axis][1];
468 xr_clip (xr, bg_clip);
469 xr_set_source_rgba (xr->cairo, bg);
471 bb[H][0] - spill[H][0],
472 bb[V][0] - spill[V][0],
473 bb[H][1] + spill[H][1],
474 bb[V][1] + spill[V][1]);
475 cairo_restore (xr->cairo);
477 cairo_save (xr->cairo);
478 if (!xr->style->use_system_colors)
479 xr_set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]);
481 bb[V][0] += valign_offset;
483 for (int axis = 0; axis < TABLE_N_AXES; axis++)
485 bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
486 bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
488 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
489 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
490 cairo_restore (xr->cairo);
494 xrr_adjust_break (void *xr_, const struct table_cell *cell,
495 int width, int height)
497 struct xr_fsm *xr = xr_;
498 int bb[TABLE_N_AXES][2];
499 int clip[TABLE_N_AXES][2];
502 if (xrr_measure_cell_height (xr_, cell, width) < height)
506 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
507 + cell->style->cell_style.margin[H][1]);
511 bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
512 + cell->style->cell_style.margin[V][1]);
513 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
514 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
519 xrr_scale (void *xr_, double scale)
521 struct xr_fsm *xr = xr_;
522 cairo_scale (xr->cairo, scale, scale);
526 xr_clip (struct xr_fsm *xr, int clip[TABLE_N_AXES][2])
528 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
530 double x0 = xr_to_pt (clip[H][0]);
531 double y0 = xr_to_pt (clip[V][0]);
532 double x1 = xr_to_pt (clip[H][1]);
533 double y1 = xr_to_pt (clip[V][1]);
535 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
536 cairo_clip (xr->cairo);
541 add_attr (PangoAttrList *list, PangoAttribute *attr,
542 guint start_index, guint end_index)
544 attr->start_index = start_index;
545 attr->end_index = end_index;
546 pango_attr_list_insert (list, attr);
550 markup_escape (struct string *out, unsigned int options,
551 const char *in, size_t len)
553 if (!(options & TAB_MARKUP))
555 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
567 ds_put_cstr (out, "&");
570 ds_put_cstr (out, "<");
573 ds_put_cstr (out, ">");
576 ds_put_byte (out, c);
583 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
585 int size[TABLE_N_AXES];
586 pango_layout_get_size (layout, &size[H], &size[V]);
590 static PangoFontDescription *
591 parse_font (const char *font, int default_size, bool bold, bool italic)
593 if (!c_strcasecmp (font, "Monospaced"))
596 PangoFontDescription *desc = pango_font_description_from_string (font);
600 /* If the font description didn't include an explicit font size, then set it
601 to DEFAULT_SIZE, which is in inch/72000 units. */
602 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
603 pango_font_description_set_size (desc,
604 (default_size / 1000.0) * PANGO_SCALE);
606 pango_font_description_set_weight (desc, (bold
608 : PANGO_WEIGHT_NORMAL));
609 pango_font_description_set_style (desc, (italic
611 : PANGO_STYLE_NORMAL));
617 xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell,
618 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
619 int *widthp, int *brk)
621 const struct font_style *font_style = &cell->style->font_style;
622 const struct cell_style *cell_style = &cell->style->cell_style;
623 unsigned int options = cell->options;
625 enum table_axis X = options & TAB_ROTATE ? V : H;
626 enum table_axis Y = !X;
627 int R = options & TAB_ROTATE ? 0 : 1;
629 enum xr_font_type font_type = (options & TAB_FIX
631 : XR_FONT_PROPORTIONAL);
632 PangoFontDescription *desc = NULL;
633 if (font_style->typeface)
635 font_style->typeface,
636 font_style->size ? font_style->size * 1000 : 10000,
637 font_style->bold, font_style->italic);
639 desc = xr->style->fonts[font_type];
642 PangoContext *context = pango_cairo_create_context (xr->cairo);
643 pango_cairo_context_set_resolution (context, xr->style->font_resolution);
644 PangoLayout *layout = pango_layout_new (context);
645 g_object_unref (context);
647 pango_layout_set_font_description (layout, desc);
649 const char *text = cell->text;
650 enum table_halign halign = table_halign_interpret (
651 cell_style->halign, cell->options & TAB_NUMERIC);
652 if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
654 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
656 const char *decimal = strrchr (text, cell_style->decimal_char);
659 pango_layout_set_text (layout, decimal, strlen (decimal));
660 pango_layout_set_width (layout, -1);
661 margin_adjustment += get_layout_dimension (layout, H);
664 if (margin_adjustment < 0)
665 bb[H][1] += margin_adjustment;
668 struct string tmp = DS_EMPTY_INITIALIZER;
669 PangoAttrList *attrs = NULL;
671 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
672 Pango's implementation of it): it will break after a period or a comma
673 that precedes a digit, e.g. in ".000" it will break after the period.
674 This code looks for such a situation and inserts a U+2060 WORD JOINER
675 to prevent the break.
677 This isn't necessary when the decimal point is between two digits
678 (e.g. "0.000" won't be broken) or when the display width is not limited so
679 that word wrapping won't happen.
681 It isn't necessary to look for more than one period or comma, as would
682 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
683 are present then there will always be a digit on both sides of every
685 if (options & TAB_MARKUP)
687 PangoAttrList *new_attrs;
689 if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
692 tmp.ss = ss_cstr (new_text);
693 tmp.capacity = tmp.ss.length;
697 /* XXX should we report the error? */
698 ds_put_cstr (&tmp, text);
701 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
703 const char *decimal = text + strcspn (text, ".,");
705 && c_isdigit (decimal[1])
706 && (decimal == text || !c_isdigit (decimal[-1])))
708 ds_extend (&tmp, strlen (text) + 16);
709 markup_escape (&tmp, options, text, decimal - text + 1);
710 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
711 markup_escape (&tmp, options, decimal + 1, -1);
715 if (font_style->underline)
718 attrs = pango_attr_list_new ();
719 pango_attr_list_insert (attrs, pango_attr_underline_new (
720 PANGO_UNDERLINE_SINGLE));
723 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
725 /* If we haven't already put TEXT into tmp, do it now. */
726 if (ds_is_empty (&tmp))
728 ds_extend (&tmp, strlen (text) + 16);
729 markup_escape (&tmp, options, text, -1);
732 size_t subscript_ofs = ds_length (&tmp);
733 for (size_t i = 0; i < cell->n_subscripts; i++)
736 ds_put_byte (&tmp, ',');
737 ds_put_cstr (&tmp, cell->subscripts[i]);
740 size_t superscript_ofs = ds_length (&tmp);
741 if (cell->superscript)
742 ds_put_cstr (&tmp, cell->superscript);
744 size_t footnote_ofs = ds_length (&tmp);
745 for (size_t i = 0; i < cell->n_footnotes; i++)
748 ds_put_byte (&tmp, ',');
749 ds_put_cstr (&tmp, cell->footnotes[i]->marker);
752 /* Allow footnote markers to occupy the right margin. That way, numbers
753 in the column are still aligned. */
754 if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
756 /* Measure the width of the footnote marker, so we know how much we
757 need to make room for. */
758 pango_layout_set_text (layout, ds_cstr (&tmp) + footnote_ofs,
759 ds_length (&tmp) - footnote_ofs);
761 PangoAttrList *fn_attrs = pango_attr_list_new ();
762 pango_attr_list_insert (
763 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
764 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
765 pango_layout_set_attributes (layout, fn_attrs);
766 pango_attr_list_unref (fn_attrs);
767 int footnote_width = get_layout_dimension (layout, X);
769 /* Bound the adjustment by the width of the right margin. */
770 int right_margin = px_to_xr (cell_style->margin[X][R]);
771 int footnote_adjustment = MIN (footnote_width, right_margin);
773 /* Adjust the bounding box. */
774 if (options & TAB_ROTATE)
775 footnote_adjustment = -footnote_adjustment;
776 bb[X][R] += footnote_adjustment;
779 pango_layout_set_attributes (layout, NULL);
782 /* Set attributes. */
784 attrs = pango_attr_list_new ();
785 add_attr (attrs, pango_attr_font_desc_new (desc), subscript_ofs,
786 PANGO_ATTR_INDEX_TO_TEXT_END);
787 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
788 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
789 if (cell->n_subscripts)
790 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
791 superscript_ofs - subscript_ofs);
792 if (cell->superscript || cell->n_footnotes)
793 add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
794 PANGO_ATTR_INDEX_TO_TEXT_END);
797 /* Set the attributes, if any. */
800 pango_layout_set_attributes (layout, attrs);
801 pango_attr_list_unref (attrs);
805 if (ds_is_empty (&tmp))
806 pango_layout_set_text (layout, text, -1);
808 pango_layout_set_text (layout, ds_cstr (&tmp), ds_length (&tmp));
811 pango_layout_set_alignment (layout,
812 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
813 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
814 : PANGO_ALIGN_CENTER));
815 pango_layout_set_width (
817 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
818 pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
820 if (clip[H][0] != clip[H][1])
822 cairo_save (xr->cairo);
823 if (!(options & TAB_ROTATE))
825 if (options & TAB_ROTATE)
827 cairo_translate (xr->cairo,
829 xr_to_pt (bb[V][1]));
830 cairo_rotate (xr->cairo, -M_PI_2);
833 cairo_translate (xr->cairo,
835 xr_to_pt (bb[V][0]));
836 pango_cairo_show_layout (xr->cairo, layout);
838 /* If enabled, this draws a blue rectangle around the extents of each
839 line of text, which can be rather useful for debugging layout
843 PangoLayoutIter *iter;
844 iter = pango_layout_get_iter (layout);
847 PangoRectangle extents;
849 pango_layout_iter_get_line_extents (iter, &extents, NULL);
850 cairo_save (xr->cairo);
851 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
854 pango_to_xr (extents.x),
855 pango_to_xr (extents.y),
856 pango_to_xr (extents.x + extents.width),
857 pango_to_xr (extents.y + extents.height));
858 cairo_restore (xr->cairo);
860 while (pango_layout_iter_next_line (iter));
861 pango_layout_iter_free (iter);
864 cairo_restore (xr->cairo);
867 int size[TABLE_N_AXES];
868 pango_layout_get_size (layout, &size[H], &size[V]);
869 int w = pango_to_xr (size[X]);
870 int h = pango_to_xr (size[Y]);
873 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
875 PangoLayoutIter *iter;
878 /* Choose a breakpoint between lines instead of in the middle of one. */
879 iter = pango_layout_get_iter (layout);
882 PangoRectangle extents;
886 pango_layout_iter_get_line_extents (iter, NULL, &extents);
887 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
888 extents.x = pango_to_xr (extents.x);
889 extents.y = pango_to_xr (y0);
890 extents.width = pango_to_xr (extents.width);
891 extents.height = pango_to_xr (y1 - y0);
892 bottom = bb[V][0] + extents.y + extents.height;
893 if (bottom < bb[V][1])
895 if (brk && clip[H][0] != clip[H][1])
903 while (pango_layout_iter_next_line (iter));
904 pango_layout_iter_free (iter);
906 /* If enabled, draws a green line across the chosen breakpoint, which can
907 be useful for debugging issues with breaking. */
911 xr_draw_line (xr, 0, best,
912 xr->style->size[H], best,
914 &(struct cell_color) CELL_COLOR (0, 255, 0));
918 pango_layout_set_attributes (layout, NULL);
920 if (desc != xr->style->fonts[font_type])
921 pango_font_description_free (desc);
922 g_object_unref (G_OBJECT (layout));
928 xr_layout_cell (struct xr_fsm *xr, const struct table_cell *cell,
929 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
930 int *width, int *height, int *brk)
935 /* If enabled, draws a blue rectangle around the cell extents, which can be
936 useful for debugging layout. */
939 if (clip[H][0] != clip[H][1])
941 cairo_save (xr->cairo);
942 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
943 xr_draw_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
944 cairo_restore (xr->cairo);
950 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
953 #define CHART_WIDTH 500
954 #define CHART_HEIGHT 375
957 xr_fsm_create (const struct output_item *item_,
958 const struct xr_fsm_style *style, cairo_t *cr)
960 if (is_page_setup_item (item_)
961 || is_group_open_item (item_)
962 || is_group_close_item (item_))
965 struct output_item *item;
966 if (is_table_item (item_)
967 || is_chart_item (item_)
968 || is_page_eject_item (item_))
969 item = output_item_ref (item_);
970 else if (is_message_item (item_))
971 item = table_item_super (
972 text_item_to_table_item (
973 message_item_to_text_item (
975 output_item_ref (item_)))));
976 else if (is_text_item (item_))
978 if (to_text_item (item_)->type == TEXT_ITEM_PAGE_TITLE)
981 item = table_item_super (
982 text_item_to_table_item (
984 output_item_ref (item_))));
986 else if (is_group_open_item (item_))
988 item = table_item_super (
989 text_item_to_table_item (
990 text_item_create (TEXT_ITEM_TITLE,
991 to_group_open_item (item_)->command_name)));
995 assert (is_table_item (item)
996 || is_chart_item (item)
997 || is_page_eject_item (item));
999 static const struct render_ops xrr_render_ops = {
1000 .measure_cell_width = xrr_measure_cell_width,
1001 .measure_cell_height = xrr_measure_cell_height,
1002 .adjust_break = xrr_adjust_break,
1003 .draw_line = xrr_draw_line,
1004 .draw_cell = xrr_draw_cell,
1008 enum { LW = XR_LINE_WIDTH, LS = XR_LINE_SPACE };
1009 static const int xr_line_widths[RENDER_N_LINES] =
1011 [RENDER_LINE_NONE] = 0,
1012 [RENDER_LINE_SINGLE] = LW,
1013 [RENDER_LINE_DASHED] = LW,
1014 [RENDER_LINE_THICK] = LW * 2,
1015 [RENDER_LINE_THIN] = LW / 2,
1016 [RENDER_LINE_DOUBLE] = 2 * LW + LS,
1019 struct xr_fsm *fsm = xmalloc (sizeof *fsm);
1020 *fsm = (struct xr_fsm) {
1021 .style = xr_fsm_style_ref (style),
1024 .ops = &xrr_render_ops,
1026 .size = { [H] = style->size[H], [V] = style->size[V] },
1027 .line_widths = xr_line_widths,
1028 .min_break = { [H] = style->min_break[H], [V] = style->min_break[V] },
1029 .supports_margins = true,
1030 .rtl = render_direction_rtl (),
1034 if (is_table_item (item))
1037 fsm->p = render_pager_create (&fsm->rp, to_table_item (item));
1041 for (int i = 0; i < XR_N_FONTS; i++)
1043 PangoContext *context = pango_cairo_create_context (cr);
1044 pango_cairo_context_set_resolution (context, style->font_resolution);
1045 PangoLayout *layout = pango_layout_new (context);
1046 g_object_unref (context);
1048 pango_layout_set_font_description (layout, style->fonts[i]);
1050 pango_layout_set_text (layout, "0", 1);
1052 int char_size[TABLE_N_AXES];
1053 pango_layout_get_size (layout, &char_size[H], &char_size[V]);
1054 for (int a = 0; a < TABLE_N_AXES; a++)
1056 int csa = pango_to_xr (char_size[a]);
1057 fsm->rp.font_size[a] = MAX (fsm->rp.font_size[a], csa);
1060 g_object_unref (G_OBJECT (layout));
1067 xr_fsm_destroy (struct xr_fsm *fsm)
1071 xr_fsm_style_unref (fsm->style);
1072 output_item_unref (fsm->item);
1073 render_pager_destroy (fsm->p);
1074 assert (!fsm->cairo);
1079 /* This is primarily meant for use with screen rendering since the result is a
1080 fixed value for charts. */
1082 xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp)
1086 if (is_table_item (fsm->item))
1089 w = render_pager_get_size (fsm->p, H) / XR_POINT;
1090 h = render_pager_get_size (fsm->p, V) / XR_POINT;
1093 else if (is_chart_item (fsm->item))
1108 xr_fsm_draw_table (struct xr_fsm *fsm, int space)
1110 return (render_pager_has_next (fsm->p)
1111 ? render_pager_draw_next (fsm->p, space)
1116 xr_fsm_draw_chart (struct xr_fsm *fsm, int space)
1118 const int chart_height = 0.8 * MIN (fsm->rp.size[H], fsm->rp.size[V]);
1119 if (space < chart_height)
1123 xr_draw_chart (to_chart_item (fsm->item), fsm->cairo,
1124 xr_to_pt (fsm->rp.size[H]), xr_to_pt (chart_height));
1125 return chart_height;
1129 xr_fsm_draw_eject (struct xr_fsm *fsm, int space)
1131 if (space >= fsm->rp.size[V])
1137 xr_fsm_draw_all (struct xr_fsm *fsm, cairo_t *cr)
1139 xr_fsm_draw_region (fsm, cr, 0, 0, INT_MAX, INT_MAX);
1143 mul_XR_POINT (int x)
1145 return (x >= INT_MAX / XR_POINT ? INT_MAX
1146 : x <= INT_MIN / XR_POINT ? INT_MIN
1151 xr_fsm_draw_region (struct xr_fsm *fsm, cairo_t *cr,
1152 int x, int y, int w, int h)
1154 if (is_table_item (fsm->item))
1157 render_pager_draw_region (fsm->p, mul_XR_POINT (x), mul_XR_POINT (y),
1158 mul_XR_POINT (w), mul_XR_POINT (h));
1161 else if (is_chart_item (fsm->item))
1162 xr_draw_chart (to_chart_item (fsm->item), cr, CHART_WIDTH, CHART_HEIGHT);
1163 else if (is_page_eject_item (fsm->item))
1165 /* Nothing to do. */
1172 xr_fsm_draw_slice (struct xr_fsm *fsm, cairo_t *cr, int space)
1174 if (xr_fsm_is_empty (fsm))
1179 int used = (is_table_item (fsm->item) ? xr_fsm_draw_table (fsm, space)
1180 : is_chart_item (fsm->item) ? xr_fsm_draw_chart (fsm, space)
1181 : is_page_eject_item (fsm->item) ? xr_fsm_draw_eject (fsm, space)
1191 xr_fsm_is_empty (const struct xr_fsm *fsm)
1193 return (is_table_item (fsm->item)
1194 ? !render_pager_has_next (fsm->p)