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.h"
21 #include "libpspp/assertion.h"
22 #include "libpspp/cast.h"
23 #include "libpspp/hash-functions.h"
24 #include "libpspp/message.h"
25 #include "libpspp/pool.h"
26 #include "libpspp/start-date.h"
27 #include "libpspp/str.h"
28 #include "libpspp/string-map.h"
29 #include "libpspp/version.h"
30 #include "data/file-handle-def.h"
31 #include "output/cairo-chart.h"
32 #include "output/cairo-fsm.h"
33 #include "output/chart-item-provider.h"
34 #include "output/charts/boxplot.h"
35 #include "output/charts/np-plot.h"
36 #include "output/charts/piechart.h"
37 #include "output/charts/barchart.h"
38 #include "output/charts/plot-hist.h"
39 #include "output/charts/roc-chart.h"
40 #include "output/charts/spreadlevel-plot.h"
41 #include "output/charts/scree.h"
42 #include "output/charts/scatterplot.h"
43 #include "output/driver-provider.h"
44 #include "output/group-item.h"
45 #include "output/message-item.h"
46 #include "output/options.h"
47 #include "output/page-eject-item.h"
48 #include "output/page-setup-item.h"
49 #include "output/render.h"
50 #include "output/table-item.h"
51 #include "output/table.h"
52 #include "output/text-item.h"
54 #include <cairo/cairo-pdf.h>
55 #include <cairo/cairo-ps.h>
56 #include <cairo/cairo-svg.h>
58 #include <cairo/cairo.h>
61 #include <pango/pango-font.h>
62 #include <pango/pango-layout.h>
63 #include <pango/pango.h>
64 #include <pango/pangocairo.h>
67 #include "gl/c-ctype.h"
68 #include "gl/c-strcase.h"
69 #include "gl/intprops.h"
70 #include "gl/minmax.h"
71 #include "gl/xalloc.h"
74 #define _(msgid) gettext (msgid)
76 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
80 /* The unit used for internal measurements is inch/(72 * XR_POINT).
81 (Thus, XR_POINT units represent one point.) */
82 #define XR_POINT PANGO_SCALE
84 /* Conversions to and from points. */
88 return x / (double) XR_POINT;
91 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
95 return x * (PANGO_SCALE * 72 / 96);
98 /* Dimensions for drawing lines in tables. */
99 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
100 #define XR_LINE_SPACE XR_POINT /* Space between double lines. */
110 /* A font for use with Cairo. */
113 PangoFontDescription *desc;
117 /* Cairo output driver. */
120 struct output_driver driver;
122 /* User parameters. */
123 struct xr_font fonts[XR_N_FONTS];
125 int width; /* Page width minus margins. */
126 int length; /* Page length minus margins and header. */
128 int left_margin; /* Left margin in inch/(72 * XR_POINT). */
129 int right_margin; /* Right margin in inch/(72 * XR_POINT). */
130 int top_margin; /* Top margin in inch/(72 * XR_POINT). */
131 int bottom_margin; /* Bottom margin in inch/(72 * XR_POINT). */
133 int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
134 int object_spacing; /* Space between output objects. */
136 struct cell_color bg; /* Background color */
137 struct cell_color fg; /* Foreground color */
138 bool transparent; /* true -> do not render background */
139 bool systemcolors; /* true -> do not change colors */
141 int initial_page_number;
143 struct page_heading headings[2]; /* Top and bottom headings. */
144 int headings_height[2];
146 /* Internal state. */
147 struct render_params *params;
148 struct xr_fsm_style *style;
150 int char_width, char_height;
152 cairo_surface_t *surface;
153 int page_number; /* Current page number. */
158 static const struct output_driver_class cairo_driver_class;
160 static void xr_driver_destroy_fsm (struct xr_driver *);
161 static void xr_driver_run_fsm (struct xr_driver *);
163 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
164 enum render_line_style styles[TABLE_N_AXES][2],
165 struct cell_color colors[TABLE_N_AXES][2]);
166 static void xr_measure_cell_width (void *, const struct table_cell *,
168 static int xr_measure_cell_height (void *, const struct table_cell *,
170 static void xr_draw_cell (void *, const struct table_cell *, int color_idx,
171 int bb[TABLE_N_AXES][2], int valign_offset,
172 int spill[TABLE_N_AXES][2],
173 int clip[TABLE_N_AXES][2]);
174 static int xr_adjust_break (void *, const struct table_cell *,
175 int width, int height);
177 /* Output driver basics. */
179 static struct xr_driver *
180 xr_driver_cast (struct output_driver *driver)
182 assert (driver->class == &cairo_driver_class);
183 return UP_CAST (driver, struct xr_driver, driver);
186 static struct driver_option *
187 opt (struct output_driver *d, struct string_map *options, const char *key,
188 const char *default_value)
190 return driver_option_get (d, options, key, default_value);
193 static PangoFontDescription *
194 parse_font (const char *font, int default_size, bool bold, bool italic)
196 if (!c_strcasecmp (font, "Monospaced"))
199 PangoFontDescription *desc = pango_font_description_from_string (font);
203 /* If the font description didn't include an explicit font size, then set it
204 to DEFAULT_SIZE, which is in inch/72000 units. */
205 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
206 pango_font_description_set_size (desc,
207 (default_size / 1000.0) * PANGO_SCALE);
209 pango_font_description_set_weight (desc, (bold
211 : PANGO_WEIGHT_NORMAL));
212 pango_font_description_set_style (desc, (italic
214 : PANGO_STYLE_NORMAL));
219 static PangoFontDescription *
220 parse_font_option (struct output_driver *d, struct string_map *options,
221 const char *key, const char *default_value,
222 int default_size, bool bold, bool italic)
224 char *string = parse_string (opt (d, options, key, default_value));
225 PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
228 msg (MW, _("`%s': bad font specification"), string);
230 /* Fall back to DEFAULT_VALUE, which had better be a valid font
232 desc = parse_font (default_value, default_size, bold, italic);
233 assert (desc != NULL);
241 apply_options (struct xr_driver *xr, struct string_map *o)
243 struct output_driver *d = &xr->driver;
245 /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
246 int left_margin, right_margin;
247 int top_margin, bottom_margin;
248 int paper_width, paper_length;
250 int min_break[TABLE_N_AXES];
252 /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
253 const double scale = XR_POINT / 1000.;
257 for (i = 0; i < XR_N_FONTS; i++)
259 struct xr_font *font = &xr->fonts[i];
261 if (font->desc != NULL)
262 pango_font_description_free (font->desc);
265 font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
266 xr->fonts[XR_FONT_FIXED].desc = parse_font_option
267 (d, o, "fixed-font", "monospace", font_size, false, false);
268 xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font_option (
269 d, o, "prop-font", "sans serif", font_size, false, false);
271 xr->fg = parse_color (opt (d, o, "foreground-color", "#000000000000"));
272 xr->bg = parse_color (opt (d, o, "background-color", "#FFFFFFFFFFFF"));
274 xr->transparent = parse_boolean (opt (d, o, "transparent", "false"));
275 xr->systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
277 /* Get dimensions. */
278 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
279 left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
280 right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
281 top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
282 bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
284 min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
285 min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
287 int object_spacing = (parse_dimension (opt (d, o, "object-spacing", NULL))
290 /* Convert to inch/(XR_POINT * 72). */
291 xr->left_margin = left_margin * scale;
292 xr->right_margin = right_margin * scale;
293 xr->top_margin = top_margin * scale;
294 xr->bottom_margin = bottom_margin * scale;
295 xr->width = (paper_width - left_margin - right_margin) * scale;
296 xr->length = (paper_length - top_margin - bottom_margin) * scale;
297 xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
298 xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
299 xr->object_spacing = object_spacing >= 0 ? object_spacing : XR_POINT * 12;
301 /* There are no headings so headings_height can stay 0. */
304 static struct xr_driver *
305 xr_allocate (const char *name, int device_type, struct string_map *o,
308 struct xr_driver *xr = xzalloc (sizeof *xr);
309 struct output_driver *d = &xr->driver;
311 output_driver_init (d, &cairo_driver_class, name, device_type);
313 /* This is a nasty kluge for an issue that does not make sense. On any
314 surface other than a screen (e.g. for output to PDF or PS or SVG), the
315 fonts are way too big by default. A "9-point" font seems to appear about
316 16 points tall. We use a scale factor for these surfaces to help, but the
317 underlying issue is a mystery. */
318 xr->font_scale = font_scale;
320 apply_options (xr, o);
326 pango_to_xr (int pango)
328 return (XR_POINT != PANGO_SCALE
329 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
336 return (XR_POINT != PANGO_SCALE
337 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
342 xr_measure_fonts (cairo_t *cairo, const struct xr_font fonts[XR_N_FONTS],
343 int *char_width, int *char_height)
347 for (int i = 0; i < XR_N_FONTS; i++)
349 PangoLayout *layout = pango_cairo_create_layout (cairo);
350 pango_layout_set_font_description (layout, fonts[i].desc);
352 pango_layout_set_text (layout, "0", 1);
355 pango_layout_get_size (layout, &cw, &ch);
356 *char_width = MAX (*char_width, pango_to_xr (cw));
357 *char_height = MAX (*char_height, pango_to_xr (ch));
359 g_object_unref (G_OBJECT (layout));
364 get_layout_height (PangoLayout *layout)
367 pango_layout_get_size (layout, &w, &h);
372 xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
373 const struct page_heading *ph, int page_number,
374 int width, bool draw, int base_y)
376 PangoLayout *layout = pango_cairo_create_layout (cairo);
377 pango_layout_set_font_description (layout, font);
380 for (size_t i = 0; i < ph->n; i++)
382 const struct page_paragraph *pp = &ph->paragraphs[i];
384 char *markup = output_driver_substitute_heading_vars (pp->markup,
386 pango_layout_set_markup (layout, markup, -1);
389 pango_layout_set_alignment (
391 (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
392 : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER
393 : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
394 : PANGO_ALIGN_RIGHT));
395 pango_layout_set_width (layout, xr_to_pango (width));
399 cairo_translate (cairo, 0, xr_to_pt (y + base_y));
400 pango_cairo_show_layout (cairo, layout);
401 cairo_restore (cairo);
404 y += pango_to_xr (get_layout_height (layout));
407 g_object_unref (G_OBJECT (layout));
413 xr_measure_headings (cairo_surface_t *surface,
414 const PangoFontDescription *font,
415 const struct page_heading headings[2],
416 int width, int object_spacing, int height[2])
418 cairo_t *cairo = cairo_create (surface);
420 for (int i = 0; i < 2; i++)
422 int h = xr_render_page_heading (cairo, font, &headings[i], -1,
425 /* If the top heading is nonempty, add some space below it. */
433 cairo_destroy (cairo);
438 xr_check_fonts (cairo_surface_t *surface,
439 const struct xr_font fonts[XR_N_FONTS],
440 int usable_width, int usable_length)
442 cairo_t *cairo = cairo_create (surface);
443 int char_width, char_height;
444 xr_measure_fonts (cairo, fonts, &char_width, &char_height);
445 cairo_destroy (cairo);
448 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
449 if (usable_width / char_width < MIN_WIDTH)
451 msg (ME, _("The defined page is not wide enough to hold at least %d "
452 "characters in the default font. In fact, there's only "
453 "room for %d characters."),
454 MIN_WIDTH, usable_width / char_width);
457 if (usable_length / char_height < MIN_LENGTH)
459 msg (ME, _("The defined page is not long enough to hold at least %d "
460 "lines in the default font. In fact, there's only "
461 "room for %d lines."),
462 MIN_LENGTH, usable_length / char_height);
469 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
473 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
475 xr_measure_fonts (xr->cairo, xr->fonts, &xr->char_width, &xr->char_height);
477 for (int i = 0; i < XR_N_FONTS; i++)
479 struct xr_font *font = &xr->fonts[i];
480 font->layout = pango_cairo_create_layout (cairo);
481 pango_layout_set_font_description (font->layout, font->desc);
484 if (xr->params == NULL)
486 static const struct render_ops xr_render_ops = {
487 .draw_line = xr_draw_line,
488 .measure_cell_width = xr_measure_cell_width,
489 .measure_cell_height = xr_measure_cell_height,
490 .adjust_break = xr_adjust_break,
491 .draw_cell = xr_draw_cell,
494 xr->params = xmalloc (sizeof *xr->params);
495 xr->params->ops = &xr_render_ops;
496 xr->params->aux = xr;
497 xr->params->size[H] = xr->width;
498 xr->params->size[V] = xr->length;
499 xr->params->font_size[H] = xr->char_width;
500 xr->params->font_size[V] = xr->char_height;
502 enum { LW = XR_LINE_WIDTH, LS = XR_LINE_SPACE };
503 static const int xr_line_widths[RENDER_N_LINES] =
505 [RENDER_LINE_NONE] = 0,
506 [RENDER_LINE_SINGLE] = LW,
507 [RENDER_LINE_DASHED] = LW,
508 [RENDER_LINE_THICK] = LW * 2,
509 [RENDER_LINE_THIN] = LW / 2,
510 [RENDER_LINE_DOUBLE] = 2 * LW + LS,
512 xr->params->line_widths = xr_line_widths;
514 for (int i = 0; i < TABLE_N_AXES; i++)
515 xr->params->min_break[i] = xr->min_break[i];
516 xr->params->supports_margins = true;
517 xr->params->rtl = render_direction_rtl ();
520 if (xr->style == NULL)
522 xr->style = xmalloc (sizeof *xr->style);
523 *xr->style = (struct xr_fsm_style) {
525 .size = { [H] = xr->width, [V] = xr->length },
526 .min_break = { [H] = xr->min_break[H], [V] = xr->min_break[V] },
527 .use_system_colors = xr->systemcolors,
528 .transparent = xr->transparent,
529 .font_scale = xr->font_scale,
532 for (size_t i = 0; i < XR_N_FONTS; i++)
533 xr->style->fonts[i] = pango_font_description_copy (xr->fonts[i].desc);
535 pango_font_description_to_string (xr->fonts[0].desc),
536 pango_font_description_to_string (xr->fonts[1].desc));
539 if (!xr->systemcolors)
540 cairo_set_source_rgb (xr->cairo,
541 xr->fg.r / 255.0, xr->fg.g / 255.0, xr->fg.b / 255.0);
544 static struct output_driver *
545 xr_create (struct file_handle *fh, enum settings_output_devices device_type,
546 struct string_map *o, enum xr_output_type file_type)
548 const char *file_name = fh_get_file_name (fh);
549 struct xr_driver *xr = xr_allocate (file_name, device_type, o, 72.0 / 128.0);
550 double width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
551 double length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
552 if (file_type == XR_PDF)
553 xr->surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
554 else if (file_type == XR_PS)
555 xr->surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
556 else if (file_type == XR_SVG)
557 xr->surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
561 cairo_status_t status = cairo_surface_status (xr->surface);
562 if (status != CAIRO_STATUS_SUCCESS)
564 msg (ME, _("error opening output file `%s': %s"),
565 file_name, cairo_status_to_string (status));
569 if (!xr_check_fonts (xr->surface, xr->fonts, xr->width, xr->length))
577 output_driver_destroy (&xr->driver);
581 static struct output_driver *
582 xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
583 struct string_map *o)
585 return xr_create (fh, device_type, o, XR_PDF);
588 static struct output_driver *
589 xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
590 struct string_map *o)
592 return xr_create (fh, device_type, o, XR_PS);
595 static struct output_driver *
596 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
597 struct string_map *o)
599 return xr_create (fh, device_type, o, XR_SVG);
603 xr_destroy (struct output_driver *driver)
605 struct xr_driver *xr = xr_driver_cast (driver);
608 xr_driver_destroy_fsm (xr);
610 if (xr->cairo != NULL)
612 cairo_surface_finish (xr->surface);
613 cairo_status_t status = cairo_status (xr->cairo);
614 if (status != CAIRO_STATUS_SUCCESS)
615 fprintf (stderr, _("error drawing output for %s driver: %s"),
616 output_driver_get_name (driver),
617 cairo_status_to_string (status));
618 cairo_surface_destroy (xr->surface);
620 cairo_destroy (xr->cairo);
623 for (i = 0; i < XR_N_FONTS; i++)
625 struct xr_font *font = &xr->fonts[i];
627 if (font->desc != NULL)
628 pango_font_description_free (font->desc);
629 if (font->layout != NULL)
630 g_object_unref (font->layout);
633 xr_fsm_style_unref (xr->style);
639 xr_flush (struct output_driver *driver)
641 struct xr_driver *xr = xr_driver_cast (driver);
643 cairo_surface_flush (cairo_get_target (xr->cairo));
647 xr_update_page_setup (struct output_driver *driver,
648 const struct page_setup *ps)
650 struct xr_driver *xr = xr_driver_cast (driver);
652 xr->initial_page_number = ps->initial_page_number;
653 xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
658 int usable[TABLE_N_AXES];
659 for (int i = 0; i < 2; i++)
660 usable[i] = (ps->paper[i]
661 - (ps->margins[i][0] + ps->margins[i][1])) * 72 * XR_POINT;
663 int headings_height[2];
664 usable[V] -= xr_measure_headings (
665 xr->surface, xr->fonts[XR_FONT_PROPORTIONAL].desc, ps->headings,
666 usable[H], xr->object_spacing, headings_height);
668 enum table_axis h = ps->orientation == PAGE_LANDSCAPE;
669 enum table_axis v = !h;
670 if (!xr_check_fonts (xr->surface, xr->fonts, usable[h], usable[v]))
673 for (int i = 0; i < 2; i++)
675 page_heading_uninit (&xr->headings[i]);
676 page_heading_copy (&xr->headings[i], &ps->headings[i]);
677 xr->headings_height[i] = headings_height[i];
679 xr->width = usable[h];
680 xr->length = usable[v];
681 xr->left_margin = ps->margins[h][0] * 72 * XR_POINT;
682 xr->right_margin = ps->margins[h][1] * 72 * XR_POINT;
683 xr->top_margin = ps->margins[v][0] * 72 * XR_POINT;
684 xr->bottom_margin = ps->margins[v][1] * 72 * XR_POINT;
685 cairo_pdf_surface_set_size (xr->surface,
686 ps->paper[h] * 72.0, ps->paper[v] * 72.0);
690 xr_submit (struct output_driver *driver, const struct output_item *output_item)
692 struct xr_driver *xr = xr_driver_cast (driver);
694 if (is_page_setup_item (output_item))
696 xr_update_page_setup (driver,
697 to_page_setup_item (output_item)->page_setup);
703 xr->page_number = xr->initial_page_number - 1;
704 xr_set_cairo (xr, cairo_create (xr->surface));
705 cairo_save (xr->cairo);
706 xr_driver_next_page (xr, xr->cairo);
709 xr_driver_output_item (xr, output_item);
710 while (xr_driver_need_new_page (xr))
712 cairo_restore (xr->cairo);
713 cairo_show_page (xr->cairo);
714 cairo_save (xr->cairo);
715 xr_driver_next_page (xr, xr->cairo);
719 /* Functions for rendering a series of output items to a series of Cairo
720 contexts, with pagination.
722 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
723 its underlying implementation.
725 See the big comment in cairo.h for intended usage. */
727 /* Gives new page CAIRO to XR for output. */
729 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
731 if (!xr->transparent)
734 cairo_set_source_rgb (cairo,
735 xr->bg.r / 255.0, xr->bg.g / 255.0, xr->bg.b / 255.0);
736 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
738 cairo_restore (cairo);
740 cairo_translate (cairo,
741 xr_to_pt (xr->left_margin),
742 xr_to_pt (xr->top_margin + xr->headings_height[0]));
748 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
749 &xr->headings[0], xr->page_number, xr->width, true,
750 -xr->headings_height[0]);
751 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
752 &xr->headings[1], xr->page_number, xr->width, true,
755 xr_driver_run_fsm (xr);
758 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
759 rendering a previous output item, that is, only if xr_driver_need_new_page()
762 xr_driver_output_item (struct xr_driver *xr,
763 const struct output_item *output_item)
765 assert (xr->fsm == NULL);
766 xr->fsm = xr_fsm_create (output_item, xr->style, xr->cairo);
767 xr_driver_run_fsm (xr);
770 /* Returns true if XR is in the middle of rendering an output item and needs a
771 new page to be appended using xr_driver_next_page() to make progress,
774 xr_driver_need_new_page (const struct xr_driver *xr)
776 return xr->fsm != NULL;
779 /* Returns true if the current page doesn't have any content yet. */
781 xr_driver_is_page_blank (const struct xr_driver *xr)
787 xr_driver_destroy_fsm (struct xr_driver *xr)
789 xr_fsm_destroy (xr->fsm);
794 xr_driver_run_fsm (struct xr_driver *xr)
798 cairo_save (xr->cairo);
799 cairo_translate (xr->cairo, 0, xr_to_pt (xr->y));
800 int used = xr_fsm_draw_slice (xr->fsm, xr->cairo, xr->length - xr->y);
801 printf ("draw slice at %d -> size %d\n", xr->y, used);
803 cairo_restore (xr->cairo);
805 if (xr_fsm_is_empty (xr->fsm))
806 xr_driver_destroy_fsm (xr);
811 xr_layout_cell (struct xr_driver *, const struct table_cell *,
812 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
813 int *width, int *height, int *brk);
816 set_source_rgba (cairo_t *cairo, const struct cell_color *color)
818 cairo_set_source_rgba (cairo,
819 color->r / 255., color->g / 255., color->b / 255.,
820 color->alpha / 255.);
824 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
825 const struct cell_color *color)
827 cairo_new_path (xr->cairo);
828 if (!xr->systemcolors)
829 set_source_rgba (xr->cairo, color);
830 cairo_set_line_width (
832 xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
833 : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
835 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
836 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
837 cairo_stroke (xr->cairo);
841 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
843 cairo_new_path (xr->cairo);
844 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
845 cairo_close_path (xr->cairo);
846 cairo_stroke (xr->cairo);
847 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
848 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y0 + xr->y));
849 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
850 cairo_line_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y1 + xr->y));
854 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
856 cairo_new_path (xr->cairo);
857 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
858 cairo_rectangle (xr->cairo,
859 xr_to_pt (x0), xr_to_pt (y0 + xr->y),
860 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
861 cairo_fill (xr->cairo);
864 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
865 shortening it to X0...X1 if SHORTEN is true.
866 Draws a horizontal line X1...X3 at Y if RIGHT says so,
867 shortening it to X2...X3 if SHORTEN is true. */
869 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
870 enum render_line_style left, enum render_line_style right,
871 const struct cell_color *left_color,
872 const struct cell_color *right_color,
875 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
876 && cell_color_equal (left_color, right_color))
877 dump_line (xr, x0, y, x3, y, left, left_color);
880 if (left != RENDER_LINE_NONE)
881 dump_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
882 if (right != RENDER_LINE_NONE)
883 dump_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
887 /* Draws a vertical line Y0...Y2 at X if TOP says so,
888 shortening it to Y0...Y1 if SHORTEN is true.
889 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
890 shortening it to Y2...Y3 if SHORTEN is true. */
892 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
893 enum render_line_style top, enum render_line_style bottom,
894 const struct cell_color *top_color,
895 const struct cell_color *bottom_color,
898 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
899 && cell_color_equal (top_color, bottom_color))
900 dump_line (xr, x, y0, x, y3, top, top_color);
903 if (top != RENDER_LINE_NONE)
904 dump_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
905 if (bottom != RENDER_LINE_NONE)
906 dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
911 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
912 enum render_line_style styles[TABLE_N_AXES][2],
913 struct cell_color colors[TABLE_N_AXES][2])
915 const int x0 = bb[H][0];
916 const int y0 = bb[V][0];
917 const int x3 = bb[H][1];
918 const int y3 = bb[V][1];
919 const int top = styles[H][0];
920 const int bottom = styles[H][1];
922 int start_side = render_direction_rtl();
923 int end_side = !start_side;
924 const int start_of_line = styles[V][start_side];
925 const int end_of_line = styles[V][end_side];
926 const struct cell_color *top_color = &colors[H][0];
927 const struct cell_color *bottom_color = &colors[H][1];
928 const struct cell_color *start_color = &colors[V][start_side];
929 const struct cell_color *end_color = &colors[V][end_side];
931 /* The algorithm here is somewhat subtle, to allow it to handle
932 all the kinds of intersections that we need.
934 Three additional ordinates are assigned along the x axis. The
935 first is xc, midway between x0 and x3. The others are x1 and
936 x2; for a single vertical line these are equal to xc, and for
937 a double vertical line they are the ordinates of the left and
938 right half of the double line.
940 yc, y1, and y2 are assigned similarly along the y axis.
942 The following diagram shows the coordinate system and output
943 for double top and bottom lines, single left line, and no
947 y0 ________________________
953 y1 = y2 = yc |######### # |
958 y3 |________#_____#_______|
960 struct xr_driver *xr = xr_;
962 /* Offset from center of each line in a pair of double lines. */
963 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
965 /* Are the lines along each axis single or double?
966 (It doesn't make sense to have different kinds of line on the
967 same axis, so we don't try to gracefully handle that case.) */
968 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
969 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
971 /* When horizontal lines are doubled,
972 the left-side line along y1 normally runs from x0 to x2,
973 and the right-side line along y1 from x3 to x1.
974 If the top-side line is also doubled, we shorten the y1 lines,
975 so that the left-side line runs only to x1,
976 and the right-side line only to x2.
977 Otherwise, the horizontal line at y = y1 below would cut off
978 the intersection, which looks ugly:
980 y0 ________________________
985 y1 |######### ########|
988 y2 |######################|
991 y3 |______________________|
992 It is more of a judgment call when the horizontal line is
993 single. We actually choose to cut off the line anyhow, as
994 shown in the first diagram above.
996 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
997 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
998 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
999 int horz_line_ofs = double_vert ? double_line_ofs : 0;
1000 int xc = (x0 + x3) / 2;
1001 int x1 = xc - horz_line_ofs;
1002 int x2 = xc + horz_line_ofs;
1004 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
1005 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
1006 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
1007 int vert_line_ofs = double_horz ? double_line_ofs : 0;
1008 int yc = (y0 + y3) / 2;
1009 int y1 = yc - vert_line_ofs;
1010 int y2 = yc + vert_line_ofs;
1013 horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
1014 start_color, end_color, shorten_yc_line);
1017 horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
1018 start_color, end_color, shorten_y1_lines);
1019 horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
1020 start_color, end_color, shorten_y2_lines);
1024 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
1028 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
1030 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
1036 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
1037 int *min_width, int *max_width)
1039 struct xr_driver *xr = xr_;
1040 int bb[TABLE_N_AXES][2];
1041 int clip[TABLE_N_AXES][2];
1048 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1049 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
1052 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
1055 *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
1056 + cell->style->cell_style.margin[H][1]);
1058 *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
1059 + cell->style->cell_style.margin[H][1]);
1063 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
1065 struct xr_driver *xr = xr_;
1066 int bb[TABLE_N_AXES][2];
1067 int clip[TABLE_N_AXES][2];
1071 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1072 + cell->style->cell_style.margin[H][1]);
1075 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1076 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
1077 h += px_to_xr (cell->style->cell_style.margin[V][0]
1078 + cell->style->cell_style.margin[V][1]);
1082 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
1085 xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
1086 int bb[TABLE_N_AXES][2], int valign_offset,
1087 int spill[TABLE_N_AXES][2],
1088 int clip[TABLE_N_AXES][2])
1090 struct xr_driver *xr = xr_;
1093 if (!xr->transparent)
1095 cairo_save (xr->cairo);
1096 int bg_clip[TABLE_N_AXES][2];
1097 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1099 bg_clip[axis][0] = clip[axis][0];
1100 if (bb[axis][0] == clip[axis][0])
1101 bg_clip[axis][0] -= spill[axis][0];
1103 bg_clip[axis][1] = clip[axis][1];
1104 if (bb[axis][1] == clip[axis][1])
1105 bg_clip[axis][1] += spill[axis][1];
1107 xr_clip (xr, bg_clip);
1108 set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]);
1110 bb[H][0] - spill[H][0],
1111 bb[V][0] - spill[V][0],
1112 bb[H][1] + spill[H][1],
1113 bb[V][1] + spill[V][1]);
1114 cairo_restore (xr->cairo);
1116 cairo_save (xr->cairo);
1117 if (!xr->systemcolors)
1118 set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]);
1120 bb[V][0] += valign_offset;
1122 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1124 bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
1125 bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
1127 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
1128 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1129 cairo_restore (xr->cairo);
1133 xr_adjust_break (void *xr_, const struct table_cell *cell,
1134 int width, int height)
1136 struct xr_driver *xr = xr_;
1137 int bb[TABLE_N_AXES][2];
1138 int clip[TABLE_N_AXES][2];
1141 if (xr_measure_cell_height (xr_, cell, width) < height)
1145 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1146 + cell->style->cell_style.margin[H][1]);
1150 bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
1151 + cell->style->cell_style.margin[V][1]);
1152 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1153 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1158 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
1160 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1162 double x0 = xr_to_pt (clip[H][0]);
1163 double y0 = xr_to_pt (clip[V][0] + xr->y);
1164 double x1 = xr_to_pt (clip[H][1]);
1165 double y1 = xr_to_pt (clip[V][1] + xr->y);
1167 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1168 cairo_clip (xr->cairo);
1173 add_attr (PangoAttrList *list, PangoAttribute *attr,
1174 guint start_index, guint end_index)
1176 attr->start_index = start_index;
1177 attr->end_index = end_index;
1178 pango_attr_list_insert (list, attr);
1182 markup_escape (struct string *out, unsigned int options,
1183 const char *in, size_t len)
1185 if (!(options & TAB_MARKUP))
1187 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
1199 ds_put_cstr (out, "&");
1202 ds_put_cstr (out, "<");
1205 ds_put_cstr (out, ">");
1208 ds_put_byte (out, c);
1215 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
1217 int size[TABLE_N_AXES];
1218 pango_layout_get_size (layout, &size[H], &size[V]);
1223 xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
1224 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1225 int *widthp, int *brk)
1227 const struct font_style *font_style = &cell->style->font_style;
1228 const struct cell_style *cell_style = &cell->style->cell_style;
1229 unsigned int options = cell->options;
1231 enum table_axis X = options & TAB_ROTATE ? V : H;
1232 enum table_axis Y = !X;
1233 int R = options & TAB_ROTATE ? 0 : 1;
1235 struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1236 : &xr->fonts[XR_FONT_PROPORTIONAL]);
1237 struct xr_font local_font;
1238 if (font_style->typeface)
1240 PangoFontDescription *desc = parse_font (
1241 font_style->typeface,
1242 font_style->size ? font_style->size * 1000 * xr->font_scale : 10000,
1243 font_style->bold, font_style->italic);
1246 PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1247 pango_layout_set_font_description (layout, desc);
1249 local_font.desc = desc;
1250 local_font.layout = layout;
1255 const char *text = cell->text;
1256 enum table_halign halign = table_halign_interpret (
1257 cell_style->halign, cell->options & TAB_NUMERIC);
1258 if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
1260 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
1262 const char *decimal = strrchr (text, cell_style->decimal_char);
1265 pango_layout_set_text (font->layout, decimal, strlen (decimal));
1266 pango_layout_set_width (font->layout, -1);
1267 margin_adjustment += get_layout_dimension (font->layout, H);
1270 if (margin_adjustment < 0)
1271 bb[H][1] += margin_adjustment;
1274 struct string tmp = DS_EMPTY_INITIALIZER;
1275 PangoAttrList *attrs = NULL;
1277 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
1278 Pango's implementation of it): it will break after a period or a comma
1279 that precedes a digit, e.g. in ".000" it will break after the period.
1280 This code looks for such a situation and inserts a U+2060 WORD JOINER
1281 to prevent the break.
1283 This isn't necessary when the decimal point is between two digits
1284 (e.g. "0.000" won't be broken) or when the display width is not limited so
1285 that word wrapping won't happen.
1287 It isn't necessary to look for more than one period or comma, as would
1288 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
1289 are present then there will always be a digit on both sides of every
1290 period and comma. */
1291 if (options & TAB_MARKUP)
1293 PangoAttrList *new_attrs;
1295 if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
1298 tmp.ss = ss_cstr (new_text);
1299 tmp.capacity = tmp.ss.length;
1303 /* XXX should we report the error? */
1304 ds_put_cstr (&tmp, text);
1307 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
1309 const char *decimal = text + strcspn (text, ".,");
1311 && c_isdigit (decimal[1])
1312 && (decimal == text || !c_isdigit (decimal[-1])))
1314 ds_extend (&tmp, strlen (text) + 16);
1315 markup_escape (&tmp, options, text, decimal - text + 1);
1316 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
1317 markup_escape (&tmp, options, decimal + 1, -1);
1321 if (font_style->underline)
1324 attrs = pango_attr_list_new ();
1325 pango_attr_list_insert (attrs, pango_attr_underline_new (
1326 PANGO_UNDERLINE_SINGLE));
1329 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
1331 /* If we haven't already put TEXT into tmp, do it now. */
1332 if (ds_is_empty (&tmp))
1334 ds_extend (&tmp, strlen (text) + 16);
1335 markup_escape (&tmp, options, text, -1);
1338 size_t subscript_ofs = ds_length (&tmp);
1339 for (size_t i = 0; i < cell->n_subscripts; i++)
1342 ds_put_byte (&tmp, ',');
1343 ds_put_cstr (&tmp, cell->subscripts[i]);
1346 size_t superscript_ofs = ds_length (&tmp);
1347 if (cell->superscript)
1348 ds_put_cstr (&tmp, cell->superscript);
1350 size_t footnote_ofs = ds_length (&tmp);
1351 for (size_t i = 0; i < cell->n_footnotes; i++)
1354 ds_put_byte (&tmp, ',');
1355 ds_put_cstr (&tmp, cell->footnotes[i]->marker);
1358 /* Allow footnote markers to occupy the right margin. That way, numbers
1359 in the column are still aligned. */
1360 if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
1362 /* Measure the width of the footnote marker, so we know how much we
1363 need to make room for. */
1364 pango_layout_set_text (font->layout, ds_cstr (&tmp) + footnote_ofs,
1365 ds_length (&tmp) - footnote_ofs);
1367 PangoAttrList *fn_attrs = pango_attr_list_new ();
1368 pango_attr_list_insert (
1369 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
1370 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
1371 pango_layout_set_attributes (font->layout, fn_attrs);
1372 pango_attr_list_unref (fn_attrs);
1373 int footnote_width = get_layout_dimension (font->layout, X);
1375 /* Bound the adjustment by the width of the right margin. */
1376 int right_margin = px_to_xr (cell_style->margin[X][R]);
1377 int footnote_adjustment = MIN (footnote_width, right_margin);
1379 /* Adjust the bounding box. */
1380 if (options & TAB_ROTATE)
1381 footnote_adjustment = -footnote_adjustment;
1382 bb[X][R] += footnote_adjustment;
1385 pango_layout_set_attributes (font->layout, NULL);
1388 /* Set attributes. */
1390 attrs = pango_attr_list_new ();
1391 add_attr (attrs, pango_attr_font_desc_new (font->desc), subscript_ofs,
1392 PANGO_ATTR_INDEX_TO_TEXT_END);
1393 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
1394 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
1395 if (cell->n_subscripts)
1396 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
1397 superscript_ofs - subscript_ofs);
1398 if (cell->superscript || cell->n_footnotes)
1399 add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
1400 PANGO_ATTR_INDEX_TO_TEXT_END);
1403 /* Set the attributes, if any. */
1406 pango_layout_set_attributes (font->layout, attrs);
1407 pango_attr_list_unref (attrs);
1411 if (ds_is_empty (&tmp))
1412 pango_layout_set_text (font->layout, text, -1);
1414 pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
1417 pango_layout_set_alignment (font->layout,
1418 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
1419 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
1420 : PANGO_ALIGN_CENTER));
1421 pango_layout_set_width (
1423 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
1424 pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1426 if (clip[H][0] != clip[H][1])
1428 cairo_save (xr->cairo);
1429 if (!(options & TAB_ROTATE))
1431 if (options & TAB_ROTATE)
1433 cairo_translate (xr->cairo,
1434 xr_to_pt (bb[H][0]),
1435 xr_to_pt (bb[V][1] + xr->y));
1436 cairo_rotate (xr->cairo, -M_PI_2);
1439 cairo_translate (xr->cairo,
1440 xr_to_pt (bb[H][0]),
1441 xr_to_pt (bb[V][0] + xr->y));
1442 pango_cairo_show_layout (xr->cairo, font->layout);
1444 /* If enabled, this draws a blue rectangle around the extents of each
1445 line of text, which can be rather useful for debugging layout
1449 PangoLayoutIter *iter;
1450 iter = pango_layout_get_iter (font->layout);
1453 PangoRectangle extents;
1455 pango_layout_iter_get_line_extents (iter, &extents, NULL);
1456 cairo_save (xr->cairo);
1457 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1459 pango_to_xr (extents.x),
1460 pango_to_xr (extents.y) - xr->y,
1461 pango_to_xr (extents.x + extents.width),
1462 pango_to_xr (extents.y + extents.height) - xr->y);
1463 cairo_restore (xr->cairo);
1465 while (pango_layout_iter_next_line (iter));
1466 pango_layout_iter_free (iter);
1469 cairo_restore (xr->cairo);
1472 int size[TABLE_N_AXES];
1473 pango_layout_get_size (font->layout, &size[H], &size[V]);
1474 int w = pango_to_xr (size[X]);
1475 int h = pango_to_xr (size[Y]);
1478 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
1480 PangoLayoutIter *iter;
1483 /* Choose a breakpoint between lines instead of in the middle of one. */
1484 iter = pango_layout_get_iter (font->layout);
1487 PangoRectangle extents;
1491 pango_layout_iter_get_line_extents (iter, NULL, &extents);
1492 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1493 extents.x = pango_to_xr (extents.x);
1494 extents.y = pango_to_xr (y0);
1495 extents.width = pango_to_xr (extents.width);
1496 extents.height = pango_to_xr (y1 - y0);
1497 bottom = bb[V][0] + extents.y + extents.height;
1498 if (bottom < bb[V][1])
1500 if (brk && clip[H][0] != clip[H][1])
1508 while (pango_layout_iter_next_line (iter));
1509 pango_layout_iter_free (iter);
1511 /* If enabled, draws a green line across the chosen breakpoint, which can
1512 be useful for debugging issues with breaking. */
1516 dump_line (xr, -xr->left_margin, best,
1517 xr->width + xr->right_margin, best,
1519 &(struct cell_color) CELL_COLOR (0, 255, 0));
1523 pango_layout_set_attributes (font->layout, NULL);
1525 if (font == &local_font)
1527 g_object_unref (G_OBJECT (font->layout));
1528 pango_font_description_free (font->desc);
1535 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1536 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1537 int *width, int *height, int *brk)
1542 /* If enabled, draws a blue rectangle around the cell extents, which can be
1543 useful for debugging layout. */
1546 if (clip[H][0] != clip[H][1])
1548 cairo_save (xr->cairo);
1549 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1550 dump_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
1551 cairo_restore (xr->cairo);
1557 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
1560 struct output_driver_factory pdf_driver_factory =
1561 { "pdf", "pspp.pdf", xr_pdf_create };
1562 struct output_driver_factory ps_driver_factory =
1563 { "ps", "pspp.ps", xr_ps_create };
1564 struct output_driver_factory svg_driver_factory =
1565 { "svg", "pspp.svg", xr_svg_create };
1567 static const struct output_driver_class cairo_driver_class =
1576 xr_driver_create (cairo_t *cairo, struct string_map *options)
1578 struct xr_driver *xr = xr_allocate ("cairo", 0, options, 1.0);
1579 xr_set_cairo (xr, cairo);
1583 /* Destroy XR, which should have been created with xr_driver_create(). Any
1584 cairo_t added to XR is not destroyed, because it is owned by the client. */
1586 xr_driver_destroy (struct xr_driver *xr)
1591 output_driver_destroy (&xr->driver);
1595 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1596 double x, double y, double width, double height)
1598 struct xrchart_geometry geom;
1601 cairo_translate (cr, x, y + height);
1602 cairo_scale (cr, 1.0, -1.0);
1603 xrchart_geometry_init (cr, &geom, width, height);
1604 if (is_boxplot (chart_item))
1605 xrchart_draw_boxplot (chart_item, cr, &geom);
1606 else if (is_histogram_chart (chart_item))
1607 xrchart_draw_histogram (chart_item, cr, &geom);
1608 else if (is_np_plot_chart (chart_item))
1609 xrchart_draw_np_plot (chart_item, cr, &geom);
1610 else if (is_piechart (chart_item))
1611 xrchart_draw_piechart (chart_item, cr, &geom);
1612 else if (is_barchart (chart_item))
1613 xrchart_draw_barchart (chart_item, cr, &geom);
1614 else if (is_roc_chart (chart_item))
1615 xrchart_draw_roc (chart_item, cr, &geom);
1616 else if (is_scree (chart_item))
1617 xrchart_draw_scree (chart_item, cr, &geom);
1618 else if (is_spreadlevel_plot_chart (chart_item))
1619 xrchart_draw_spreadlevel (chart_item, cr, &geom);
1620 else if (is_scatterplot_chart (chart_item))
1621 xrchart_draw_scatterplot (chart_item, cr, &geom);
1624 xrchart_geometry_free (cr, &geom);
1630 xr_draw_png_chart (const struct chart_item *item,
1631 const char *file_name_template, int number,
1632 const struct cell_color *fg,
1633 const struct cell_color *bg)
1635 const int width = 640;
1636 const int length = 480;
1638 cairo_surface_t *surface;
1639 cairo_status_t status;
1640 const char *number_pos;
1644 number_pos = strchr (file_name_template, '#');
1645 if (number_pos != NULL)
1646 file_name = xasprintf ("%.*s%d%s.png", (int) (number_pos - file_name_template),
1647 file_name_template, number, number_pos + 1);
1649 file_name = xasprintf ("%s.png", file_name_template);
1651 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1652 cr = cairo_create (surface);
1654 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1657 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1659 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1661 status = cairo_surface_write_to_png (surface, file_name);
1662 if (status != CAIRO_STATUS_SUCCESS)
1663 msg (ME, _("error writing output file `%s': %s"),
1664 file_name, cairo_status_to_string (status));
1667 cairo_surface_destroy (surface);
1674 xr_draw_eps_chart (const struct chart_item *item,
1675 const char *file_name_template, int number,
1676 const struct cell_color *fg,
1677 const struct cell_color *bg)
1679 const int width = 640;
1680 const int length = 480;
1682 cairo_surface_t *surface;
1683 const char *number_pos;
1687 number_pos = strchr (file_name_template, '#');
1688 if (number_pos != NULL)
1689 file_name = xasprintf ("%.*s%d%s.eps", (int) (number_pos - file_name_template),
1690 file_name_template, number, number_pos + 1);
1692 file_name = xasprintf ("%s.eps", file_name_template);
1694 surface = cairo_ps_surface_create (file_name, width, length);
1695 cairo_ps_surface_set_eps (surface, true);
1696 cr = cairo_create (surface);
1698 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1701 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1703 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1706 cairo_surface_destroy (surface);