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);
536 if (!xr->systemcolors)
537 cairo_set_source_rgb (xr->cairo,
538 xr->fg.r / 255.0, xr->fg.g / 255.0, xr->fg.b / 255.0);
541 static struct output_driver *
542 xr_create (struct file_handle *fh, enum settings_output_devices device_type,
543 struct string_map *o, enum xr_output_type file_type)
545 const char *file_name = fh_get_file_name (fh);
546 struct xr_driver *xr = xr_allocate (file_name, device_type, o, 72.0 / 128.0);
547 double width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
548 double length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
549 if (file_type == XR_PDF)
550 xr->surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
551 else if (file_type == XR_PS)
552 xr->surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
553 else if (file_type == XR_SVG)
554 xr->surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
558 cairo_status_t status = cairo_surface_status (xr->surface);
559 if (status != CAIRO_STATUS_SUCCESS)
561 msg (ME, _("error opening output file `%s': %s"),
562 file_name, cairo_status_to_string (status));
566 if (!xr_check_fonts (xr->surface, xr->fonts, xr->width, xr->length))
574 output_driver_destroy (&xr->driver);
578 static struct output_driver *
579 xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
580 struct string_map *o)
582 return xr_create (fh, device_type, o, XR_PDF);
585 static struct output_driver *
586 xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
587 struct string_map *o)
589 return xr_create (fh, device_type, o, XR_PS);
592 static struct output_driver *
593 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
594 struct string_map *o)
596 return xr_create (fh, device_type, o, XR_SVG);
600 xr_destroy (struct output_driver *driver)
602 struct xr_driver *xr = xr_driver_cast (driver);
605 xr_driver_destroy_fsm (xr);
607 if (xr->cairo != NULL)
609 cairo_surface_finish (xr->surface);
610 cairo_status_t status = cairo_status (xr->cairo);
611 if (status != CAIRO_STATUS_SUCCESS)
612 fprintf (stderr, _("error drawing output for %s driver: %s"),
613 output_driver_get_name (driver),
614 cairo_status_to_string (status));
615 cairo_surface_destroy (xr->surface);
617 cairo_destroy (xr->cairo);
620 for (i = 0; i < XR_N_FONTS; i++)
622 struct xr_font *font = &xr->fonts[i];
624 if (font->desc != NULL)
625 pango_font_description_free (font->desc);
626 if (font->layout != NULL)
627 g_object_unref (font->layout);
630 xr_fsm_style_unref (xr->style);
636 xr_flush (struct output_driver *driver)
638 struct xr_driver *xr = xr_driver_cast (driver);
640 cairo_surface_flush (cairo_get_target (xr->cairo));
644 xr_update_page_setup (struct output_driver *driver,
645 const struct page_setup *ps)
647 struct xr_driver *xr = xr_driver_cast (driver);
649 xr->initial_page_number = ps->initial_page_number;
650 xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
655 int usable[TABLE_N_AXES];
656 for (int i = 0; i < 2; i++)
657 usable[i] = (ps->paper[i]
658 - (ps->margins[i][0] + ps->margins[i][1])) * 72 * XR_POINT;
660 int headings_height[2];
661 usable[V] -= xr_measure_headings (
662 xr->surface, xr->fonts[XR_FONT_PROPORTIONAL].desc, ps->headings,
663 usable[H], xr->object_spacing, headings_height);
665 enum table_axis h = ps->orientation == PAGE_LANDSCAPE;
666 enum table_axis v = !h;
667 if (!xr_check_fonts (xr->surface, xr->fonts, usable[h], usable[v]))
670 for (int i = 0; i < 2; i++)
672 page_heading_uninit (&xr->headings[i]);
673 page_heading_copy (&xr->headings[i], &ps->headings[i]);
674 xr->headings_height[i] = headings_height[i];
676 xr->width = usable[h];
677 xr->length = usable[v];
678 xr->left_margin = ps->margins[h][0] * 72 * XR_POINT;
679 xr->right_margin = ps->margins[h][1] * 72 * XR_POINT;
680 xr->top_margin = ps->margins[v][0] * 72 * XR_POINT;
681 xr->bottom_margin = ps->margins[v][1] * 72 * XR_POINT;
682 cairo_pdf_surface_set_size (xr->surface,
683 ps->paper[h] * 72.0, ps->paper[v] * 72.0);
687 xr_submit (struct output_driver *driver, const struct output_item *output_item)
689 struct xr_driver *xr = xr_driver_cast (driver);
691 if (is_page_setup_item (output_item))
693 xr_update_page_setup (driver,
694 to_page_setup_item (output_item)->page_setup);
700 xr->page_number = xr->initial_page_number - 1;
701 xr_set_cairo (xr, cairo_create (xr->surface));
702 cairo_save (xr->cairo);
703 xr_driver_next_page (xr, xr->cairo);
706 xr_driver_output_item (xr, output_item);
707 while (xr_driver_need_new_page (xr))
709 cairo_restore (xr->cairo);
710 cairo_show_page (xr->cairo);
711 cairo_save (xr->cairo);
712 xr_driver_next_page (xr, xr->cairo);
716 /* Functions for rendering a series of output items to a series of Cairo
717 contexts, with pagination.
719 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
720 its underlying implementation.
722 See the big comment in cairo.h for intended usage. */
724 /* Gives new page CAIRO to XR for output. */
726 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
728 if (!xr->transparent)
731 cairo_set_source_rgb (cairo,
732 xr->bg.r / 255.0, xr->bg.g / 255.0, xr->bg.b / 255.0);
733 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
735 cairo_restore (cairo);
737 cairo_translate (cairo,
738 xr_to_pt (xr->left_margin),
739 xr_to_pt (xr->top_margin + xr->headings_height[0]));
745 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
746 &xr->headings[0], xr->page_number, xr->width, true,
747 -xr->headings_height[0]);
748 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
749 &xr->headings[1], xr->page_number, xr->width, true,
752 xr_driver_run_fsm (xr);
755 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
756 rendering a previous output item, that is, only if xr_driver_need_new_page()
759 xr_driver_output_item (struct xr_driver *xr,
760 const struct output_item *output_item)
762 assert (xr->fsm == NULL);
763 xr->fsm = xr_fsm_create (output_item, xr->style, xr->cairo);
764 xr_driver_run_fsm (xr);
767 /* Returns true if XR is in the middle of rendering an output item and needs a
768 new page to be appended using xr_driver_next_page() to make progress,
771 xr_driver_need_new_page (const struct xr_driver *xr)
773 return xr->fsm != NULL;
776 /* Returns true if the current page doesn't have any content yet. */
778 xr_driver_is_page_blank (const struct xr_driver *xr)
784 xr_driver_destroy_fsm (struct xr_driver *xr)
786 xr_fsm_destroy (xr->fsm);
791 xr_driver_run_fsm (struct xr_driver *xr)
795 cairo_save (xr->cairo);
796 cairo_translate (xr->cairo, 0, xr_to_pt (xr->y));
797 int used = xr_fsm_draw_slice (xr->fsm, xr->cairo, xr->length - xr->y);
799 cairo_restore (xr->cairo);
801 if (xr_fsm_is_empty (xr->fsm))
802 xr_driver_destroy_fsm (xr);
807 xr_layout_cell (struct xr_driver *, const struct table_cell *,
808 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
809 int *width, int *height, int *brk);
812 set_source_rgba (cairo_t *cairo, const struct cell_color *color)
814 cairo_set_source_rgba (cairo,
815 color->r / 255., color->g / 255., color->b / 255.,
816 color->alpha / 255.);
820 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
821 const struct cell_color *color)
823 cairo_new_path (xr->cairo);
824 if (!xr->systemcolors)
825 set_source_rgba (xr->cairo, color);
826 cairo_set_line_width (
828 xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
829 : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
831 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
832 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
833 cairo_stroke (xr->cairo);
837 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
839 cairo_new_path (xr->cairo);
840 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
841 cairo_close_path (xr->cairo);
842 cairo_stroke (xr->cairo);
843 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
844 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y0 + xr->y));
845 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
846 cairo_line_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y1 + xr->y));
850 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
852 cairo_new_path (xr->cairo);
853 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
854 cairo_rectangle (xr->cairo,
855 xr_to_pt (x0), xr_to_pt (y0 + xr->y),
856 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
857 cairo_fill (xr->cairo);
860 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
861 shortening it to X0...X1 if SHORTEN is true.
862 Draws a horizontal line X1...X3 at Y if RIGHT says so,
863 shortening it to X2...X3 if SHORTEN is true. */
865 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
866 enum render_line_style left, enum render_line_style right,
867 const struct cell_color *left_color,
868 const struct cell_color *right_color,
871 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
872 && cell_color_equal (left_color, right_color))
873 dump_line (xr, x0, y, x3, y, left, left_color);
876 if (left != RENDER_LINE_NONE)
877 dump_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
878 if (right != RENDER_LINE_NONE)
879 dump_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
883 /* Draws a vertical line Y0...Y2 at X if TOP says so,
884 shortening it to Y0...Y1 if SHORTEN is true.
885 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
886 shortening it to Y2...Y3 if SHORTEN is true. */
888 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
889 enum render_line_style top, enum render_line_style bottom,
890 const struct cell_color *top_color,
891 const struct cell_color *bottom_color,
894 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
895 && cell_color_equal (top_color, bottom_color))
896 dump_line (xr, x, y0, x, y3, top, top_color);
899 if (top != RENDER_LINE_NONE)
900 dump_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
901 if (bottom != RENDER_LINE_NONE)
902 dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
907 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
908 enum render_line_style styles[TABLE_N_AXES][2],
909 struct cell_color colors[TABLE_N_AXES][2])
911 const int x0 = bb[H][0];
912 const int y0 = bb[V][0];
913 const int x3 = bb[H][1];
914 const int y3 = bb[V][1];
915 const int top = styles[H][0];
916 const int bottom = styles[H][1];
918 int start_side = render_direction_rtl();
919 int end_side = !start_side;
920 const int start_of_line = styles[V][start_side];
921 const int end_of_line = styles[V][end_side];
922 const struct cell_color *top_color = &colors[H][0];
923 const struct cell_color *bottom_color = &colors[H][1];
924 const struct cell_color *start_color = &colors[V][start_side];
925 const struct cell_color *end_color = &colors[V][end_side];
927 /* The algorithm here is somewhat subtle, to allow it to handle
928 all the kinds of intersections that we need.
930 Three additional ordinates are assigned along the x axis. The
931 first is xc, midway between x0 and x3. The others are x1 and
932 x2; for a single vertical line these are equal to xc, and for
933 a double vertical line they are the ordinates of the left and
934 right half of the double line.
936 yc, y1, and y2 are assigned similarly along the y axis.
938 The following diagram shows the coordinate system and output
939 for double top and bottom lines, single left line, and no
943 y0 ________________________
949 y1 = y2 = yc |######### # |
954 y3 |________#_____#_______|
956 struct xr_driver *xr = xr_;
958 /* Offset from center of each line in a pair of double lines. */
959 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
961 /* Are the lines along each axis single or double?
962 (It doesn't make sense to have different kinds of line on the
963 same axis, so we don't try to gracefully handle that case.) */
964 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
965 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
967 /* When horizontal lines are doubled,
968 the left-side line along y1 normally runs from x0 to x2,
969 and the right-side line along y1 from x3 to x1.
970 If the top-side line is also doubled, we shorten the y1 lines,
971 so that the left-side line runs only to x1,
972 and the right-side line only to x2.
973 Otherwise, the horizontal line at y = y1 below would cut off
974 the intersection, which looks ugly:
976 y0 ________________________
981 y1 |######### ########|
984 y2 |######################|
987 y3 |______________________|
988 It is more of a judgment call when the horizontal line is
989 single. We actually choose to cut off the line anyhow, as
990 shown in the first diagram above.
992 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
993 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
994 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
995 int horz_line_ofs = double_vert ? double_line_ofs : 0;
996 int xc = (x0 + x3) / 2;
997 int x1 = xc - horz_line_ofs;
998 int x2 = xc + horz_line_ofs;
1000 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
1001 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
1002 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
1003 int vert_line_ofs = double_horz ? double_line_ofs : 0;
1004 int yc = (y0 + y3) / 2;
1005 int y1 = yc - vert_line_ofs;
1006 int y2 = yc + vert_line_ofs;
1009 horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
1010 start_color, end_color, shorten_yc_line);
1013 horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
1014 start_color, end_color, shorten_y1_lines);
1015 horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
1016 start_color, end_color, shorten_y2_lines);
1020 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
1024 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
1026 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
1032 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
1033 int *min_width, int *max_width)
1035 struct xr_driver *xr = xr_;
1036 int bb[TABLE_N_AXES][2];
1037 int clip[TABLE_N_AXES][2];
1044 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1045 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
1048 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
1051 *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
1052 + cell->style->cell_style.margin[H][1]);
1054 *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
1055 + cell->style->cell_style.margin[H][1]);
1059 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
1061 struct xr_driver *xr = xr_;
1062 int bb[TABLE_N_AXES][2];
1063 int clip[TABLE_N_AXES][2];
1067 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1068 + cell->style->cell_style.margin[H][1]);
1071 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1072 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
1073 h += px_to_xr (cell->style->cell_style.margin[V][0]
1074 + cell->style->cell_style.margin[V][1]);
1078 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
1081 xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
1082 int bb[TABLE_N_AXES][2], int valign_offset,
1083 int spill[TABLE_N_AXES][2],
1084 int clip[TABLE_N_AXES][2])
1086 struct xr_driver *xr = xr_;
1089 if (!xr->transparent)
1091 cairo_save (xr->cairo);
1092 int bg_clip[TABLE_N_AXES][2];
1093 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1095 bg_clip[axis][0] = clip[axis][0];
1096 if (bb[axis][0] == clip[axis][0])
1097 bg_clip[axis][0] -= spill[axis][0];
1099 bg_clip[axis][1] = clip[axis][1];
1100 if (bb[axis][1] == clip[axis][1])
1101 bg_clip[axis][1] += spill[axis][1];
1103 xr_clip (xr, bg_clip);
1104 set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]);
1106 bb[H][0] - spill[H][0],
1107 bb[V][0] - spill[V][0],
1108 bb[H][1] + spill[H][1],
1109 bb[V][1] + spill[V][1]);
1110 cairo_restore (xr->cairo);
1112 cairo_save (xr->cairo);
1113 if (!xr->systemcolors)
1114 set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]);
1116 bb[V][0] += valign_offset;
1118 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1120 bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
1121 bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
1123 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
1124 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1125 cairo_restore (xr->cairo);
1129 xr_adjust_break (void *xr_, const struct table_cell *cell,
1130 int width, int height)
1132 struct xr_driver *xr = xr_;
1133 int bb[TABLE_N_AXES][2];
1134 int clip[TABLE_N_AXES][2];
1137 if (xr_measure_cell_height (xr_, cell, width) < height)
1141 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1142 + cell->style->cell_style.margin[H][1]);
1146 bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
1147 + cell->style->cell_style.margin[V][1]);
1148 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1149 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1154 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
1156 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1158 double x0 = xr_to_pt (clip[H][0]);
1159 double y0 = xr_to_pt (clip[V][0] + xr->y);
1160 double x1 = xr_to_pt (clip[H][1]);
1161 double y1 = xr_to_pt (clip[V][1] + xr->y);
1163 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1164 cairo_clip (xr->cairo);
1169 add_attr (PangoAttrList *list, PangoAttribute *attr,
1170 guint start_index, guint end_index)
1172 attr->start_index = start_index;
1173 attr->end_index = end_index;
1174 pango_attr_list_insert (list, attr);
1178 markup_escape (struct string *out, unsigned int options,
1179 const char *in, size_t len)
1181 if (!(options & TAB_MARKUP))
1183 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
1195 ds_put_cstr (out, "&");
1198 ds_put_cstr (out, "<");
1201 ds_put_cstr (out, ">");
1204 ds_put_byte (out, c);
1211 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
1213 int size[TABLE_N_AXES];
1214 pango_layout_get_size (layout, &size[H], &size[V]);
1219 xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
1220 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1221 int *widthp, int *brk)
1223 const struct font_style *font_style = &cell->style->font_style;
1224 const struct cell_style *cell_style = &cell->style->cell_style;
1225 unsigned int options = cell->options;
1227 enum table_axis X = options & TAB_ROTATE ? V : H;
1228 enum table_axis Y = !X;
1229 int R = options & TAB_ROTATE ? 0 : 1;
1231 struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1232 : &xr->fonts[XR_FONT_PROPORTIONAL]);
1233 struct xr_font local_font;
1234 if (font_style->typeface)
1236 PangoFontDescription *desc = parse_font (
1237 font_style->typeface,
1238 font_style->size ? font_style->size * 1000 * xr->font_scale : 10000,
1239 font_style->bold, font_style->italic);
1242 PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1243 pango_layout_set_font_description (layout, desc);
1245 local_font.desc = desc;
1246 local_font.layout = layout;
1251 const char *text = cell->text;
1252 enum table_halign halign = table_halign_interpret (
1253 cell_style->halign, cell->options & TAB_NUMERIC);
1254 if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
1256 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
1258 const char *decimal = strrchr (text, cell_style->decimal_char);
1261 pango_layout_set_text (font->layout, decimal, strlen (decimal));
1262 pango_layout_set_width (font->layout, -1);
1263 margin_adjustment += get_layout_dimension (font->layout, H);
1266 if (margin_adjustment < 0)
1267 bb[H][1] += margin_adjustment;
1270 struct string tmp = DS_EMPTY_INITIALIZER;
1271 PangoAttrList *attrs = NULL;
1273 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
1274 Pango's implementation of it): it will break after a period or a comma
1275 that precedes a digit, e.g. in ".000" it will break after the period.
1276 This code looks for such a situation and inserts a U+2060 WORD JOINER
1277 to prevent the break.
1279 This isn't necessary when the decimal point is between two digits
1280 (e.g. "0.000" won't be broken) or when the display width is not limited so
1281 that word wrapping won't happen.
1283 It isn't necessary to look for more than one period or comma, as would
1284 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
1285 are present then there will always be a digit on both sides of every
1286 period and comma. */
1287 if (options & TAB_MARKUP)
1289 PangoAttrList *new_attrs;
1291 if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
1294 tmp.ss = ss_cstr (new_text);
1295 tmp.capacity = tmp.ss.length;
1299 /* XXX should we report the error? */
1300 ds_put_cstr (&tmp, text);
1303 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
1305 const char *decimal = text + strcspn (text, ".,");
1307 && c_isdigit (decimal[1])
1308 && (decimal == text || !c_isdigit (decimal[-1])))
1310 ds_extend (&tmp, strlen (text) + 16);
1311 markup_escape (&tmp, options, text, decimal - text + 1);
1312 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
1313 markup_escape (&tmp, options, decimal + 1, -1);
1317 if (font_style->underline)
1320 attrs = pango_attr_list_new ();
1321 pango_attr_list_insert (attrs, pango_attr_underline_new (
1322 PANGO_UNDERLINE_SINGLE));
1325 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
1327 /* If we haven't already put TEXT into tmp, do it now. */
1328 if (ds_is_empty (&tmp))
1330 ds_extend (&tmp, strlen (text) + 16);
1331 markup_escape (&tmp, options, text, -1);
1334 size_t subscript_ofs = ds_length (&tmp);
1335 for (size_t i = 0; i < cell->n_subscripts; i++)
1338 ds_put_byte (&tmp, ',');
1339 ds_put_cstr (&tmp, cell->subscripts[i]);
1342 size_t superscript_ofs = ds_length (&tmp);
1343 if (cell->superscript)
1344 ds_put_cstr (&tmp, cell->superscript);
1346 size_t footnote_ofs = ds_length (&tmp);
1347 for (size_t i = 0; i < cell->n_footnotes; i++)
1350 ds_put_byte (&tmp, ',');
1351 ds_put_cstr (&tmp, cell->footnotes[i]->marker);
1354 /* Allow footnote markers to occupy the right margin. That way, numbers
1355 in the column are still aligned. */
1356 if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
1358 /* Measure the width of the footnote marker, so we know how much we
1359 need to make room for. */
1360 pango_layout_set_text (font->layout, ds_cstr (&tmp) + footnote_ofs,
1361 ds_length (&tmp) - footnote_ofs);
1363 PangoAttrList *fn_attrs = pango_attr_list_new ();
1364 pango_attr_list_insert (
1365 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
1366 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
1367 pango_layout_set_attributes (font->layout, fn_attrs);
1368 pango_attr_list_unref (fn_attrs);
1369 int footnote_width = get_layout_dimension (font->layout, X);
1371 /* Bound the adjustment by the width of the right margin. */
1372 int right_margin = px_to_xr (cell_style->margin[X][R]);
1373 int footnote_adjustment = MIN (footnote_width, right_margin);
1375 /* Adjust the bounding box. */
1376 if (options & TAB_ROTATE)
1377 footnote_adjustment = -footnote_adjustment;
1378 bb[X][R] += footnote_adjustment;
1381 pango_layout_set_attributes (font->layout, NULL);
1384 /* Set attributes. */
1386 attrs = pango_attr_list_new ();
1387 add_attr (attrs, pango_attr_font_desc_new (font->desc), subscript_ofs,
1388 PANGO_ATTR_INDEX_TO_TEXT_END);
1389 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
1390 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
1391 if (cell->n_subscripts)
1392 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
1393 superscript_ofs - subscript_ofs);
1394 if (cell->superscript || cell->n_footnotes)
1395 add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
1396 PANGO_ATTR_INDEX_TO_TEXT_END);
1399 /* Set the attributes, if any. */
1402 pango_layout_set_attributes (font->layout, attrs);
1403 pango_attr_list_unref (attrs);
1407 if (ds_is_empty (&tmp))
1408 pango_layout_set_text (font->layout, text, -1);
1410 pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
1413 pango_layout_set_alignment (font->layout,
1414 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
1415 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
1416 : PANGO_ALIGN_CENTER));
1417 pango_layout_set_width (
1419 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
1420 pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1422 if (clip[H][0] != clip[H][1])
1424 cairo_save (xr->cairo);
1425 if (!(options & TAB_ROTATE))
1427 if (options & TAB_ROTATE)
1429 cairo_translate (xr->cairo,
1430 xr_to_pt (bb[H][0]),
1431 xr_to_pt (bb[V][1] + xr->y));
1432 cairo_rotate (xr->cairo, -M_PI_2);
1435 cairo_translate (xr->cairo,
1436 xr_to_pt (bb[H][0]),
1437 xr_to_pt (bb[V][0] + xr->y));
1438 pango_cairo_show_layout (xr->cairo, font->layout);
1440 /* If enabled, this draws a blue rectangle around the extents of each
1441 line of text, which can be rather useful for debugging layout
1445 PangoLayoutIter *iter;
1446 iter = pango_layout_get_iter (font->layout);
1449 PangoRectangle extents;
1451 pango_layout_iter_get_line_extents (iter, &extents, NULL);
1452 cairo_save (xr->cairo);
1453 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1455 pango_to_xr (extents.x),
1456 pango_to_xr (extents.y) - xr->y,
1457 pango_to_xr (extents.x + extents.width),
1458 pango_to_xr (extents.y + extents.height) - xr->y);
1459 cairo_restore (xr->cairo);
1461 while (pango_layout_iter_next_line (iter));
1462 pango_layout_iter_free (iter);
1465 cairo_restore (xr->cairo);
1468 int size[TABLE_N_AXES];
1469 pango_layout_get_size (font->layout, &size[H], &size[V]);
1470 int w = pango_to_xr (size[X]);
1471 int h = pango_to_xr (size[Y]);
1474 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
1476 PangoLayoutIter *iter;
1479 /* Choose a breakpoint between lines instead of in the middle of one. */
1480 iter = pango_layout_get_iter (font->layout);
1483 PangoRectangle extents;
1487 pango_layout_iter_get_line_extents (iter, NULL, &extents);
1488 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1489 extents.x = pango_to_xr (extents.x);
1490 extents.y = pango_to_xr (y0);
1491 extents.width = pango_to_xr (extents.width);
1492 extents.height = pango_to_xr (y1 - y0);
1493 bottom = bb[V][0] + extents.y + extents.height;
1494 if (bottom < bb[V][1])
1496 if (brk && clip[H][0] != clip[H][1])
1504 while (pango_layout_iter_next_line (iter));
1505 pango_layout_iter_free (iter);
1507 /* If enabled, draws a green line across the chosen breakpoint, which can
1508 be useful for debugging issues with breaking. */
1512 dump_line (xr, -xr->left_margin, best,
1513 xr->width + xr->right_margin, best,
1515 &(struct cell_color) CELL_COLOR (0, 255, 0));
1519 pango_layout_set_attributes (font->layout, NULL);
1521 if (font == &local_font)
1523 g_object_unref (G_OBJECT (font->layout));
1524 pango_font_description_free (font->desc);
1531 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1532 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1533 int *width, int *height, int *brk)
1538 /* If enabled, draws a blue rectangle around the cell extents, which can be
1539 useful for debugging layout. */
1542 if (clip[H][0] != clip[H][1])
1544 cairo_save (xr->cairo);
1545 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1546 dump_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
1547 cairo_restore (xr->cairo);
1553 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
1556 struct output_driver_factory pdf_driver_factory =
1557 { "pdf", "pspp.pdf", xr_pdf_create };
1558 struct output_driver_factory ps_driver_factory =
1559 { "ps", "pspp.ps", xr_ps_create };
1560 struct output_driver_factory svg_driver_factory =
1561 { "svg", "pspp.svg", xr_svg_create };
1563 static const struct output_driver_class cairo_driver_class =
1572 xr_driver_create (cairo_t *cairo, struct string_map *options)
1574 struct xr_driver *xr = xr_allocate ("cairo", 0, options, 1.0);
1575 xr_set_cairo (xr, cairo);
1579 /* Destroy XR, which should have been created with xr_driver_create(). Any
1580 cairo_t added to XR is not destroyed, because it is owned by the client. */
1582 xr_driver_destroy (struct xr_driver *xr)
1587 output_driver_destroy (&xr->driver);
1591 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1592 double x, double y, double width, double height)
1594 struct xrchart_geometry geom;
1597 cairo_translate (cr, x, y + height);
1598 cairo_scale (cr, 1.0, -1.0);
1599 xrchart_geometry_init (cr, &geom, width, height);
1600 if (is_boxplot (chart_item))
1601 xrchart_draw_boxplot (chart_item, cr, &geom);
1602 else if (is_histogram_chart (chart_item))
1603 xrchart_draw_histogram (chart_item, cr, &geom);
1604 else if (is_np_plot_chart (chart_item))
1605 xrchart_draw_np_plot (chart_item, cr, &geom);
1606 else if (is_piechart (chart_item))
1607 xrchart_draw_piechart (chart_item, cr, &geom);
1608 else if (is_barchart (chart_item))
1609 xrchart_draw_barchart (chart_item, cr, &geom);
1610 else if (is_roc_chart (chart_item))
1611 xrchart_draw_roc (chart_item, cr, &geom);
1612 else if (is_scree (chart_item))
1613 xrchart_draw_scree (chart_item, cr, &geom);
1614 else if (is_spreadlevel_plot_chart (chart_item))
1615 xrchart_draw_spreadlevel (chart_item, cr, &geom);
1616 else if (is_scatterplot_chart (chart_item))
1617 xrchart_draw_scatterplot (chart_item, cr, &geom);
1620 xrchart_geometry_free (cr, &geom);
1626 xr_draw_png_chart (const struct chart_item *item,
1627 const char *file_name_template, int number,
1628 const struct cell_color *fg,
1629 const struct cell_color *bg)
1631 const int width = 640;
1632 const int length = 480;
1634 cairo_surface_t *surface;
1635 cairo_status_t status;
1636 const char *number_pos;
1640 number_pos = strchr (file_name_template, '#');
1641 if (number_pos != NULL)
1642 file_name = xasprintf ("%.*s%d%s.png", (int) (number_pos - file_name_template),
1643 file_name_template, number, number_pos + 1);
1645 file_name = xasprintf ("%s.png", file_name_template);
1647 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1648 cr = cairo_create (surface);
1650 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1653 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1655 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1657 status = cairo_surface_write_to_png (surface, file_name);
1658 if (status != CAIRO_STATUS_SUCCESS)
1659 msg (ME, _("error writing output file `%s': %s"),
1660 file_name, cairo_status_to_string (status));
1663 cairo_surface_destroy (surface);
1670 xr_draw_eps_chart (const struct chart_item *item,
1671 const char *file_name_template, int number,
1672 const struct cell_color *fg,
1673 const struct cell_color *bg)
1675 const int width = 640;
1676 const int length = 480;
1678 cairo_surface_t *surface;
1679 const char *number_pos;
1683 number_pos = strchr (file_name_template, '#');
1684 if (number_pos != NULL)
1685 file_name = xasprintf ("%.*s%d%s.eps", (int) (number_pos - file_name_template),
1686 file_name_template, number, number_pos + 1);
1688 file_name = xasprintf ("%s.eps", file_name_template);
1690 surface = cairo_ps_surface_create (file_name, width, length);
1691 cairo_ps_surface_set_eps (surface, true);
1692 cr = cairo_create (surface);
1694 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1697 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1699 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1702 cairo_surface_destroy (surface);