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 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,
630 font_style->bold, font_style->italic);
632 desc = xr->style->fonts[font_type];
635 PangoContext *context = pango_cairo_create_context (xr->cairo);
636 pango_cairo_context_set_resolution (context, xr->style->font_resolution);
637 PangoLayout *layout = pango_layout_new (context);
638 g_object_unref (context);
640 pango_layout_set_font_description (layout, desc);
642 const char *text = cell->text;
643 enum table_halign halign = table_halign_interpret (
644 cell_style->halign, cell->options & TAB_NUMERIC);
645 if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
647 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
649 const char *decimal = strrchr (text, cell_style->decimal_char);
652 pango_layout_set_text (layout, decimal, strlen (decimal));
653 pango_layout_set_width (layout, -1);
654 margin_adjustment += get_layout_dimension (layout, H);
657 if (margin_adjustment < 0)
658 bb[H][1] += margin_adjustment;
661 struct string tmp = DS_EMPTY_INITIALIZER;
662 PangoAttrList *attrs = NULL;
664 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
665 Pango's implementation of it): it will break after a period or a comma
666 that precedes a digit, e.g. in ".000" it will break after the period.
667 This code looks for such a situation and inserts a U+2060 WORD JOINER
668 to prevent the break.
670 This isn't necessary when the decimal point is between two digits
671 (e.g. "0.000" won't be broken) or when the display width is not limited so
672 that word wrapping won't happen.
674 It isn't necessary to look for more than one period or comma, as would
675 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
676 are present then there will always be a digit on both sides of every
678 if (options & TAB_MARKUP)
680 PangoAttrList *new_attrs;
682 if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
685 tmp.ss = ss_cstr (new_text);
686 tmp.capacity = tmp.ss.length;
690 /* XXX should we report the error? */
691 ds_put_cstr (&tmp, text);
694 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
696 const char *decimal = text + strcspn (text, ".,");
698 && c_isdigit (decimal[1])
699 && (decimal == text || !c_isdigit (decimal[-1])))
701 ds_extend (&tmp, strlen (text) + 16);
702 markup_escape (&tmp, options, text, decimal - text + 1);
703 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
704 markup_escape (&tmp, options, decimal + 1, -1);
708 if (font_style->underline)
711 attrs = pango_attr_list_new ();
712 pango_attr_list_insert (attrs, pango_attr_underline_new (
713 PANGO_UNDERLINE_SINGLE));
716 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
718 /* If we haven't already put TEXT into tmp, do it now. */
719 if (ds_is_empty (&tmp))
721 ds_extend (&tmp, strlen (text) + 16);
722 markup_escape (&tmp, options, text, -1);
725 size_t subscript_ofs = ds_length (&tmp);
726 for (size_t i = 0; i < cell->n_subscripts; i++)
729 ds_put_byte (&tmp, ',');
730 ds_put_cstr (&tmp, cell->subscripts[i]);
733 size_t superscript_ofs = ds_length (&tmp);
734 if (cell->superscript)
735 ds_put_cstr (&tmp, cell->superscript);
737 size_t footnote_ofs = ds_length (&tmp);
738 for (size_t i = 0; i < cell->n_footnotes; i++)
741 ds_put_byte (&tmp, ',');
742 ds_put_cstr (&tmp, cell->footnotes[i]->marker);
745 /* Allow footnote markers to occupy the right margin. That way, numbers
746 in the column are still aligned. */
747 if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
749 /* Measure the width of the footnote marker, so we know how much we
750 need to make room for. */
751 pango_layout_set_text (layout, ds_cstr (&tmp) + footnote_ofs,
752 ds_length (&tmp) - footnote_ofs);
754 PangoAttrList *fn_attrs = pango_attr_list_new ();
755 pango_attr_list_insert (
756 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
757 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
758 pango_layout_set_attributes (layout, fn_attrs);
759 pango_attr_list_unref (fn_attrs);
760 int footnote_width = get_layout_dimension (layout, X);
762 /* Bound the adjustment by the width of the right margin. */
763 int right_margin = px_to_xr (cell_style->margin[X][R]);
764 int footnote_adjustment = MIN (footnote_width, right_margin);
766 /* Adjust the bounding box. */
767 if (options & TAB_ROTATE)
768 footnote_adjustment = -footnote_adjustment;
769 bb[X][R] += footnote_adjustment;
772 pango_layout_set_attributes (layout, NULL);
775 /* Set attributes. */
777 attrs = pango_attr_list_new ();
778 add_attr (attrs, pango_attr_font_desc_new (desc), subscript_ofs,
779 PANGO_ATTR_INDEX_TO_TEXT_END);
780 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
781 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
782 if (cell->n_subscripts)
783 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
784 superscript_ofs - subscript_ofs);
785 if (cell->superscript || cell->n_footnotes)
786 add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
787 PANGO_ATTR_INDEX_TO_TEXT_END);
790 /* Set the attributes, if any. */
793 pango_layout_set_attributes (layout, attrs);
794 pango_attr_list_unref (attrs);
798 if (ds_is_empty (&tmp))
799 pango_layout_set_text (layout, text, -1);
801 pango_layout_set_text (layout, ds_cstr (&tmp), ds_length (&tmp));
804 pango_layout_set_alignment (layout,
805 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
806 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
807 : PANGO_ALIGN_CENTER));
808 pango_layout_set_width (
810 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
811 pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
813 if (clip[H][0] != clip[H][1])
815 cairo_save (xr->cairo);
816 if (!(options & TAB_ROTATE))
818 if (options & TAB_ROTATE)
820 cairo_translate (xr->cairo,
822 xr_to_pt (bb[V][1]));
823 cairo_rotate (xr->cairo, -M_PI_2);
826 cairo_translate (xr->cairo,
828 xr_to_pt (bb[V][0]));
829 pango_cairo_show_layout (xr->cairo, layout);
831 /* If enabled, this draws a blue rectangle around the extents of each
832 line of text, which can be rather useful for debugging layout
836 PangoLayoutIter *iter;
837 iter = pango_layout_get_iter (layout);
840 PangoRectangle extents;
842 pango_layout_iter_get_line_extents (iter, &extents, NULL);
843 cairo_save (xr->cairo);
844 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
847 pango_to_xr (extents.x),
848 pango_to_xr (extents.y),
849 pango_to_xr (extents.x + extents.width),
850 pango_to_xr (extents.y + extents.height));
851 cairo_restore (xr->cairo);
853 while (pango_layout_iter_next_line (iter));
854 pango_layout_iter_free (iter);
857 cairo_restore (xr->cairo);
860 int size[TABLE_N_AXES];
861 pango_layout_get_size (layout, &size[H], &size[V]);
862 int w = pango_to_xr (size[X]);
863 int h = pango_to_xr (size[Y]);
866 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
868 PangoLayoutIter *iter;
871 /* Choose a breakpoint between lines instead of in the middle of one. */
872 iter = pango_layout_get_iter (layout);
875 PangoRectangle extents;
879 pango_layout_iter_get_line_extents (iter, NULL, &extents);
880 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
881 extents.x = pango_to_xr (extents.x);
882 extents.y = pango_to_xr (y0);
883 extents.width = pango_to_xr (extents.width);
884 extents.height = pango_to_xr (y1 - y0);
885 bottom = bb[V][0] + extents.y + extents.height;
886 if (bottom < bb[V][1])
888 if (brk && clip[H][0] != clip[H][1])
896 while (pango_layout_iter_next_line (iter));
897 pango_layout_iter_free (iter);
899 /* If enabled, draws a green line across the chosen breakpoint, which can
900 be useful for debugging issues with breaking. */
904 xr_draw_line (xr, 0, best,
905 xr->style->size[H], best,
907 &(struct cell_color) CELL_COLOR (0, 255, 0));
911 pango_layout_set_attributes (layout, NULL);
913 if (desc != xr->style->fonts[font_type])
914 pango_font_description_free (desc);
915 g_object_unref (G_OBJECT (layout));
921 xr_layout_cell (struct xr_fsm *xr, const struct table_cell *cell,
922 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
923 int *width, int *height, int *brk)
928 /* If enabled, draws a blue rectangle around the cell extents, which can be
929 useful for debugging layout. */
932 if (clip[H][0] != clip[H][1])
934 cairo_save (xr->cairo);
935 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
936 xr_draw_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
937 cairo_restore (xr->cairo);
943 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
948 xr_table_render (struct xr_render_fsm *fsm, struct xr_fsm *xr)
950 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
952 while (render_pager_has_next (ts->p))
956 used = render_pager_draw_next (ts->p, xr->length);
969 xr_table_destroy (struct xr_render_fsm *fsm)
971 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
973 render_pager_destroy (ts->p);
977 static struct xr_render_fsm *
978 xr_render_table (struct xr_fsm *xr, struct table_item *table_item)
980 struct xr_table_state *ts;
982 ts = xmalloc (sizeof *ts);
983 ts->fsm.render = xr_table_render;
984 ts->fsm.destroy = xr_table_destroy;
987 xr->y += xr->char_height;
989 ts->p = render_pager_create (xr->params, table_item);
990 table_item_unref (table_item);
996 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_fsm *xr)
1002 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1004 /* Nothing to do. */
1007 static struct xr_render_fsm *
1008 xr_render_eject (void)
1010 static struct xr_render_fsm eject_renderer =
1016 return &eject_renderer;
1019 #define CHART_WIDTH 500
1020 #define CHART_HEIGHT 375
1022 static struct xr_render_fsm *
1023 xr_render_text (struct xr_fsm *xr, const struct text_item *text_item)
1025 enum text_item_type type = text_item_get_type (text_item);
1029 case TEXT_ITEM_PAGE_TITLE:
1032 case TEXT_ITEM_EJECT_PAGE:
1034 return xr_render_eject ();
1038 return xr_render_table (
1039 xr, text_item_to_table_item (text_item_ref (text_item)));
1046 #define CHART_WIDTH 500
1047 #define CHART_HEIGHT 375
1050 xr_fsm_create (const struct output_item *item_,
1051 const struct xr_fsm_style *style, cairo_t *cr)
1053 if (is_page_setup_item (item_)
1054 || is_group_open_item (item_)
1055 || is_group_close_item (item_))
1058 struct output_item *item;
1059 if (is_table_item (item_)
1060 || is_chart_item (item_)
1061 || is_page_eject_item (item_))
1062 item = output_item_ref (item_);
1063 else if (is_message_item (item_))
1064 item = table_item_super (
1065 text_item_to_table_item (
1066 message_item_to_text_item (
1068 output_item_ref (item_)))));
1069 else if (is_text_item (item_))
1071 if (to_text_item (item_)->type == TEXT_ITEM_PAGE_TITLE)
1074 item = table_item_super (
1075 text_item_to_table_item (
1077 output_item_ref (item_))));
1079 else if (is_group_open_item (item_))
1081 item = table_item_super (
1082 text_item_to_table_item (
1083 text_item_create (TEXT_ITEM_TITLE,
1084 to_group_open_item (item_)->command_name)));
1088 assert (is_table_item (item)
1089 || is_chart_item (item)
1090 || is_page_eject_item (item));
1092 static const struct render_ops xrr_render_ops = {
1093 .measure_cell_width = xrr_measure_cell_width,
1094 .measure_cell_height = xrr_measure_cell_height,
1095 .adjust_break = xrr_adjust_break,
1096 .draw_line = xrr_draw_line,
1097 .draw_cell = xrr_draw_cell,
1100 enum { LW = XR_LINE_WIDTH, LS = XR_LINE_SPACE };
1101 static const int xr_line_widths[RENDER_N_LINES] =
1103 [RENDER_LINE_NONE] = 0,
1104 [RENDER_LINE_SINGLE] = LW,
1105 [RENDER_LINE_DASHED] = LW,
1106 [RENDER_LINE_THICK] = LW * 2,
1107 [RENDER_LINE_THIN] = LW / 2,
1108 [RENDER_LINE_DOUBLE] = 2 * LW + LS,
1111 struct xr_fsm *fsm = xmalloc (sizeof *fsm);
1112 *fsm = (struct xr_fsm) {
1113 .style = xr_fsm_style_ref (style),
1116 .ops = &xrr_render_ops,
1118 .size = { [H] = style->size[H], [V] = style->size[V] },
1119 .line_widths = xr_line_widths,
1120 .min_break = { [H] = style->min_break[H], [V] = style->min_break[V] },
1121 .supports_margins = true,
1122 .rtl = render_direction_rtl (),
1126 if (is_table_item (item))
1129 fsm->p = render_pager_create (&fsm->rp, to_table_item (item));
1133 for (int i = 0; i < XR_N_FONTS; i++)
1135 PangoContext *context = pango_cairo_create_context (cr);
1136 pango_cairo_context_set_resolution (context, style->font_resolution);
1137 PangoLayout *layout = pango_layout_new (context);
1138 g_object_unref (context);
1140 pango_layout_set_font_description (layout, style->fonts[i]);
1142 pango_layout_set_text (layout, "0", 1);
1144 int char_size[TABLE_N_AXES];
1145 pango_layout_get_size (layout, &char_size[H], &char_size[V]);
1146 for (int a = 0; a < TABLE_N_AXES; a++)
1148 int csa = pango_to_xr (char_size[a]);
1149 fsm->rp.font_size[a] = MAX (fsm->rp.font_size[a], csa);
1152 g_object_unref (G_OBJECT (layout));
1159 xr_fsm_destroy (struct xr_fsm *fsm)
1163 xr_fsm_style_unref (fsm->style);
1164 output_item_unref (fsm->item);
1165 render_pager_destroy (fsm->p);
1166 assert (!fsm->cairo);
1171 /* This is primarily meant for use with screen rendering since the result is a
1172 fixed value for charts. */
1174 xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp)
1178 if (is_table_item (fsm->item))
1181 w = render_pager_get_size (fsm->p, H) / XR_POINT;
1182 h = render_pager_get_size (fsm->p, V) / XR_POINT;
1185 else if (is_chart_item (fsm->item))
1200 xr_fsm_draw_table (struct xr_fsm *fsm, int space)
1202 return (render_pager_has_next (fsm->p)
1203 ? render_pager_draw_next (fsm->p, space)
1208 xr_fsm_draw_chart (struct xr_fsm *fsm, int space)
1210 const int chart_height = 0.8 * MIN (fsm->rp.size[H], fsm->rp.size[V]);
1211 if (space < chart_height)
1215 xr_draw_chart (to_chart_item (fsm->item), fsm->cairo,
1216 xr_to_pt (fsm->rp.size[H]), xr_to_pt (chart_height));
1217 return chart_height;
1221 xr_fsm_draw_eject (struct xr_fsm *fsm, int space)
1223 if (space >= fsm->rp.size[V])
1229 xr_fsm_draw_all (struct xr_fsm *fsm, cairo_t *cr)
1231 xr_fsm_draw_region (fsm, cr, 0, 0, INT_MAX, INT_MAX);
1235 mul_XR_POINT (int x)
1237 return (x >= INT_MAX / XR_POINT ? INT_MAX
1238 : x <= INT_MIN / XR_POINT ? INT_MIN
1243 xr_fsm_draw_region (struct xr_fsm *fsm, cairo_t *cr,
1244 int x, int y, int w, int h)
1246 if (is_table_item (fsm->item))
1249 render_pager_draw_region (fsm->p, mul_XR_POINT (x), mul_XR_POINT (y),
1250 mul_XR_POINT (w), mul_XR_POINT (h));
1253 else if (is_chart_item (fsm->item))
1254 xr_draw_chart (to_chart_item (fsm->item), cr, CHART_WIDTH, CHART_HEIGHT);
1255 else if (is_page_eject_item (fsm->item))
1257 /* Nothing to do. */
1264 xr_fsm_draw_slice (struct xr_fsm *fsm, cairo_t *cr, int space)
1266 if (xr_fsm_is_empty (fsm))
1271 int used = (is_table_item (fsm->item) ? xr_fsm_draw_table (fsm, space)
1272 : is_chart_item (fsm->item) ? xr_fsm_draw_chart (fsm, space)
1273 : is_page_eject_item (fsm->item) ? xr_fsm_draw_eject (fsm, space)
1283 xr_fsm_is_empty (const struct xr_fsm *fsm)
1285 return (is_table_item (fsm->item)
1286 ? !render_pager_has_next (fsm->p)