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->transparent != b->transparent
109 || a->font_scale != b->font_scale)
112 for (size_t i = 0; i < XR_N_FONTS; i++)
113 if (!pango_font_description_equal (a->fonts[i], b->fonts[i]))
121 struct xr_fsm_style *style;
122 struct output_item *item;
124 /* Table items only. */
125 struct render_params rp;
126 struct render_pager *p;
127 cairo_t *cairo; /* XXX should this be here?! */
129 /* Chart and page-eject items only. */
133 /* The unit used for internal measurements is inch/(72 * XR_POINT).
134 (Thus, XR_POINT units represent one point.) */
135 #define XR_POINT PANGO_SCALE
137 /* Conversions to and from points. */
141 return x / (double) XR_POINT;
144 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
148 return x * (PANGO_SCALE * 72 / 96);
152 pango_to_xr (int pango)
154 return (XR_POINT != PANGO_SCALE
155 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
162 return (XR_POINT != PANGO_SCALE
163 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
167 /* Dimensions for drawing lines in tables. */
168 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
169 #define XR_LINE_SPACE XR_POINT /* Space between double lines. */
172 xr_layout_cell (struct xr_fsm *, const struct table_cell *,
173 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
174 int *width, int *height, int *brk);
177 xr_set_source_rgba (cairo_t *cairo, const struct cell_color *color)
179 cairo_set_source_rgba (cairo,
180 color->r / 255., color->g / 255., color->b / 255.,
181 color->alpha / 255.);
185 xr_draw_line (struct xr_fsm *xr, int x0, int y0, int x1, int y1, int style,
186 const struct cell_color *color)
188 cairo_new_path (xr->cairo);
189 if (!xr->style->use_system_colors)
190 xr_set_source_rgba (xr->cairo, color);
191 cairo_set_line_width (
193 xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
194 : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
196 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0));
197 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1));
198 cairo_stroke (xr->cairo);
202 xr_draw_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
204 cairo_new_path (xr->cairo);
205 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
206 cairo_close_path (xr->cairo);
207 cairo_stroke (xr->cairo);
208 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0));
209 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y0));
210 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1));
211 cairo_line_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y1));
215 fill_rectangle (struct xr_fsm *xr, int x0, int y0, int x1, int y1)
217 cairo_new_path (xr->cairo);
218 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
219 cairo_rectangle (xr->cairo,
220 xr_to_pt (x0), xr_to_pt (y0),
221 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
222 cairo_fill (xr->cairo);
225 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
226 shortening it to X0...X1 if SHORTEN is true.
227 Draws a horizontal line X1...X3 at Y if RIGHT says so,
228 shortening it to X2...X3 if SHORTEN is true. */
230 xr_draw_horz_line (struct xr_fsm *xr, int x0, int x1, int x2, int x3, int y,
231 enum render_line_style left, enum render_line_style right,
232 const struct cell_color *left_color,
233 const struct cell_color *right_color,
236 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
237 && cell_color_equal (left_color, right_color))
238 xr_draw_line (xr, x0, y, x3, y, left, left_color);
241 if (left != RENDER_LINE_NONE)
242 xr_draw_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
243 if (right != RENDER_LINE_NONE)
244 xr_draw_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
248 /* Draws a vertical line Y0...Y2 at X if TOP says so,
249 shortening it to Y0...Y1 if SHORTEN is true.
250 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
251 shortening it to Y2...Y3 if SHORTEN is true. */
253 xr_draw_vert_line (struct xr_fsm *xr, int y0, int y1, int y2, int y3, int x,
254 enum render_line_style top, enum render_line_style bottom,
255 const struct cell_color *top_color,
256 const struct cell_color *bottom_color,
259 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
260 && cell_color_equal (top_color, bottom_color))
261 xr_draw_line (xr, x, y0, x, y3, top, top_color);
264 if (top != RENDER_LINE_NONE)
265 xr_draw_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
266 if (bottom != RENDER_LINE_NONE)
267 xr_draw_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
272 xrr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
273 enum render_line_style styles[TABLE_N_AXES][2],
274 struct cell_color colors[TABLE_N_AXES][2])
276 const int x0 = bb[H][0];
277 const int y0 = bb[V][0];
278 const int x3 = bb[H][1];
279 const int y3 = bb[V][1];
280 const int top = styles[H][0];
281 const int bottom = styles[H][1];
283 int start_side = render_direction_rtl();
284 int end_side = !start_side;
285 const int start_of_line = styles[V][start_side];
286 const int end_of_line = styles[V][end_side];
287 const struct cell_color *top_color = &colors[H][0];
288 const struct cell_color *bottom_color = &colors[H][1];
289 const struct cell_color *start_color = &colors[V][start_side];
290 const struct cell_color *end_color = &colors[V][end_side];
292 /* The algorithm here is somewhat subtle, to allow it to handle
293 all the kinds of intersections that we need.
295 Three additional ordinates are assigned along the x axis. The
296 first is xc, midway between x0 and x3. The others are x1 and
297 x2; for a single vertical line these are equal to xc, and for
298 a double vertical line they are the ordinates of the left and
299 right half of the double line.
301 yc, y1, and y2 are assigned similarly along the y axis.
303 The following diagram shows the coordinate system and output
304 for double top and bottom lines, single left line, and no
308 y0 ________________________
314 y1 = y2 = yc |######### # |
319 y3 |________#_____#_______|
321 struct xr_fsm *xr = xr_;
323 /* Offset from center of each line in a pair of double lines. */
324 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
326 /* Are the lines along each axis single or double?
327 (It doesn't make sense to have different kinds of line on the
328 same axis, so we don't try to gracefully handle that case.) */
329 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
330 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
332 /* When horizontal lines are doubled,
333 the left-side line along y1 normally runs from x0 to x2,
334 and the right-side line along y1 from x3 to x1.
335 If the top-side line is also doubled, we shorten the y1 lines,
336 so that the left-side line runs only to x1,
337 and the right-side line only to x2.
338 Otherwise, the horizontal line at y = y1 below would cut off
339 the intersection, which looks ugly:
341 y0 ________________________
346 y1 |######### ########|
349 y2 |######################|
352 y3 |______________________|
353 It is more of a judgment call when the horizontal line is
354 single. We actually choose to cut off the line anyhow, as
355 shown in the first diagram above.
357 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
358 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
359 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
360 int horz_line_ofs = double_vert ? double_line_ofs : 0;
361 int xc = (x0 + x3) / 2;
362 int x1 = xc - horz_line_ofs;
363 int x2 = xc + horz_line_ofs;
365 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
366 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
367 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
368 int vert_line_ofs = double_horz ? double_line_ofs : 0;
369 int yc = (y0 + y3) / 2;
370 int y1 = yc - vert_line_ofs;
371 int y2 = yc + vert_line_ofs;
374 xr_draw_horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
375 start_color, end_color, shorten_yc_line);
378 xr_draw_horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
379 start_color, end_color, shorten_y1_lines);
380 xr_draw_horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
381 start_color, end_color, shorten_y2_lines);
385 xr_draw_vert_line (xr, y0, y1, y2, y3, xc, top, bottom,
386 top_color, bottom_color, shorten_xc_line);
389 xr_draw_vert_line (xr, y0, y1, y2, y3, x1, top, bottom,
390 top_color, bottom_color, shorten_x1_lines);
391 xr_draw_vert_line (xr, y0, y1, y2, y3, x2, top, bottom,
392 top_color, bottom_color, shorten_x2_lines);
397 xrr_measure_cell_width (void *xr_, const struct table_cell *cell,
398 int *min_width, int *max_width)
400 struct xr_fsm *xr = xr_;
401 int bb[TABLE_N_AXES][2];
402 int clip[TABLE_N_AXES][2];
409 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
410 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
413 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
416 *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
417 + cell->style->cell_style.margin[H][1]);
419 *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
420 + cell->style->cell_style.margin[H][1]);
424 xrr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
426 struct xr_fsm *xr = xr_;
427 int bb[TABLE_N_AXES][2];
428 int clip[TABLE_N_AXES][2];
432 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
433 + cell->style->cell_style.margin[H][1]);
436 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
437 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
438 h += px_to_xr (cell->style->cell_style.margin[V][0]
439 + cell->style->cell_style.margin[V][1]);
443 static void xr_clip (struct xr_fsm *, int clip[TABLE_N_AXES][2]);
446 xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
447 int bb[TABLE_N_AXES][2], int valign_offset,
448 int spill[TABLE_N_AXES][2],
449 int clip[TABLE_N_AXES][2])
451 struct xr_fsm *xr = xr_;
454 if (!xr->style->transparent)
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, &cell->style->font_style.bg[color_idx]);
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 xr_clip (struct xr_fsm *xr, int clip[TABLE_N_AXES][2])
521 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
523 double x0 = xr_to_pt (clip[H][0]);
524 double y0 = xr_to_pt (clip[V][0]);
525 double x1 = xr_to_pt (clip[H][1]);
526 double y1 = xr_to_pt (clip[V][1]);
528 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
529 cairo_clip (xr->cairo);
534 add_attr (PangoAttrList *list, PangoAttribute *attr,
535 guint start_index, guint end_index)
537 attr->start_index = start_index;
538 attr->end_index = end_index;
539 pango_attr_list_insert (list, attr);
543 markup_escape (struct string *out, unsigned int options,
544 const char *in, size_t len)
546 if (!(options & TAB_MARKUP))
548 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
560 ds_put_cstr (out, "&");
563 ds_put_cstr (out, "<");
566 ds_put_cstr (out, ">");
569 ds_put_byte (out, c);
576 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
578 int size[TABLE_N_AXES];
579 pango_layout_get_size (layout, &size[H], &size[V]);
583 static PangoFontDescription *
584 parse_font (const char *font, int default_size, bool bold, bool italic)
586 if (!c_strcasecmp (font, "Monospaced"))
589 PangoFontDescription *desc = pango_font_description_from_string (font);
593 /* If the font description didn't include an explicit font size, then set it
594 to DEFAULT_SIZE, which is in inch/72000 units. */
595 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
596 pango_font_description_set_size (desc,
597 (default_size / 1000.0) * PANGO_SCALE);
599 pango_font_description_set_weight (desc, (bold
601 : PANGO_WEIGHT_NORMAL));
602 pango_font_description_set_style (desc, (italic
604 : PANGO_STYLE_NORMAL));
610 xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell,
611 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
612 int *widthp, int *brk)
614 const struct font_style *font_style = &cell->style->font_style;
615 const struct cell_style *cell_style = &cell->style->cell_style;
616 unsigned int options = cell->options;
618 enum table_axis X = options & TAB_ROTATE ? V : H;
619 enum table_axis Y = !X;
620 int R = options & TAB_ROTATE ? 0 : 1;
622 enum xr_font_type font_type = (options & TAB_FIX
624 : XR_FONT_PROPORTIONAL);
625 PangoFontDescription *desc = NULL;
626 if (font_style->typeface)
628 font_style->typeface,
629 (font_style->size ? font_style->size * 1000 : 10000) * xr->style->font_scale,
630 font_style->bold, font_style->italic);
632 desc = xr->style->fonts[font_type];
635 PangoContext *context = pango_cairo_create_context (xr->cairo);
636 if (xr->style->font_scale != 1.0)
637 pango_cairo_context_set_resolution (context, 72.0);
638 PangoLayout *layout = pango_layout_new (context);
639 pango_layout_set_font_description (layout, desc);
641 const char *text = cell->text;
642 enum table_halign halign = table_halign_interpret (
643 cell_style->halign, cell->options & TAB_NUMERIC);
644 if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
646 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
648 const char *decimal = strrchr (text, cell_style->decimal_char);
651 pango_layout_set_text (layout, decimal, strlen (decimal));
652 pango_layout_set_width (layout, -1);
653 margin_adjustment += get_layout_dimension (layout, H);
656 if (margin_adjustment < 0)
657 bb[H][1] += margin_adjustment;
660 struct string tmp = DS_EMPTY_INITIALIZER;
661 PangoAttrList *attrs = NULL;
663 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
664 Pango's implementation of it): it will break after a period or a comma
665 that precedes a digit, e.g. in ".000" it will break after the period.
666 This code looks for such a situation and inserts a U+2060 WORD JOINER
667 to prevent the break.
669 This isn't necessary when the decimal point is between two digits
670 (e.g. "0.000" won't be broken) or when the display width is not limited so
671 that word wrapping won't happen.
673 It isn't necessary to look for more than one period or comma, as would
674 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
675 are present then there will always be a digit on both sides of every
677 if (options & TAB_MARKUP)
679 PangoAttrList *new_attrs;
681 if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
684 tmp.ss = ss_cstr (new_text);
685 tmp.capacity = tmp.ss.length;
689 /* XXX should we report the error? */
690 ds_put_cstr (&tmp, text);
693 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
695 const char *decimal = text + strcspn (text, ".,");
697 && c_isdigit (decimal[1])
698 && (decimal == text || !c_isdigit (decimal[-1])))
700 ds_extend (&tmp, strlen (text) + 16);
701 markup_escape (&tmp, options, text, decimal - text + 1);
702 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
703 markup_escape (&tmp, options, decimal + 1, -1);
707 if (font_style->underline)
710 attrs = pango_attr_list_new ();
711 pango_attr_list_insert (attrs, pango_attr_underline_new (
712 PANGO_UNDERLINE_SINGLE));
715 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
717 /* If we haven't already put TEXT into tmp, do it now. */
718 if (ds_is_empty (&tmp))
720 ds_extend (&tmp, strlen (text) + 16);
721 markup_escape (&tmp, options, text, -1);
724 size_t subscript_ofs = ds_length (&tmp);
725 for (size_t i = 0; i < cell->n_subscripts; i++)
728 ds_put_byte (&tmp, ',');
729 ds_put_cstr (&tmp, cell->subscripts[i]);
732 size_t superscript_ofs = ds_length (&tmp);
733 if (cell->superscript)
734 ds_put_cstr (&tmp, cell->superscript);
736 size_t footnote_ofs = ds_length (&tmp);
737 for (size_t i = 0; i < cell->n_footnotes; i++)
740 ds_put_byte (&tmp, ',');
741 ds_put_cstr (&tmp, cell->footnotes[i]->marker);
744 /* Allow footnote markers to occupy the right margin. That way, numbers
745 in the column are still aligned. */
746 if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
748 /* Measure the width of the footnote marker, so we know how much we
749 need to make room for. */
750 pango_layout_set_text (layout, ds_cstr (&tmp) + footnote_ofs,
751 ds_length (&tmp) - footnote_ofs);
753 PangoAttrList *fn_attrs = pango_attr_list_new ();
754 pango_attr_list_insert (
755 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
756 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
757 pango_layout_set_attributes (layout, fn_attrs);
758 pango_attr_list_unref (fn_attrs);
759 int footnote_width = get_layout_dimension (layout, X);
761 /* Bound the adjustment by the width of the right margin. */
762 int right_margin = px_to_xr (cell_style->margin[X][R]);
763 int footnote_adjustment = MIN (footnote_width, right_margin);
765 /* Adjust the bounding box. */
766 if (options & TAB_ROTATE)
767 footnote_adjustment = -footnote_adjustment;
768 bb[X][R] += footnote_adjustment;
771 pango_layout_set_attributes (layout, NULL);
774 /* Set attributes. */
776 attrs = pango_attr_list_new ();
777 add_attr (attrs, pango_attr_font_desc_new (desc), subscript_ofs,
778 PANGO_ATTR_INDEX_TO_TEXT_END);
779 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
780 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
781 if (cell->n_subscripts)
782 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
783 superscript_ofs - subscript_ofs);
784 if (cell->superscript || cell->n_footnotes)
785 add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
786 PANGO_ATTR_INDEX_TO_TEXT_END);
789 /* Set the attributes, if any. */
792 pango_layout_set_attributes (layout, attrs);
793 pango_attr_list_unref (attrs);
797 if (ds_is_empty (&tmp))
798 pango_layout_set_text (layout, text, -1);
800 pango_layout_set_text (layout, ds_cstr (&tmp), ds_length (&tmp));
803 pango_layout_set_alignment (layout,
804 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
805 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
806 : PANGO_ALIGN_CENTER));
807 pango_layout_set_width (
809 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
810 pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
812 if (clip[H][0] != clip[H][1])
814 cairo_save (xr->cairo);
815 if (!(options & TAB_ROTATE))
817 if (options & TAB_ROTATE)
819 cairo_translate (xr->cairo,
821 xr_to_pt (bb[V][1]));
822 cairo_rotate (xr->cairo, -M_PI_2);
825 cairo_translate (xr->cairo,
827 xr_to_pt (bb[V][0]));
828 pango_cairo_show_layout (xr->cairo, layout);
830 /* If enabled, this draws a blue rectangle around the extents of each
831 line of text, which can be rather useful for debugging layout
835 PangoLayoutIter *iter;
836 iter = pango_layout_get_iter (layout);
839 PangoRectangle extents;
841 pango_layout_iter_get_line_extents (iter, &extents, NULL);
842 cairo_save (xr->cairo);
843 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
846 pango_to_xr (extents.x),
847 pango_to_xr (extents.y),
848 pango_to_xr (extents.x + extents.width),
849 pango_to_xr (extents.y + extents.height));
850 cairo_restore (xr->cairo);
852 while (pango_layout_iter_next_line (iter));
853 pango_layout_iter_free (iter);
856 cairo_restore (xr->cairo);
859 int size[TABLE_N_AXES];
860 pango_layout_get_size (layout, &size[H], &size[V]);
861 int w = pango_to_xr (size[X]);
862 int h = pango_to_xr (size[Y]);
865 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
867 PangoLayoutIter *iter;
870 /* Choose a breakpoint between lines instead of in the middle of one. */
871 iter = pango_layout_get_iter (layout);
874 PangoRectangle extents;
878 pango_layout_iter_get_line_extents (iter, NULL, &extents);
879 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
880 extents.x = pango_to_xr (extents.x);
881 extents.y = pango_to_xr (y0);
882 extents.width = pango_to_xr (extents.width);
883 extents.height = pango_to_xr (y1 - y0);
884 bottom = bb[V][0] + extents.y + extents.height;
885 if (bottom < bb[V][1])
887 if (brk && clip[H][0] != clip[H][1])
895 while (pango_layout_iter_next_line (iter));
896 pango_layout_iter_free (iter);
898 /* If enabled, draws a green line across the chosen breakpoint, which can
899 be useful for debugging issues with breaking. */
903 xr_draw_line (xr, 0, best,
904 xr->style->size[H], best,
906 &(struct cell_color) CELL_COLOR (0, 255, 0));
910 pango_layout_set_attributes (layout, NULL);
912 if (desc != xr->style->fonts[font_type])
913 pango_font_description_free (desc);
914 g_object_unref (G_OBJECT (layout));
920 xr_layout_cell (struct xr_fsm *xr, const struct table_cell *cell,
921 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
922 int *width, int *height, int *brk)
927 /* If enabled, draws a blue rectangle around the cell extents, which can be
928 useful for debugging layout. */
931 if (clip[H][0] != clip[H][1])
933 cairo_save (xr->cairo);
934 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
935 xr_draw_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
936 cairo_restore (xr->cairo);
942 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
947 xr_table_render (struct xr_render_fsm *fsm, struct xr_fsm *xr)
949 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
951 while (render_pager_has_next (ts->p))
955 used = render_pager_draw_next (ts->p, xr->length);
968 xr_table_destroy (struct xr_render_fsm *fsm)
970 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
972 render_pager_destroy (ts->p);
976 static struct xr_render_fsm *
977 xr_render_table (struct xr_fsm *xr, struct table_item *table_item)
979 struct xr_table_state *ts;
981 ts = xmalloc (sizeof *ts);
982 ts->fsm.render = xr_table_render;
983 ts->fsm.destroy = xr_table_destroy;
986 xr->y += xr->char_height;
988 ts->p = render_pager_create (xr->params, table_item);
989 table_item_unref (table_item);
995 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_fsm *xr)
1001 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1003 /* Nothing to do. */
1006 static struct xr_render_fsm *
1007 xr_render_eject (void)
1009 static struct xr_render_fsm eject_renderer =
1015 return &eject_renderer;
1018 #define CHART_WIDTH 500
1019 #define CHART_HEIGHT 375
1021 static struct xr_render_fsm *
1022 xr_render_text (struct xr_fsm *xr, const struct text_item *text_item)
1024 enum text_item_type type = text_item_get_type (text_item);
1028 case TEXT_ITEM_PAGE_TITLE:
1031 case TEXT_ITEM_EJECT_PAGE:
1033 return xr_render_eject ();
1037 return xr_render_table (
1038 xr, text_item_to_table_item (text_item_ref (text_item)));
1045 #define CHART_WIDTH 500
1046 #define CHART_HEIGHT 375
1049 xr_fsm_create (const struct output_item *item_,
1050 const struct xr_fsm_style *style, cairo_t *cr)
1052 if (is_page_setup_item (item_)
1053 || is_group_open_item (item_)
1054 || is_group_close_item (item_))
1057 struct output_item *item;
1058 if (is_table_item (item_)
1059 || is_chart_item (item_)
1060 || is_page_eject_item (item_))
1061 item = output_item_ref (item_);
1062 else if (is_message_item (item_))
1063 item = table_item_super (
1064 text_item_to_table_item (
1065 message_item_to_text_item (
1067 output_item_ref (item_)))));
1068 else if (is_text_item (item_))
1070 if (to_text_item (item_)->type == TEXT_ITEM_PAGE_TITLE)
1073 item = table_item_super (
1074 text_item_to_table_item (
1076 output_item_ref (item_))));
1078 else if (is_group_open_item (item_))
1080 item = table_item_super (
1081 text_item_to_table_item (
1082 text_item_create (TEXT_ITEM_TITLE,
1083 to_group_open_item (item_)->command_name)));
1087 assert (is_table_item (item)
1088 || is_chart_item (item)
1089 || is_page_eject_item (item));
1091 static const struct render_ops xrr_render_ops = {
1092 .measure_cell_width = xrr_measure_cell_width,
1093 .measure_cell_height = xrr_measure_cell_height,
1094 .adjust_break = xrr_adjust_break,
1095 .draw_line = xrr_draw_line,
1096 .draw_cell = xrr_draw_cell,
1099 enum { LW = XR_LINE_WIDTH, LS = XR_LINE_SPACE };
1100 static const int xr_line_widths[RENDER_N_LINES] =
1102 [RENDER_LINE_NONE] = 0,
1103 [RENDER_LINE_SINGLE] = LW,
1104 [RENDER_LINE_DASHED] = LW,
1105 [RENDER_LINE_THICK] = LW * 2,
1106 [RENDER_LINE_THIN] = LW / 2,
1107 [RENDER_LINE_DOUBLE] = 2 * LW + LS,
1110 struct xr_fsm *fsm = xmalloc (sizeof *fsm);
1111 *fsm = (struct xr_fsm) {
1112 .style = xr_fsm_style_ref (style),
1115 .ops = &xrr_render_ops,
1117 .size = { [H] = style->size[H], [V] = style->size[V] },
1118 .line_widths = xr_line_widths,
1119 .min_break = { [H] = style->min_break[H], [V] = style->min_break[V] },
1120 .supports_margins = true,
1121 .rtl = render_direction_rtl (),
1125 if (is_table_item (item))
1128 fsm->p = render_pager_create (&fsm->rp, to_table_item (item));
1132 for (int i = 0; i < XR_N_FONTS; i++)
1134 PangoLayout *layout = pango_cairo_create_layout (cr);
1135 pango_layout_set_font_description (layout, style->fonts[i]);
1137 pango_layout_set_text (layout, "0", 1);
1139 int char_size[TABLE_N_AXES];
1140 pango_layout_get_size (layout, &char_size[H], &char_size[V]);
1141 for (int a = 0; a < TABLE_N_AXES; a++)
1143 int csa = pango_to_xr (char_size[a]);
1144 fsm->rp.font_size[a] = MAX (fsm->rp.font_size[a], csa);
1147 g_object_unref (G_OBJECT (layout));
1154 xr_fsm_destroy (struct xr_fsm *fsm)
1158 xr_fsm_style_unref (fsm->style);
1159 output_item_unref (fsm->item);
1160 render_pager_destroy (fsm->p);
1161 assert (!fsm->cairo);
1166 /* This is primarily meant for use with screen rendering since the result is a
1167 fixed value for charts. */
1169 xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp)
1173 if (is_table_item (fsm->item))
1176 w = render_pager_get_size (fsm->p, H) / XR_POINT;
1177 h = render_pager_get_size (fsm->p, V) / XR_POINT;
1180 else if (is_chart_item (fsm->item))
1195 xr_fsm_draw_table (struct xr_fsm *fsm, int space)
1197 return (render_pager_has_next (fsm->p)
1198 ? render_pager_draw_next (fsm->p, space)
1203 xr_fsm_draw_chart (struct xr_fsm *fsm, int space)
1205 const int chart_height = 0.8 * MIN (fsm->rp.size[H], fsm->rp.size[V]);
1206 if (space < chart_height)
1210 xr_draw_chart (to_chart_item (fsm->item), fsm->cairo,
1211 xr_to_pt (fsm->rp.size[H]), xr_to_pt (chart_height));
1212 return chart_height;
1216 xr_fsm_draw_eject (struct xr_fsm *fsm, int space)
1218 if (space >= fsm->rp.size[V])
1224 xr_fsm_draw_all (struct xr_fsm *fsm, cairo_t *cr)
1226 xr_fsm_draw_region (fsm, cr, 0, 0, INT_MAX, INT_MAX);
1230 mul_XR_POINT (int x)
1232 return (x >= INT_MAX / XR_POINT ? INT_MAX
1233 : x <= INT_MIN / XR_POINT ? INT_MIN
1238 xr_fsm_draw_region (struct xr_fsm *fsm, cairo_t *cr,
1239 int x, int y, int w, int h)
1241 if (is_table_item (fsm->item))
1244 render_pager_draw_region (fsm->p, mul_XR_POINT (x), mul_XR_POINT (y),
1245 mul_XR_POINT (w), mul_XR_POINT (h));
1248 else if (is_chart_item (fsm->item))
1249 xr_draw_chart (to_chart_item (fsm->item), cr, CHART_WIDTH, CHART_HEIGHT);
1250 else if (is_page_eject_item (fsm->item))
1252 /* Nothing to do. */
1259 xr_fsm_draw_slice (struct xr_fsm *fsm, cairo_t *cr, int space)
1261 if (xr_fsm_is_empty (fsm))
1266 int used = (is_table_item (fsm->item) ? xr_fsm_draw_table (fsm, space)
1267 : is_chart_item (fsm->item) ? xr_fsm_draw_chart (fsm, space)
1268 : is_page_eject_item (fsm->item) ? xr_fsm_draw_eject (fsm, space)
1278 xr_fsm_is_empty (const struct xr_fsm *fsm)
1280 return (is_table_item (fsm->item)
1281 ? !render_pager_has_next (fsm->p)