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/message.h"
24 #include "libpspp/pool.h"
25 #include "libpspp/start-date.h"
26 #include "libpspp/str.h"
27 #include "libpspp/string-map.h"
28 #include "libpspp/version.h"
29 #include "data/file-handle-def.h"
30 #include "output/cairo-chart.h"
31 #include "output/chart-item-provider.h"
32 #include "output/charts/boxplot.h"
33 #include "output/charts/np-plot.h"
34 #include "output/charts/piechart.h"
35 #include "output/charts/barchart.h"
36 #include "output/charts/plot-hist.h"
37 #include "output/charts/roc-chart.h"
38 #include "output/charts/spreadlevel-plot.h"
39 #include "output/charts/scree.h"
40 #include "output/charts/scatterplot.h"
41 #include "output/driver-provider.h"
42 #include "output/group-item.h"
43 #include "output/message-item.h"
44 #include "output/options.h"
45 #include "output/page-setup-item.h"
46 #include "output/render.h"
47 #include "output/table-item.h"
48 #include "output/table.h"
49 #include "output/text-item.h"
51 #include <cairo/cairo-pdf.h>
52 #include <cairo/cairo-ps.h>
53 #include <cairo/cairo-svg.h>
54 #include <cairo/cairo.h>
56 #include <pango/pango-font.h>
57 #include <pango/pango-layout.h>
58 #include <pango/pango.h>
59 #include <pango/pangocairo.h>
62 #include "gl/c-ctype.h"
63 #include "gl/c-strcase.h"
64 #include "gl/intprops.h"
65 #include "gl/minmax.h"
66 #include "gl/xalloc.h"
69 #define _(msgid) gettext (msgid)
71 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
75 /* The unit used for internal measurements is inch/(72 * XR_POINT).
76 (Thus, XR_POINT units represent one point.) */
77 #define XR_POINT PANGO_SCALE
79 /* Conversions to and from points. */
83 return x / (double) XR_POINT;
86 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
90 return x * (PANGO_SCALE * 72 / 96);
93 /* Dimensions for drawing lines in tables. */
94 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
95 #define XR_LINE_SPACE XR_POINT /* Space between double lines. */
108 XR_FONT_PROPORTIONAL,
114 /* A font for use with Cairo. */
117 PangoFontDescription *desc;
121 /* An output item whose rendering is in progress. */
124 /* Renders as much of itself as it can on the current page. Returns true
125 if rendering is complete, false if the output item needs another
127 bool (*render) (struct xr_render_fsm *, struct xr_driver *);
129 /* Destroys the output item. */
130 void (*destroy) (struct xr_render_fsm *);
133 /* Cairo output driver. */
136 struct output_driver driver;
138 /* User parameters. */
139 struct xr_font fonts[XR_N_FONTS];
141 int width; /* Page width minus margins. */
142 int length; /* Page length minus margins and header. */
144 int left_margin; /* Left margin in inch/(72 * XR_POINT). */
145 int right_margin; /* Right margin in inch/(72 * XR_POINT). */
146 int top_margin; /* Top margin in inch/(72 * XR_POINT). */
147 int bottom_margin; /* Bottom margin in inch/(72 * XR_POINT). */
149 int line_space; /* Space between lines. */
150 int line_width; /* Width of lines. */
152 int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
153 int object_spacing; /* Space between output objects. */
155 struct xr_color bg; /* Background color */
156 struct xr_color fg; /* Foreground color */
158 int initial_page_number;
160 struct page_heading headings[2]; /* Top and bottom headings. */
161 int headings_height[2];
163 /* Internal state. */
164 struct render_params *params;
165 int char_width, char_height;
170 cairo_surface_t *surface;
171 int page_number; /* Current page number. */
173 struct xr_render_fsm *fsm;
175 struct string_map heading_vars;
178 static const struct output_driver_class cairo_driver_class;
180 static void xr_driver_destroy_fsm (struct xr_driver *);
181 static void xr_driver_run_fsm (struct xr_driver *);
183 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
184 enum render_line_style styles[TABLE_N_AXES][2],
185 struct cell_color colors[TABLE_N_AXES][2]);
186 static void xr_measure_cell_width (void *, const struct table_cell *,
188 static int xr_measure_cell_height (void *, const struct table_cell *,
190 static void xr_draw_cell (void *, const struct table_cell *, int color_idx,
191 int bb[TABLE_N_AXES][2],
192 int spill[TABLE_N_AXES][2],
193 int clip[TABLE_N_AXES][2]);
194 static int xr_adjust_break (void *, const struct table_cell *,
195 int width, int height);
197 static struct xr_render_fsm *xr_render_output_item (
198 struct xr_driver *, const struct output_item *);
200 /* Output driver basics. */
202 static struct xr_driver *
203 xr_driver_cast (struct output_driver *driver)
205 assert (driver->class == &cairo_driver_class);
206 return UP_CAST (driver, struct xr_driver, driver);
209 static struct driver_option *
210 opt (struct output_driver *d, struct string_map *options, const char *key,
211 const char *default_value)
213 return driver_option_get (d, options, key, default_value);
216 /* Parse color information specified by KEY into {RED,GREEN,BLUE}.
217 Currently, the input string must be of the form "#RRRRGGGGBBBB"
218 Future implementations might allow things like "yellow" and
219 "sky-blue-ultra-brown"
222 parse_color (struct output_driver *d, struct string_map *options,
223 const char *key, const char *default_value,
224 struct xr_color *color)
226 int red, green, blue;
227 char *string = parse_string (opt (d, options, key, default_value));
229 if (3 != sscanf (string, "#%04x%04x%04x", &red, &green, &blue))
231 /* If the parsed option string fails, then try the default value */
232 if ( 3 != sscanf (default_value, "#%04x%04x%04x", &red, &green, &blue))
234 /* ... and if that fails set everything to zero */
235 red = green = blue = 0;
241 /* Convert 16 bit ints to float */
242 color->red = red / (double) 0xFFFF;
243 color->green = green / (double) 0xFFFF;
244 color->blue = blue / (double) 0xFFFF;
247 static PangoFontDescription *
248 parse_font (const char *font, int default_size, bool bold, bool italic)
250 if (!c_strcasecmp (font, "Monospaced"))
253 PangoFontDescription *desc = pango_font_description_from_string (font);
257 /* If the font description didn't include an explicit font size, then set it
258 to DEFAULT_SIZE, which is in inch/72000 units. */
259 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
260 pango_font_description_set_size (desc,
261 (default_size / 1000.0) * PANGO_SCALE);
263 pango_font_description_set_weight (desc, (bold
265 : PANGO_WEIGHT_NORMAL));
266 pango_font_description_set_style (desc, (italic
268 : PANGO_STYLE_NORMAL));
273 static PangoFontDescription *
274 parse_font_option (struct output_driver *d, struct string_map *options,
275 const char *key, const char *default_value,
276 int default_size, bool bold, bool italic)
278 char *string = parse_string (opt (d, options, key, default_value));
279 PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
282 msg (MW, _("`%s': bad font specification"), string);
284 /* Fall back to DEFAULT_VALUE, which had better be a valid font
286 desc = parse_font (default_value, default_size, bold, italic);
287 assert (desc != NULL);
295 apply_options (struct xr_driver *xr, struct string_map *o)
297 struct output_driver *d = &xr->driver;
299 /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
300 int left_margin, right_margin;
301 int top_margin, bottom_margin;
302 int paper_width, paper_length;
304 int min_break[TABLE_N_AXES];
306 /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
307 const double scale = XR_POINT / 1000.;
311 for (i = 0; i < XR_N_FONTS; i++)
313 struct xr_font *font = &xr->fonts[i];
315 if (font->desc != NULL)
316 pango_font_description_free (font->desc);
319 font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
320 xr->fonts[XR_FONT_FIXED].desc = parse_font_option
321 (d, o, "fixed-font", "monospace", font_size, false, false);
322 xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font_option (
323 d, o, "prop-font", "sans serif", font_size, false, false);
324 xr->fonts[XR_FONT_EMPHASIS].desc = parse_font_option (
325 d, o, "emph-font", "sans serif", font_size, false, true);
327 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg);
328 parse_color (d, o, "foreground-color", "#000000000000", &xr->fg);
330 /* Get dimensions. */
331 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
332 left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
333 right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
334 top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
335 bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
337 min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
338 min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
340 int object_spacing = (parse_dimension (opt (d, o, "object-spacing", NULL))
343 /* Convert to inch/(XR_POINT * 72). */
344 xr->left_margin = left_margin * scale;
345 xr->right_margin = right_margin * scale;
346 xr->top_margin = top_margin * scale;
347 xr->bottom_margin = bottom_margin * scale;
348 xr->width = (paper_width - left_margin - right_margin) * scale;
349 xr->length = (paper_length - top_margin - bottom_margin) * scale;
350 xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
351 xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
352 xr->object_spacing = object_spacing >= 0 ? object_spacing : XR_POINT * 12;
354 /* There are no headings so headings_height can stay 0. */
357 static struct xr_driver *
358 xr_allocate (const char *name, int device_type, struct string_map *o)
360 struct xr_driver *xr = xzalloc (sizeof *xr);
361 struct output_driver *d = &xr->driver;
363 output_driver_init (d, &cairo_driver_class, name, device_type);
365 string_map_init (&xr->heading_vars);
367 apply_options (xr, o);
373 pango_to_xr (int pango)
375 return (XR_POINT != PANGO_SCALE
376 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
383 return (XR_POINT != PANGO_SCALE
384 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
389 xr_measure_fonts (cairo_t *cairo, const struct xr_font fonts[XR_N_FONTS],
390 int *char_width, int *char_height)
394 for (int i = 0; i < XR_N_FONTS; i++)
396 PangoLayout *layout = pango_cairo_create_layout (cairo);
397 pango_layout_set_font_description (layout, fonts[i].desc);
399 pango_layout_set_text (layout, "0", 1);
402 pango_layout_get_size (layout, &cw, &ch);
403 *char_width = MAX (*char_width, pango_to_xr (cw));
404 *char_height = MAX (*char_height, pango_to_xr (ch));
406 g_object_unref (G_OBJECT (layout));
411 get_layout_height (PangoLayout *layout)
414 pango_layout_get_size (layout, &w, &h);
419 xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
420 const struct page_heading *ph, int page_number,
421 int width, bool draw, int base_y)
423 PangoLayout *layout = pango_cairo_create_layout (cairo);
424 pango_layout_set_font_description (layout, font);
427 for (size_t i = 0; i < ph->n; i++)
429 const struct page_paragraph *pp = &ph->paragraphs[i];
431 char *markup = output_driver_substitute_heading_vars (pp->markup,
433 pango_layout_set_markup (layout, markup, -1);
436 pango_layout_set_alignment (
438 (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
439 : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER
440 : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
441 : PANGO_ALIGN_RIGHT));
442 pango_layout_set_width (layout, xr_to_pango (width));
446 cairo_translate (cairo, 0, xr_to_pt (y + base_y));
447 pango_cairo_show_layout (cairo, layout);
448 cairo_restore (cairo);
451 y += pango_to_xr (get_layout_height (layout));
454 g_object_unref (G_OBJECT (layout));
460 xr_measure_headings (cairo_surface_t *surface,
461 const PangoFontDescription *font,
462 const struct page_heading headings[2],
463 int width, int object_spacing, int height[2])
465 cairo_t *cairo = cairo_create (surface);
467 for (int i = 0; i < 2; i++)
469 int h = xr_render_page_heading (cairo, font, &headings[i], -1,
472 /* If the top heading is nonempty, add some space below it. */
480 cairo_destroy (cairo);
485 xr_check_fonts (cairo_surface_t *surface,
486 const struct xr_font fonts[XR_N_FONTS],
487 int usable_width, int usable_length)
489 cairo_t *cairo = cairo_create (surface);
490 int char_width, char_height;
491 xr_measure_fonts (cairo, fonts, &char_width, &char_height);
492 cairo_destroy (cairo);
495 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
496 if (usable_width / char_width < MIN_WIDTH)
498 msg (ME, _("The defined page is not wide enough to hold at least %d "
499 "characters in the default font. In fact, there's only "
500 "room for %d characters."),
501 MIN_WIDTH, usable_width / char_width);
504 if (usable_length / char_height < MIN_LENGTH)
506 msg (ME, _("The defined page is not long enough to hold at least %d "
507 "lines in the default font. In fact, there's only "
508 "room for %d lines."),
509 MIN_LENGTH, usable_length / char_height);
516 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
520 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
522 xr_measure_fonts (xr->cairo, xr->fonts, &xr->char_width, &xr->char_height);
524 for (int i = 0; i < XR_N_FONTS; i++)
526 struct xr_font *font = &xr->fonts[i];
527 font->layout = pango_cairo_create_layout (cairo);
528 pango_layout_set_font_description (font->layout, font->desc);
531 if (xr->params == NULL)
533 xr->params = xmalloc (sizeof *xr->params);
534 xr->params->draw_line = xr_draw_line;
535 xr->params->measure_cell_width = xr_measure_cell_width;
536 xr->params->measure_cell_height = xr_measure_cell_height;
537 xr->params->adjust_break = xr_adjust_break;
538 xr->params->draw_cell = xr_draw_cell;
539 xr->params->aux = xr;
540 xr->params->size[H] = xr->width;
541 xr->params->size[V] = xr->length;
542 xr->params->font_size[H] = xr->char_width;
543 xr->params->font_size[V] = xr->char_height;
545 int lw = XR_LINE_WIDTH;
546 int ls = XR_LINE_SPACE;
547 for (int i = 0; i < TABLE_N_AXES; i++)
549 xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
550 xr->params->line_widths[i][RENDER_LINE_SINGLE] = lw;
551 xr->params->line_widths[i][RENDER_LINE_DASHED] = lw;
552 xr->params->line_widths[i][RENDER_LINE_THICK] = lw * 2;
553 xr->params->line_widths[i][RENDER_LINE_THIN] = lw / 2;
554 xr->params->line_widths[i][RENDER_LINE_DOUBLE] = 2 * lw + ls;
557 for (int i = 0; i < TABLE_N_AXES; i++)
558 xr->params->min_break[i] = xr->min_break[i];
559 xr->params->supports_margins = true;
560 xr->params->rtl = render_direction_rtl ();
563 cairo_set_source_rgb (xr->cairo, xr->fg.red, xr->fg.green, xr->fg.blue);
566 static struct output_driver *
567 xr_create (const char *file_name, enum settings_output_devices device_type,
568 struct string_map *o, enum xr_output_type file_type)
570 struct xr_driver *xr;
571 cairo_status_t status;
572 double width_pt, length_pt;
574 xr = xr_allocate (file_name, device_type, o);
576 width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
577 length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
578 if (file_type == XR_PDF)
579 xr->surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
580 else if (file_type == XR_PS)
581 xr->surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
582 else if (file_type == XR_SVG)
583 xr->surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
587 status = cairo_surface_status (xr->surface);
588 if (status != CAIRO_STATUS_SUCCESS)
590 msg (ME, _("error opening output file `%s': %s"),
591 file_name, cairo_status_to_string (status));
595 if (!xr_check_fonts (xr->surface, xr->fonts, xr->width, xr->length))
601 output_driver_destroy (&xr->driver);
605 static struct output_driver *
606 xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
607 struct string_map *o)
609 struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_PDF);
614 static struct output_driver *
615 xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
616 struct string_map *o)
618 struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_PS);
623 static struct output_driver *
624 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
625 struct string_map *o)
627 struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_SVG);
633 xr_destroy (struct output_driver *driver)
635 struct xr_driver *xr = xr_driver_cast (driver);
638 xr_driver_destroy_fsm (xr);
640 if (xr->cairo != NULL)
642 cairo_surface_finish (xr->surface);
643 cairo_status_t status = cairo_status (xr->cairo);
644 if (status != CAIRO_STATUS_SUCCESS)
645 msg (ME, _("error drawing output for %s driver: %s"),
646 output_driver_get_name (driver),
647 cairo_status_to_string (status));
648 cairo_surface_destroy (xr->surface);
650 cairo_destroy (xr->cairo);
653 for (i = 0; i < XR_N_FONTS; i++)
655 struct xr_font *font = &xr->fonts[i];
657 if (font->desc != NULL)
658 pango_font_description_free (font->desc);
659 if (font->layout != NULL)
660 g_object_unref (font->layout);
668 xr_flush (struct output_driver *driver)
670 struct xr_driver *xr = xr_driver_cast (driver);
672 cairo_surface_flush (cairo_get_target (xr->cairo));
676 xr_update_page_setup (struct output_driver *driver,
677 const struct page_setup *ps)
679 struct xr_driver *xr = xr_driver_cast (driver);
681 xr->initial_page_number = ps->initial_page_number;
682 xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
687 int usable[TABLE_N_AXES];
688 for (int i = 0; i < 2; i++)
689 usable[i] = (ps->paper[i]
690 - (ps->margins[i][0] + ps->margins[i][1])) * 72 * XR_POINT;
692 int headings_height[2];
693 usable[V] -= xr_measure_headings (
694 xr->surface, xr->fonts[XR_FONT_PROPORTIONAL].desc, ps->headings,
695 usable[H], xr->object_spacing, headings_height);
697 enum table_axis h = ps->orientation == PAGE_LANDSCAPE;
698 enum table_axis v = !h;
699 if (!xr_check_fonts (xr->surface, xr->fonts, usable[h], usable[v]))
702 for (int i = 0; i < 2; i++)
704 page_heading_uninit (&xr->headings[i]);
705 page_heading_copy (&xr->headings[i], &ps->headings[i]);
706 xr->headings_height[i] = headings_height[i];
708 xr->width = usable[h];
709 xr->length = usable[v];
710 xr->left_margin = ps->margins[h][0] * 72 * XR_POINT;
711 xr->right_margin = ps->margins[h][1] * 72 * XR_POINT;
712 xr->top_margin = ps->margins[v][0] * 72 * XR_POINT;
713 xr->bottom_margin = ps->margins[v][1] * 72 * XR_POINT;
714 cairo_pdf_surface_set_size (xr->surface,
715 ps->paper[h] * 72.0, ps->paper[v] * 72.0);
719 xr_submit (struct output_driver *driver, const struct output_item *output_item)
721 struct xr_driver *xr = xr_driver_cast (driver);
723 if (is_page_setup_item (output_item))
725 xr_update_page_setup (driver,
726 to_page_setup_item (output_item)->page_setup);
732 xr->page_number = xr->initial_page_number - 1;
733 xr_set_cairo (xr, cairo_create (xr->surface));
734 cairo_save (xr->cairo);
735 xr_driver_next_page (xr, xr->cairo);
738 xr_driver_output_item (xr, output_item);
739 while (xr_driver_need_new_page (xr))
741 cairo_restore (xr->cairo);
742 cairo_show_page (xr->cairo);
743 cairo_save (xr->cairo);
744 xr_driver_next_page (xr, xr->cairo);
748 /* Functions for rendering a series of output items to a series of Cairo
749 contexts, with pagination.
751 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
752 its underlying implementation.
754 See the big comment in cairo.h for intended usage. */
756 /* Gives new page CAIRO to XR for output. */
758 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
761 cairo_set_source_rgb (cairo, xr->bg.red, xr->bg.green, xr->bg.blue);
762 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
764 cairo_restore (cairo);
766 cairo_translate (cairo,
767 xr_to_pt (xr->left_margin),
768 xr_to_pt (xr->top_margin + xr->headings_height[0]));
774 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
775 &xr->headings[0], xr->page_number, xr->width, true,
776 -xr->headings_height[0]);
777 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
778 &xr->headings[1], xr->page_number, xr->width, true,
781 xr_driver_run_fsm (xr);
784 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
785 rendering a previous output item, that is, only if xr_driver_need_new_page()
788 xr_driver_output_item (struct xr_driver *xr,
789 const struct output_item *output_item)
791 assert (xr->fsm == NULL);
792 xr->fsm = xr_render_output_item (xr, output_item);
793 xr_driver_run_fsm (xr);
796 /* Returns true if XR is in the middle of rendering an output item and needs a
797 new page to be appended using xr_driver_next_page() to make progress,
800 xr_driver_need_new_page (const struct xr_driver *xr)
802 return xr->fsm != NULL;
805 /* Returns true if the current page doesn't have any content yet. */
807 xr_driver_is_page_blank (const struct xr_driver *xr)
813 xr_driver_destroy_fsm (struct xr_driver *xr)
817 xr->fsm->destroy (xr->fsm);
823 xr_driver_run_fsm (struct xr_driver *xr)
825 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
826 xr_driver_destroy_fsm (xr);
830 xr_layout_cell (struct xr_driver *, const struct table_cell *,
831 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
832 int *width, int *height, int *brk);
835 set_source_rgba (cairo_t *cairo, const struct cell_color *color)
837 cairo_set_source_rgba (cairo,
838 color->r / 255., color->g / 255., color->b / 255.,
839 color->alpha / 255.);
843 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
844 const struct cell_color *color)
846 cairo_new_path (xr->cairo);
847 set_source_rgba (xr->cairo, color);
848 cairo_set_line_width (
850 xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
851 : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
853 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
854 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
855 cairo_stroke (xr->cairo);
859 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
861 cairo_new_path (xr->cairo);
862 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
863 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
864 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y0 + xr->y));
865 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
866 cairo_line_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y1 + xr->y));
867 cairo_close_path (xr->cairo);
868 cairo_stroke (xr->cairo);
872 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
874 cairo_new_path (xr->cairo);
875 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
876 cairo_rectangle (xr->cairo,
877 xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y),
878 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
879 cairo_fill (xr->cairo);
882 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
883 shortening it to X0...X1 if SHORTEN is true.
884 Draws a horizontal line X1...X3 at Y if RIGHT says so,
885 shortening it to X2...X3 if SHORTEN is true. */
887 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
888 enum render_line_style left, enum render_line_style right,
889 const struct cell_color *left_color,
890 const struct cell_color *right_color,
893 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
894 && cell_color_equal (left_color, right_color))
895 dump_line (xr, x0, y, x3, y, left, left_color);
898 if (left != RENDER_LINE_NONE)
899 dump_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
900 if (right != RENDER_LINE_NONE)
901 dump_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
905 /* Draws a vertical line Y0...Y2 at X if TOP says so,
906 shortening it to Y0...Y1 if SHORTEN is true.
907 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
908 shortening it to Y2...Y3 if SHORTEN is true. */
910 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
911 enum render_line_style top, enum render_line_style bottom,
912 const struct cell_color *top_color,
913 const struct cell_color *bottom_color,
916 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
917 && cell_color_equal (top_color, bottom_color))
918 dump_line (xr, x, y0, x, y3, top, top_color);
921 if (top != RENDER_LINE_NONE)
922 dump_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
923 if (bottom != RENDER_LINE_NONE)
924 dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
929 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
930 enum render_line_style styles[TABLE_N_AXES][2],
931 struct cell_color colors[TABLE_N_AXES][2])
933 const int x0 = bb[H][0];
934 const int y0 = bb[V][0];
935 const int x3 = bb[H][1];
936 const int y3 = bb[V][1];
937 const int top = styles[H][0];
938 const int bottom = styles[H][1];
940 int start_side = render_direction_rtl();
941 int end_side = !start_side;
942 const int start_of_line = styles[V][start_side];
943 const int end_of_line = styles[V][end_side];
944 const struct cell_color *top_color = &colors[H][0];
945 const struct cell_color *bottom_color = &colors[H][1];
946 const struct cell_color *start_color = &colors[V][start_side];
947 const struct cell_color *end_color = &colors[V][end_side];
949 /* The algorithm here is somewhat subtle, to allow it to handle
950 all the kinds of intersections that we need.
952 Three additional ordinates are assigned along the x axis. The
953 first is xc, midway between x0 and x3. The others are x1 and
954 x2; for a single vertical line these are equal to xc, and for
955 a double vertical line they are the ordinates of the left and
956 right half of the double line.
958 yc, y1, and y2 are assigned similarly along the y axis.
960 The following diagram shows the coordinate system and output
961 for double top and bottom lines, single left line, and no
965 y0 ________________________
971 y1 = y2 = yc |######### # |
976 y3 |________#_____#_______|
978 struct xr_driver *xr = xr_;
980 /* Offset from center of each line in a pair of double lines. */
981 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
983 /* Are the lines along each axis single or double?
984 (It doesn't make sense to have different kinds of line on the
985 same axis, so we don't try to gracefully handle that case.) */
986 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
987 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
989 /* When horizontal lines are doubled,
990 the left-side line along y1 normally runs from x0 to x2,
991 and the right-side line along y1 from x3 to x1.
992 If the top-side line is also doubled, we shorten the y1 lines,
993 so that the left-side line runs only to x1,
994 and the right-side line only to x2.
995 Otherwise, the horizontal line at y = y1 below would cut off
996 the intersection, which looks ugly:
998 y0 ________________________
1003 y1 |######### ########|
1006 y2 |######################|
1009 y3 |______________________|
1010 It is more of a judgment call when the horizontal line is
1011 single. We actually choose to cut off the line anyhow, as
1012 shown in the first diagram above.
1014 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
1015 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
1016 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
1017 int horz_line_ofs = double_vert ? double_line_ofs : 0;
1018 int xc = (x0 + x3) / 2;
1019 int x1 = xc - horz_line_ofs;
1020 int x2 = xc + horz_line_ofs;
1022 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
1023 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
1024 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
1025 int vert_line_ofs = double_horz ? double_line_ofs : 0;
1026 int yc = (y0 + y3) / 2;
1027 int y1 = yc - vert_line_ofs;
1028 int y2 = yc + vert_line_ofs;
1031 horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
1032 start_color, end_color, shorten_yc_line);
1035 horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
1036 start_color, end_color, shorten_y1_lines);
1037 horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
1038 start_color, end_color, shorten_y2_lines);
1042 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
1046 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
1048 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
1054 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
1055 int *min_width, int *max_width)
1057 struct xr_driver *xr = xr_;
1058 int bb[TABLE_N_AXES][2];
1059 int clip[TABLE_N_AXES][2];
1066 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1067 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
1070 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
1073 *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
1074 + cell->style->cell_style.margin[H][1]);
1076 *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
1077 + cell->style->cell_style.margin[H][1]);
1081 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
1083 struct xr_driver *xr = xr_;
1084 int bb[TABLE_N_AXES][2];
1085 int clip[TABLE_N_AXES][2];
1089 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1090 + cell->style->cell_style.margin[H][1]);
1093 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1094 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
1095 h += px_to_xr (cell->style->cell_style.margin[V][0]
1096 + cell->style->cell_style.margin[V][1]);
1100 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
1103 xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
1104 int bb[TABLE_N_AXES][2],
1105 int spill[TABLE_N_AXES][2],
1106 int clip[TABLE_N_AXES][2])
1108 struct xr_driver *xr = xr_;
1111 cairo_save (xr->cairo);
1112 int bg_clip[TABLE_N_AXES][2];
1113 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1115 bg_clip[axis][0] = clip[axis][0];
1116 if (bb[axis][0] == clip[axis][0])
1117 bg_clip[axis][0] -= spill[axis][0];
1119 bg_clip[axis][1] = clip[axis][1];
1120 if (bb[axis][1] == clip[axis][1])
1121 bg_clip[axis][1] += spill[axis][1];
1123 xr_clip (xr, bg_clip);
1124 set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]);
1126 bb[H][0] - spill[H][0],
1127 bb[V][0] - spill[V][0],
1128 bb[H][1] + spill[H][1],
1129 bb[V][1] + spill[V][1]);
1130 cairo_restore (xr->cairo);
1132 cairo_save (xr->cairo);
1133 set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]);
1135 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1137 bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
1138 bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
1140 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
1141 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1142 cairo_restore (xr->cairo);
1146 xr_adjust_break (void *xr_, const struct table_cell *cell,
1147 int width, int height)
1149 struct xr_driver *xr = xr_;
1150 int bb[TABLE_N_AXES][2];
1151 int clip[TABLE_N_AXES][2];
1154 if (xr_measure_cell_height (xr_, cell, width) < height)
1158 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1159 + cell->style->cell_style.margin[H][1]);
1163 bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
1164 + cell->style->cell_style.margin[V][1]);
1165 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1166 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1171 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
1173 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1175 double x0 = xr_to_pt (clip[H][0] + xr->x);
1176 double y0 = xr_to_pt (clip[V][0] + xr->y);
1177 double x1 = xr_to_pt (clip[H][1] + xr->x);
1178 double y1 = xr_to_pt (clip[V][1] + xr->y);
1180 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1181 cairo_clip (xr->cairo);
1186 add_attr_with_start (PangoAttrList *list, PangoAttribute *attr, guint start_index)
1188 attr->start_index = start_index;
1189 pango_attr_list_insert (list, attr);
1193 markup_escape (const char *in, struct string *out)
1195 for (int c = *in++; c; c = *in++)
1199 ds_put_cstr (out, "&");
1202 ds_put_cstr (out, "<");
1205 ds_put_cstr (out, ">");
1208 ds_put_byte (out, c);
1214 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
1216 int size[TABLE_N_AXES];
1217 pango_layout_get_size (layout, &size[H], &size[V]);
1222 xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
1223 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1224 int *widthp, int *brk)
1226 const struct font_style *font_style = &cell->style->font_style;
1227 const struct cell_style *cell_style = &cell->style->cell_style;
1228 unsigned int options = cell->options;
1230 enum table_axis X = options & TAB_ROTATE ? V : H;
1231 enum table_axis Y = !X;
1232 int R = options & TAB_ROTATE ? 0 : 1;
1234 struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1235 : &xr->fonts[XR_FONT_PROPORTIONAL]);
1236 struct xr_font local_font;
1237 if (font_style->typeface)
1239 PangoFontDescription *desc = parse_font (
1240 font_style->typeface,
1241 font_style->size ? font_style->size * 1000 * 72 / 128 : 10000,
1242 font_style->bold, font_style->italic);
1245 PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1246 pango_layout_set_font_description (layout, desc);
1248 local_font.desc = desc;
1249 local_font.layout = layout;
1254 const char *text = cell->text;
1255 enum table_halign halign = table_halign_interpret (
1256 cell_style->halign, cell->options & TAB_NUMERIC);
1257 if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
1259 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
1261 const char *decimal = strrchr (text, cell_style->decimal_char);
1264 pango_layout_set_text (font->layout, decimal, strlen (decimal));
1265 pango_layout_set_width (font->layout, -1);
1266 margin_adjustment += get_layout_dimension (font->layout, H);
1269 if (margin_adjustment < 0)
1270 bb[H][1] += margin_adjustment;
1273 struct string tmp = DS_EMPTY_INITIALIZER;
1275 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
1276 Pango's implementation of it): it will break after a period or a comma
1277 that precedes a digit, e.g. in ".000" it will break after the period.
1278 This code looks for such a situation and inserts a U+2060 WORD JOINER
1279 to prevent the break.
1281 This isn't necessary when the decimal point is between two digits
1282 (e.g. "0.000" won't be broken) or when the display width is not limited so
1283 that word wrapping won't happen.
1285 It isn't necessary to look for more than one period or comma, as would
1286 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
1287 are present then there will always be a digit on both sides of every
1288 period and comma. */
1289 if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
1291 const char *decimal = text + strcspn (text, ".,");
1293 && c_isdigit (decimal[1])
1294 && (decimal == text || !c_isdigit (decimal[-1])))
1296 ds_extend (&tmp, strlen (text) + 16);
1297 ds_put_substring (&tmp, ss_buffer (text, decimal - text + 1));
1298 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
1299 ds_put_cstr (&tmp, decimal + 1);
1303 if (cell->n_footnotes)
1305 int footnote_adjustment;
1306 if (cell->n_footnotes == 1 && halign == TABLE_HALIGN_RIGHT)
1308 const char *marker = cell->footnotes[0]->marker;
1309 pango_layout_set_text (font->layout, marker, strlen (marker));
1311 PangoAttrList *attrs = pango_attr_list_new ();
1312 pango_attr_list_insert (attrs, pango_attr_rise_new (7000));
1313 pango_layout_set_attributes (font->layout, attrs);
1314 pango_attr_list_unref (attrs);
1316 int w = get_layout_dimension (font->layout, X);
1317 int right_margin = px_to_xr (cell_style->margin[X][R]);
1318 footnote_adjustment = MIN (w, right_margin);
1320 pango_layout_set_attributes (font->layout, NULL);
1323 footnote_adjustment = px_to_xr (cell_style->margin[X][R]);
1326 bb[X][R] += footnote_adjustment;
1328 bb[X][R] -= footnote_adjustment;
1330 if (ds_is_empty (&tmp))
1332 ds_extend (&tmp, strlen (text) + 16);
1333 ds_put_cstr (&tmp, text);
1335 size_t initial_length = ds_length (&tmp);
1337 for (size_t i = 0; i < cell->n_footnotes; i++)
1340 ds_put_byte (&tmp, ',');
1342 const char *marker = cell->footnotes[i]->marker;
1343 if (options & TAB_MARKUP)
1344 markup_escape (marker, &tmp);
1346 ds_put_cstr (&tmp, marker);
1349 if (options & TAB_MARKUP)
1350 pango_layout_set_markup (font->layout,
1351 ds_cstr (&tmp), ds_length (&tmp));
1353 pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
1355 PangoAttrList *attrs = pango_attr_list_new ();
1356 if (font_style->underline)
1357 pango_attr_list_insert (attrs, pango_attr_underline_new (
1358 PANGO_UNDERLINE_SINGLE));
1359 add_attr_with_start (attrs, pango_attr_rise_new (7000), initial_length);
1360 add_attr_with_start (
1361 attrs, pango_attr_font_desc_new (font->desc), initial_length);
1362 pango_layout_set_attributes (font->layout, attrs);
1363 pango_attr_list_unref (attrs);
1367 const char *content = ds_is_empty (&tmp) ? text : ds_cstr (&tmp);
1368 if (options & TAB_MARKUP)
1369 pango_layout_set_markup (font->layout, content, -1);
1371 pango_layout_set_text (font->layout, content, -1);
1373 if (font_style->underline)
1375 PangoAttrList *attrs = pango_attr_list_new ();
1376 pango_attr_list_insert (attrs, pango_attr_underline_new (
1377 PANGO_UNDERLINE_SINGLE));
1378 pango_layout_set_attributes (font->layout, attrs);
1379 pango_attr_list_unref (attrs);
1384 pango_layout_set_alignment (font->layout,
1385 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
1386 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
1387 : PANGO_ALIGN_CENTER));
1388 pango_layout_set_width (
1390 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
1391 pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1393 if (clip[H][0] != clip[H][1])
1395 cairo_save (xr->cairo);
1396 if (!(options & TAB_ROTATE))
1398 if (options & TAB_ROTATE)
1400 cairo_translate (xr->cairo,
1401 xr_to_pt (bb[H][0] + xr->x),
1402 xr_to_pt (bb[V][1] + xr->y));
1403 cairo_rotate (xr->cairo, -M_PI_2);
1406 cairo_translate (xr->cairo,
1407 xr_to_pt (bb[H][0] + xr->x),
1408 xr_to_pt (bb[V][0] + xr->y));
1409 pango_cairo_show_layout (xr->cairo, font->layout);
1411 /* If enabled, this draws a blue rectangle around the extents of each
1412 line of text, which can be rather useful for debugging layout
1416 PangoLayoutIter *iter;
1417 iter = pango_layout_get_iter (font->layout);
1420 PangoRectangle extents;
1422 pango_layout_iter_get_line_extents (iter, &extents, NULL);
1423 cairo_save (xr->cairo);
1424 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1426 pango_to_xr (extents.x) - xr->x,
1427 pango_to_xr (extents.y) - xr->y,
1428 pango_to_xr (extents.x + extents.width) - xr->x,
1429 pango_to_xr (extents.y + extents.height) - xr->y);
1430 cairo_restore (xr->cairo);
1432 while (pango_layout_iter_next_line (iter));
1433 pango_layout_iter_free (iter);
1436 cairo_restore (xr->cairo);
1439 int size[TABLE_N_AXES];
1440 pango_layout_get_size (font->layout, &size[H], &size[V]);
1441 int w = pango_to_xr (size[X]);
1442 int h = pango_to_xr (size[Y]);
1445 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
1447 PangoLayoutIter *iter;
1448 int best UNUSED = 0;
1450 /* Choose a breakpoint between lines instead of in the middle of one. */
1451 iter = pango_layout_get_iter (font->layout);
1454 PangoRectangle extents;
1458 pango_layout_iter_get_line_extents (iter, NULL, &extents);
1459 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1460 extents.x = pango_to_xr (extents.x);
1461 extents.y = pango_to_xr (y0);
1462 extents.width = pango_to_xr (extents.width);
1463 extents.height = pango_to_xr (y1 - y0);
1464 bottom = bb[V][0] + extents.y + extents.height;
1465 if (bottom < bb[V][1])
1467 if (brk && clip[H][0] != clip[H][1])
1475 while (pango_layout_iter_next_line (iter));
1476 pango_layout_iter_free (iter);
1478 /* If enabled, draws a green line across the chosen breakpoint, which can
1479 be useful for debugging issues with breaking. */
1482 if (best && !xr->nest)
1483 dump_line (xr, -xr->left_margin, best,
1484 xr->width + xr->right_margin, best,
1486 &(struct cell_color) CELL_COLOR (0, 255, 0));
1490 pango_layout_set_attributes (font->layout, NULL);
1492 if (font == &local_font)
1494 g_object_unref (G_OBJECT (font->layout));
1495 pango_font_description_free (font->desc);
1502 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1503 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1504 int *width, int *height, int *brk)
1509 /* If enabled, draws a blue rectangle around the cell extents, which can be
1510 useful for debugging layout. */
1513 if (clip[H][0] != clip[H][1])
1515 int offset = (xr->nest) * XR_POINT;
1517 cairo_save (xr->cairo);
1518 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1520 bb[H][0] + offset, bb[V][0] + offset,
1521 bb[H][1] - offset, bb[V][1] - offset);
1522 cairo_restore (xr->cairo);
1528 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
1531 struct output_driver_factory pdf_driver_factory =
1532 { "pdf", "pspp.pdf", xr_pdf_create };
1533 struct output_driver_factory ps_driver_factory =
1534 { "ps", "pspp.ps", xr_ps_create };
1535 struct output_driver_factory svg_driver_factory =
1536 { "svg", "pspp.svg", xr_svg_create };
1538 static const struct output_driver_class cairo_driver_class =
1546 /* GUI rendering helpers. */
1550 struct output_item *item;
1553 struct render_pager *p;
1554 struct xr_driver *xr;
1557 #define CHART_WIDTH 500
1558 #define CHART_HEIGHT 375
1563 xr_driver_create (cairo_t *cairo, struct string_map *options)
1565 struct xr_driver *xr = xr_allocate ("cairo", 0, options);
1566 xr_set_cairo (xr, cairo);
1570 /* Destroy XR, which should have been created with xr_driver_create(). Any
1571 cairo_t added to XR is not destroyed, because it is owned by the client. */
1573 xr_driver_destroy (struct xr_driver *xr)
1578 output_driver_destroy (&xr->driver);
1582 static struct xr_rendering *
1583 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1585 struct table_item *table_item;
1586 struct xr_rendering *r;
1588 table_item = table_item_create (table_from_string (TABLE_HALIGN_LEFT, text),
1590 r = xr_rendering_create (xr, &table_item->output_item, cr);
1591 table_item_unref (table_item);
1597 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1599 if (is_table_item (xr->item))
1600 apply_options (xr->xr, o);
1603 struct xr_rendering *
1604 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1607 struct xr_rendering *r = NULL;
1609 if (is_text_item (item))
1610 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1612 else if (is_message_item (item))
1614 const struct message_item *message_item = to_message_item (item);
1615 char *s = msg_to_string (message_item_get_msg (message_item));
1616 r = xr_rendering_create_text (xr, s, cr);
1619 else if (is_table_item (item))
1621 r = xzalloc (sizeof *r);
1622 r->item = output_item_ref (item);
1624 xr_set_cairo (xr, cr);
1625 r->p = render_pager_create (xr->params, to_table_item (item));
1627 else if (is_chart_item (item))
1629 r = xzalloc (sizeof *r);
1630 r->item = output_item_ref (item);
1632 else if (is_group_open_item (item))
1633 r = xr_rendering_create_text (xr, to_group_open_item (item)->command_name,
1640 xr_rendering_destroy (struct xr_rendering *r)
1644 output_item_unref (r->item);
1645 render_pager_destroy (r->p);
1651 xr_rendering_measure (struct xr_rendering *r, int *wp, int *hp)
1655 if (is_table_item (r->item))
1657 w = render_pager_get_size (r->p, H) / XR_POINT;
1658 h = render_pager_get_size (r->p, V) / XR_POINT;
1672 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1673 double x, double y, double width, double height);
1677 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1678 int x0, int y0, int x1, int y1)
1680 if (is_table_item (r->item))
1682 struct xr_driver *xr = r->xr;
1684 xr_set_cairo (xr, cr);
1686 render_pager_draw_region (r->p, x0 * XR_POINT, y0 * XR_POINT,
1687 (x1 - x0) * XR_POINT, (y1 - y0) * XR_POINT);
1690 xr_draw_chart (to_chart_item (r->item), cr,
1691 0, 0, CHART_WIDTH, CHART_HEIGHT);
1695 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1696 double x, double y, double width, double height)
1698 struct xrchart_geometry geom;
1701 cairo_translate (cr, x, y + height);
1702 cairo_scale (cr, 1.0, -1.0);
1703 xrchart_geometry_init (cr, &geom, width, height);
1704 if (is_boxplot (chart_item))
1705 xrchart_draw_boxplot (chart_item, cr, &geom);
1706 else if (is_histogram_chart (chart_item))
1707 xrchart_draw_histogram (chart_item, cr, &geom);
1708 else if (is_np_plot_chart (chart_item))
1709 xrchart_draw_np_plot (chart_item, cr, &geom);
1710 else if (is_piechart (chart_item))
1711 xrchart_draw_piechart (chart_item, cr, &geom);
1712 else if (is_barchart (chart_item))
1713 xrchart_draw_barchart (chart_item, cr, &geom);
1714 else if (is_roc_chart (chart_item))
1715 xrchart_draw_roc (chart_item, cr, &geom);
1716 else if (is_scree (chart_item))
1717 xrchart_draw_scree (chart_item, cr, &geom);
1718 else if (is_spreadlevel_plot_chart (chart_item))
1719 xrchart_draw_spreadlevel (chart_item, cr, &geom);
1720 else if (is_scatterplot_chart (chart_item))
1721 xrchart_draw_scatterplot (chart_item, cr, &geom);
1724 xrchart_geometry_free (cr, &geom);
1730 xr_draw_png_chart (const struct chart_item *item,
1731 const char *file_name_template, int number,
1732 const struct xr_color *fg,
1733 const struct xr_color *bg
1736 const int width = 640;
1737 const int length = 480;
1739 cairo_surface_t *surface;
1740 cairo_status_t status;
1741 const char *number_pos;
1745 number_pos = strchr (file_name_template, '#');
1746 if (number_pos != NULL)
1747 file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1748 file_name_template, number, number_pos + 1);
1750 file_name = xstrdup (file_name_template);
1752 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1753 cr = cairo_create (surface);
1755 cairo_set_source_rgb (cr, bg->red, bg->green, bg->blue);
1758 cairo_set_source_rgb (cr, fg->red, fg->green, fg->blue);
1760 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1762 status = cairo_surface_write_to_png (surface, file_name);
1763 if (status != CAIRO_STATUS_SUCCESS)
1764 msg (ME, _("error writing output file `%s': %s"),
1765 file_name, cairo_status_to_string (status));
1768 cairo_surface_destroy (surface);
1773 struct xr_table_state
1775 struct xr_render_fsm fsm;
1776 struct render_pager *p;
1780 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1782 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1784 while (render_pager_has_next (ts->p))
1788 used = render_pager_draw_next (ts->p, xr->length - xr->y);
1801 xr_table_destroy (struct xr_render_fsm *fsm)
1803 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1805 render_pager_destroy (ts->p);
1809 static struct xr_render_fsm *
1810 xr_render_table (struct xr_driver *xr, struct table_item *table_item)
1812 struct xr_table_state *ts;
1814 ts = xmalloc (sizeof *ts);
1815 ts->fsm.render = xr_table_render;
1816 ts->fsm.destroy = xr_table_destroy;
1819 xr->y += xr->char_height;
1821 ts->p = render_pager_create (xr->params, table_item);
1822 table_item_unref (table_item);
1827 struct xr_chart_state
1829 struct xr_render_fsm fsm;
1830 struct chart_item *chart_item;
1834 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1836 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1838 const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
1840 if (xr->y > xr->length - chart_height)
1843 if (xr->cairo != NULL)
1845 xr_draw_chart (cs->chart_item, xr->cairo,
1848 xr_to_pt (xr->width),
1849 xr_to_pt (chart_height));
1851 xr->y += chart_height;
1857 xr_chart_destroy (struct xr_render_fsm *fsm)
1859 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1861 chart_item_unref (cs->chart_item);
1865 static struct xr_render_fsm *
1866 xr_render_chart (const struct chart_item *chart_item)
1868 struct xr_chart_state *cs;
1870 cs = xmalloc (sizeof *cs);
1871 cs->fsm.render = xr_chart_render;
1872 cs->fsm.destroy = xr_chart_destroy;
1873 cs->chart_item = chart_item_ref (chart_item);
1879 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1885 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1887 /* Nothing to do. */
1890 static struct xr_render_fsm *
1891 xr_render_eject (void)
1893 static struct xr_render_fsm eject_renderer =
1899 return &eject_renderer;
1902 static struct xr_render_fsm *
1903 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1905 enum text_item_type type = text_item_get_type (text_item);
1906 const char *text = text_item_get_text (text_item);
1910 case TEXT_ITEM_PAGE_TITLE:
1911 string_map_replace (&xr->heading_vars, "PageTitle", text);
1914 case TEXT_ITEM_BLANK_LINE:
1916 xr->y += xr->char_height;
1919 case TEXT_ITEM_EJECT_PAGE:
1921 return xr_render_eject ();
1925 return xr_render_table (
1926 xr, text_item_to_table_item (text_item_ref (text_item)));
1932 static struct xr_render_fsm *
1933 xr_render_message (struct xr_driver *xr,
1934 const struct message_item *message_item)
1936 char *s = msg_to_string (message_item_get_msg (message_item));
1937 struct text_item *item = text_item_create (TEXT_ITEM_PARAGRAPH, s);
1939 return xr_render_table (xr, text_item_to_table_item (item));
1942 static struct xr_render_fsm *
1943 xr_render_output_item (struct xr_driver *xr,
1944 const struct output_item *output_item)
1946 if (is_table_item (output_item))
1947 return xr_render_table (xr, table_item_ref (to_table_item (output_item)));
1948 else if (is_chart_item (output_item))
1949 return xr_render_chart (to_chart_item (output_item));
1950 else if (is_text_item (output_item))
1951 return xr_render_text (xr, to_text_item (output_item));
1952 else if (is_message_item (output_item))
1953 return xr_render_message (xr, to_message_item (output_item));