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/chart-item-provider.h"
33 #include "output/charts/boxplot.h"
34 #include "output/charts/np-plot.h"
35 #include "output/charts/piechart.h"
36 #include "output/charts/barchart.h"
37 #include "output/charts/plot-hist.h"
38 #include "output/charts/roc-chart.h"
39 #include "output/charts/spreadlevel-plot.h"
40 #include "output/charts/scree.h"
41 #include "output/charts/scatterplot.h"
42 #include "output/driver-provider.h"
43 #include "output/group-item.h"
44 #include "output/message-item.h"
45 #include "output/options.h"
46 #include "output/page-setup-item.h"
47 #include "output/render.h"
48 #include "output/table-item.h"
49 #include "output/table.h"
50 #include "output/text-item.h"
52 #include <cairo/cairo-pdf.h>
53 #include <cairo/cairo-ps.h>
54 #include <cairo/cairo-svg.h>
55 #include <cairo/cairo.h>
58 #include <pango/pango-font.h>
59 #include <pango/pango-layout.h>
60 #include <pango/pango.h>
61 #include <pango/pangocairo.h>
64 #include "gl/c-ctype.h"
65 #include "gl/c-strcase.h"
66 #include "gl/intprops.h"
67 #include "gl/minmax.h"
68 #include "gl/xalloc.h"
71 #define _(msgid) gettext (msgid)
73 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
77 /* The unit used for internal measurements is inch/(72 * XR_POINT).
78 (Thus, XR_POINT units represent one point.) */
79 #define XR_POINT PANGO_SCALE
81 /* Conversions to and from points. */
85 return x / (double) XR_POINT;
88 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
92 return x * (PANGO_SCALE * 72 / 96);
95 /* Dimensions for drawing lines in tables. */
96 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
97 #define XR_LINE_SPACE XR_POINT /* Space between double lines. */
110 XR_FONT_PROPORTIONAL,
116 /* A font for use with Cairo. */
119 PangoFontDescription *desc;
123 /* An output item whose rendering is in progress. */
126 /* Renders as much of itself as it can on the current page. Returns true
127 if rendering is complete, false if the output item needs another
129 bool (*render) (struct xr_render_fsm *, struct xr_driver *);
131 /* Destroys the output item. */
132 void (*destroy) (struct xr_render_fsm *);
135 /* Cairo output driver. */
138 struct output_driver driver;
140 /* User parameters. */
141 struct xr_font fonts[XR_N_FONTS];
143 int width; /* Page width minus margins. */
144 int length; /* Page length minus margins and header. */
146 int left_margin; /* Left margin in inch/(72 * XR_POINT). */
147 int right_margin; /* Right margin in inch/(72 * XR_POINT). */
148 int top_margin; /* Top margin in inch/(72 * XR_POINT). */
149 int bottom_margin; /* Bottom margin in inch/(72 * XR_POINT). */
151 int line_space; /* Space between lines. */
152 int line_width; /* Width of lines. */
154 int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
155 int object_spacing; /* Space between output objects. */
157 struct cell_color bg; /* Background color */
158 struct cell_color fg; /* Foreground color */
159 bool transparent; /* true -> do not render background */
160 bool systemcolors; /* true -> do not change colors */
162 int initial_page_number;
164 struct page_heading headings[2]; /* Top and bottom headings. */
165 int headings_height[2];
167 /* Internal state. */
168 struct render_params *params;
170 int char_width, char_height;
172 cairo_surface_t *surface;
173 int page_number; /* Current page number. */
175 struct xr_render_fsm *fsm;
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], int valign_offset,
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 static PangoFontDescription *
217 parse_font (const char *font, int default_size, bool bold, bool italic)
219 if (!c_strcasecmp (font, "Monospaced"))
222 PangoFontDescription *desc = pango_font_description_from_string (font);
226 /* If the font description didn't include an explicit font size, then set it
227 to DEFAULT_SIZE, which is in inch/72000 units. */
228 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
229 pango_font_description_set_size (desc,
230 (default_size / 1000.0) * PANGO_SCALE);
232 pango_font_description_set_weight (desc, (bold
234 : PANGO_WEIGHT_NORMAL));
235 pango_font_description_set_style (desc, (italic
237 : PANGO_STYLE_NORMAL));
242 static PangoFontDescription *
243 parse_font_option (struct output_driver *d, struct string_map *options,
244 const char *key, const char *default_value,
245 int default_size, bool bold, bool italic)
247 char *string = parse_string (opt (d, options, key, default_value));
248 PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
251 msg (MW, _("`%s': bad font specification"), string);
253 /* Fall back to DEFAULT_VALUE, which had better be a valid font
255 desc = parse_font (default_value, default_size, bold, italic);
256 assert (desc != NULL);
264 apply_options (struct xr_driver *xr, struct string_map *o)
266 struct output_driver *d = &xr->driver;
268 /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
269 int left_margin, right_margin;
270 int top_margin, bottom_margin;
271 int paper_width, paper_length;
273 int min_break[TABLE_N_AXES];
275 /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
276 const double scale = XR_POINT / 1000.;
280 for (i = 0; i < XR_N_FONTS; i++)
282 struct xr_font *font = &xr->fonts[i];
284 if (font->desc != NULL)
285 pango_font_description_free (font->desc);
288 font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
289 xr->fonts[XR_FONT_FIXED].desc = parse_font_option
290 (d, o, "fixed-font", "monospace", font_size, false, false);
291 xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font_option (
292 d, o, "prop-font", "sans serif", font_size, false, false);
293 xr->fonts[XR_FONT_EMPHASIS].desc = parse_font_option (
294 d, o, "emph-font", "sans serif", font_size, false, true);
296 xr->fg = parse_color (opt (d, o, "foreground-color", "#000000000000"));
297 xr->bg = parse_color (opt (d, o, "background-color", "#FFFFFFFFFFFF"));
299 xr->transparent = parse_boolean (opt (d, o, "transparent", "false"));
300 xr->systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
302 /* Get dimensions. */
303 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
304 left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
305 right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
306 top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
307 bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
309 min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
310 min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
312 int object_spacing = (parse_dimension (opt (d, o, "object-spacing", NULL))
315 /* Convert to inch/(XR_POINT * 72). */
316 xr->left_margin = left_margin * scale;
317 xr->right_margin = right_margin * scale;
318 xr->top_margin = top_margin * scale;
319 xr->bottom_margin = bottom_margin * scale;
320 xr->width = (paper_width - left_margin - right_margin) * scale;
321 xr->length = (paper_length - top_margin - bottom_margin) * scale;
322 xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
323 xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
324 xr->object_spacing = object_spacing >= 0 ? object_spacing : XR_POINT * 12;
326 /* There are no headings so headings_height can stay 0. */
329 static struct xr_driver *
330 xr_allocate (const char *name, int device_type, struct string_map *o,
333 struct xr_driver *xr = xzalloc (sizeof *xr);
334 struct output_driver *d = &xr->driver;
336 output_driver_init (d, &cairo_driver_class, name, device_type);
338 /* This is a nasty kluge for an issue that does not make sense. On any
339 surface other than a screen (e.g. for output to PDF or PS or SVG), the
340 fonts are way too big by default. A "9-point" font seems to appear about
341 16 points tall. We use a scale factor for these surfaces to help, but the
342 underlying issue is a mystery. */
343 xr->font_scale = font_scale;
345 apply_options (xr, o);
351 pango_to_xr (int pango)
353 return (XR_POINT != PANGO_SCALE
354 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
361 return (XR_POINT != PANGO_SCALE
362 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
367 xr_measure_fonts (cairo_t *cairo, const struct xr_font fonts[XR_N_FONTS],
368 int *char_width, int *char_height)
372 for (int i = 0; i < XR_N_FONTS; i++)
374 PangoLayout *layout = pango_cairo_create_layout (cairo);
375 pango_layout_set_font_description (layout, fonts[i].desc);
377 pango_layout_set_text (layout, "0", 1);
380 pango_layout_get_size (layout, &cw, &ch);
381 *char_width = MAX (*char_width, pango_to_xr (cw));
382 *char_height = MAX (*char_height, pango_to_xr (ch));
384 g_object_unref (G_OBJECT (layout));
389 get_layout_height (PangoLayout *layout)
392 pango_layout_get_size (layout, &w, &h);
397 xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
398 const struct page_heading *ph, int page_number,
399 int width, bool draw, int base_y)
401 PangoLayout *layout = pango_cairo_create_layout (cairo);
402 pango_layout_set_font_description (layout, font);
405 for (size_t i = 0; i < ph->n; i++)
407 const struct page_paragraph *pp = &ph->paragraphs[i];
409 char *markup = output_driver_substitute_heading_vars (pp->markup,
411 pango_layout_set_markup (layout, markup, -1);
414 pango_layout_set_alignment (
416 (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
417 : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER
418 : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
419 : PANGO_ALIGN_RIGHT));
420 pango_layout_set_width (layout, xr_to_pango (width));
424 cairo_translate (cairo, 0, xr_to_pt (y + base_y));
425 pango_cairo_show_layout (cairo, layout);
426 cairo_restore (cairo);
429 y += pango_to_xr (get_layout_height (layout));
432 g_object_unref (G_OBJECT (layout));
438 xr_measure_headings (cairo_surface_t *surface,
439 const PangoFontDescription *font,
440 const struct page_heading headings[2],
441 int width, int object_spacing, int height[2])
443 cairo_t *cairo = cairo_create (surface);
445 for (int i = 0; i < 2; i++)
447 int h = xr_render_page_heading (cairo, font, &headings[i], -1,
450 /* If the top heading is nonempty, add some space below it. */
458 cairo_destroy (cairo);
463 xr_check_fonts (cairo_surface_t *surface,
464 const struct xr_font fonts[XR_N_FONTS],
465 int usable_width, int usable_length)
467 cairo_t *cairo = cairo_create (surface);
468 int char_width, char_height;
469 xr_measure_fonts (cairo, fonts, &char_width, &char_height);
470 cairo_destroy (cairo);
473 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
474 if (usable_width / char_width < MIN_WIDTH)
476 msg (ME, _("The defined page is not wide enough to hold at least %d "
477 "characters in the default font. In fact, there's only "
478 "room for %d characters."),
479 MIN_WIDTH, usable_width / char_width);
482 if (usable_length / char_height < MIN_LENGTH)
484 msg (ME, _("The defined page is not long enough to hold at least %d "
485 "lines in the default font. In fact, there's only "
486 "room for %d lines."),
487 MIN_LENGTH, usable_length / char_height);
494 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
498 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
500 xr_measure_fonts (xr->cairo, xr->fonts, &xr->char_width, &xr->char_height);
502 for (int i = 0; i < XR_N_FONTS; i++)
504 struct xr_font *font = &xr->fonts[i];
505 font->layout = pango_cairo_create_layout (cairo);
506 pango_layout_set_font_description (font->layout, font->desc);
509 if (xr->params == NULL)
511 xr->params = xmalloc (sizeof *xr->params);
512 xr->params->draw_line = xr_draw_line;
513 xr->params->measure_cell_width = xr_measure_cell_width;
514 xr->params->measure_cell_height = xr_measure_cell_height;
515 xr->params->adjust_break = xr_adjust_break;
516 xr->params->draw_cell = xr_draw_cell;
517 xr->params->aux = xr;
518 xr->params->size[H] = xr->width;
519 xr->params->size[V] = xr->length;
520 xr->params->font_size[H] = xr->char_width;
521 xr->params->font_size[V] = xr->char_height;
523 int lw = XR_LINE_WIDTH;
524 int ls = XR_LINE_SPACE;
525 for (int i = 0; i < TABLE_N_AXES; i++)
527 xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
528 xr->params->line_widths[i][RENDER_LINE_SINGLE] = lw;
529 xr->params->line_widths[i][RENDER_LINE_DASHED] = lw;
530 xr->params->line_widths[i][RENDER_LINE_THICK] = lw * 2;
531 xr->params->line_widths[i][RENDER_LINE_THIN] = lw / 2;
532 xr->params->line_widths[i][RENDER_LINE_DOUBLE] = 2 * lw + ls;
535 for (int i = 0; i < TABLE_N_AXES; i++)
536 xr->params->min_break[i] = xr->min_break[i];
537 xr->params->supports_margins = true;
538 xr->params->rtl = render_direction_rtl ();
541 if (!xr->systemcolors)
542 cairo_set_source_rgb (xr->cairo,
543 xr->fg.r / 255.0, xr->fg.g / 255.0, xr->fg.b / 255.0);
546 static struct output_driver *
547 xr_create (struct file_handle *fh, enum settings_output_devices device_type,
548 struct string_map *o, enum xr_output_type file_type)
550 const char *file_name = fh_get_file_name (fh);
551 struct xr_driver *xr = xr_allocate (file_name, device_type, o, 72.0 / 128.0);
552 double width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
553 double length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
554 if (file_type == XR_PDF)
555 xr->surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
556 else if (file_type == XR_PS)
557 xr->surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
558 else if (file_type == XR_SVG)
559 xr->surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
563 cairo_status_t status = cairo_surface_status (xr->surface);
564 if (status != CAIRO_STATUS_SUCCESS)
566 msg (ME, _("error opening output file `%s': %s"),
567 file_name, cairo_status_to_string (status));
571 if (!xr_check_fonts (xr->surface, xr->fonts, xr->width, xr->length))
579 output_driver_destroy (&xr->driver);
583 static struct output_driver *
584 xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
585 struct string_map *o)
587 return xr_create (fh, device_type, o, XR_PDF);
590 static struct output_driver *
591 xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
592 struct string_map *o)
594 return xr_create (fh, device_type, o, XR_PS);
597 static struct output_driver *
598 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
599 struct string_map *o)
601 return xr_create (fh, device_type, o, XR_SVG);
605 xr_destroy (struct output_driver *driver)
607 struct xr_driver *xr = xr_driver_cast (driver);
610 xr_driver_destroy_fsm (xr);
612 if (xr->cairo != NULL)
614 cairo_surface_finish (xr->surface);
615 cairo_status_t status = cairo_status (xr->cairo);
616 if (status != CAIRO_STATUS_SUCCESS)
617 fprintf (stderr, _("error drawing output for %s driver: %s"),
618 output_driver_get_name (driver),
619 cairo_status_to_string (status));
620 cairo_surface_destroy (xr->surface);
622 cairo_destroy (xr->cairo);
625 for (i = 0; i < XR_N_FONTS; i++)
627 struct xr_font *font = &xr->fonts[i];
629 if (font->desc != NULL)
630 pango_font_description_free (font->desc);
631 if (font->layout != NULL)
632 g_object_unref (font->layout);
640 xr_flush (struct output_driver *driver)
642 struct xr_driver *xr = xr_driver_cast (driver);
644 cairo_surface_flush (cairo_get_target (xr->cairo));
648 xr_update_page_setup (struct output_driver *driver,
649 const struct page_setup *ps)
651 struct xr_driver *xr = xr_driver_cast (driver);
653 xr->initial_page_number = ps->initial_page_number;
654 xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
659 int usable[TABLE_N_AXES];
660 for (int i = 0; i < 2; i++)
661 usable[i] = (ps->paper[i]
662 - (ps->margins[i][0] + ps->margins[i][1])) * 72 * XR_POINT;
664 int headings_height[2];
665 usable[V] -= xr_measure_headings (
666 xr->surface, xr->fonts[XR_FONT_PROPORTIONAL].desc, ps->headings,
667 usable[H], xr->object_spacing, headings_height);
669 enum table_axis h = ps->orientation == PAGE_LANDSCAPE;
670 enum table_axis v = !h;
671 if (!xr_check_fonts (xr->surface, xr->fonts, usable[h], usable[v]))
674 for (int i = 0; i < 2; i++)
676 page_heading_uninit (&xr->headings[i]);
677 page_heading_copy (&xr->headings[i], &ps->headings[i]);
678 xr->headings_height[i] = headings_height[i];
680 xr->width = usable[h];
681 xr->length = usable[v];
682 xr->left_margin = ps->margins[h][0] * 72 * XR_POINT;
683 xr->right_margin = ps->margins[h][1] * 72 * XR_POINT;
684 xr->top_margin = ps->margins[v][0] * 72 * XR_POINT;
685 xr->bottom_margin = ps->margins[v][1] * 72 * XR_POINT;
686 cairo_pdf_surface_set_size (xr->surface,
687 ps->paper[h] * 72.0, ps->paper[v] * 72.0);
691 xr_submit (struct output_driver *driver, const struct output_item *output_item)
693 struct xr_driver *xr = xr_driver_cast (driver);
695 if (is_page_setup_item (output_item))
697 xr_update_page_setup (driver,
698 to_page_setup_item (output_item)->page_setup);
704 xr->page_number = xr->initial_page_number - 1;
705 xr_set_cairo (xr, cairo_create (xr->surface));
706 cairo_save (xr->cairo);
707 xr_driver_next_page (xr, xr->cairo);
710 xr_driver_output_item (xr, output_item);
711 while (xr_driver_need_new_page (xr))
713 cairo_restore (xr->cairo);
714 cairo_show_page (xr->cairo);
715 cairo_save (xr->cairo);
716 xr_driver_next_page (xr, xr->cairo);
720 /* Functions for rendering a series of output items to a series of Cairo
721 contexts, with pagination.
723 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
724 its underlying implementation.
726 See the big comment in cairo.h for intended usage. */
728 /* Gives new page CAIRO to XR for output. */
730 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
732 if (!xr->transparent)
735 cairo_set_source_rgb (cairo,
736 xr->bg.r / 255.0, xr->bg.g / 255.0, xr->bg.b / 255.0);
737 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
739 cairo_restore (cairo);
741 cairo_translate (cairo,
742 xr_to_pt (xr->left_margin),
743 xr_to_pt (xr->top_margin + xr->headings_height[0]));
749 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
750 &xr->headings[0], xr->page_number, xr->width, true,
751 -xr->headings_height[0]);
752 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
753 &xr->headings[1], xr->page_number, xr->width, true,
756 xr_driver_run_fsm (xr);
759 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
760 rendering a previous output item, that is, only if xr_driver_need_new_page()
763 xr_driver_output_item (struct xr_driver *xr,
764 const struct output_item *output_item)
766 assert (xr->fsm == NULL);
767 xr->fsm = xr_render_output_item (xr, output_item);
768 xr_driver_run_fsm (xr);
771 /* Returns true if XR is in the middle of rendering an output item and needs a
772 new page to be appended using xr_driver_next_page() to make progress,
775 xr_driver_need_new_page (const struct xr_driver *xr)
777 return xr->fsm != NULL;
780 /* Returns true if the current page doesn't have any content yet. */
782 xr_driver_is_page_blank (const struct xr_driver *xr)
788 xr_driver_destroy_fsm (struct xr_driver *xr)
792 xr->fsm->destroy (xr->fsm);
798 xr_driver_run_fsm (struct xr_driver *xr)
800 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
801 xr_driver_destroy_fsm (xr);
805 xr_layout_cell (struct xr_driver *, const struct table_cell *,
806 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
807 int *width, int *height, int *brk);
810 set_source_rgba (cairo_t *cairo, const struct cell_color *color)
812 cairo_set_source_rgba (cairo,
813 color->r / 255., color->g / 255., color->b / 255.,
814 color->alpha / 255.);
818 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
819 const struct cell_color *color)
821 cairo_new_path (xr->cairo);
822 if (!xr->systemcolors)
823 set_source_rgba (xr->cairo, color);
824 cairo_set_line_width (
826 xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
827 : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
829 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
830 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
831 cairo_stroke (xr->cairo);
835 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
837 cairo_new_path (xr->cairo);
838 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
839 cairo_close_path (xr->cairo);
840 cairo_stroke (xr->cairo);
841 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
842 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y0 + xr->y));
843 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
844 cairo_line_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y1 + xr->y));
848 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
850 cairo_new_path (xr->cairo);
851 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
852 cairo_rectangle (xr->cairo,
853 xr_to_pt (x0), xr_to_pt (y0 + xr->y),
854 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
855 cairo_fill (xr->cairo);
858 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
859 shortening it to X0...X1 if SHORTEN is true.
860 Draws a horizontal line X1...X3 at Y if RIGHT says so,
861 shortening it to X2...X3 if SHORTEN is true. */
863 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
864 enum render_line_style left, enum render_line_style right,
865 const struct cell_color *left_color,
866 const struct cell_color *right_color,
869 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
870 && cell_color_equal (left_color, right_color))
871 dump_line (xr, x0, y, x3, y, left, left_color);
874 if (left != RENDER_LINE_NONE)
875 dump_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
876 if (right != RENDER_LINE_NONE)
877 dump_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
881 /* Draws a vertical line Y0...Y2 at X if TOP says so,
882 shortening it to Y0...Y1 if SHORTEN is true.
883 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
884 shortening it to Y2...Y3 if SHORTEN is true. */
886 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
887 enum render_line_style top, enum render_line_style bottom,
888 const struct cell_color *top_color,
889 const struct cell_color *bottom_color,
892 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
893 && cell_color_equal (top_color, bottom_color))
894 dump_line (xr, x, y0, x, y3, top, top_color);
897 if (top != RENDER_LINE_NONE)
898 dump_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
899 if (bottom != RENDER_LINE_NONE)
900 dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
905 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
906 enum render_line_style styles[TABLE_N_AXES][2],
907 struct cell_color colors[TABLE_N_AXES][2])
909 const int x0 = bb[H][0];
910 const int y0 = bb[V][0];
911 const int x3 = bb[H][1];
912 const int y3 = bb[V][1];
913 const int top = styles[H][0];
914 const int bottom = styles[H][1];
916 int start_side = render_direction_rtl();
917 int end_side = !start_side;
918 const int start_of_line = styles[V][start_side];
919 const int end_of_line = styles[V][end_side];
920 const struct cell_color *top_color = &colors[H][0];
921 const struct cell_color *bottom_color = &colors[H][1];
922 const struct cell_color *start_color = &colors[V][start_side];
923 const struct cell_color *end_color = &colors[V][end_side];
925 /* The algorithm here is somewhat subtle, to allow it to handle
926 all the kinds of intersections that we need.
928 Three additional ordinates are assigned along the x axis. The
929 first is xc, midway between x0 and x3. The others are x1 and
930 x2; for a single vertical line these are equal to xc, and for
931 a double vertical line they are the ordinates of the left and
932 right half of the double line.
934 yc, y1, and y2 are assigned similarly along the y axis.
936 The following diagram shows the coordinate system and output
937 for double top and bottom lines, single left line, and no
941 y0 ________________________
947 y1 = y2 = yc |######### # |
952 y3 |________#_____#_______|
954 struct xr_driver *xr = xr_;
956 /* Offset from center of each line in a pair of double lines. */
957 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
959 /* Are the lines along each axis single or double?
960 (It doesn't make sense to have different kinds of line on the
961 same axis, so we don't try to gracefully handle that case.) */
962 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
963 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
965 /* When horizontal lines are doubled,
966 the left-side line along y1 normally runs from x0 to x2,
967 and the right-side line along y1 from x3 to x1.
968 If the top-side line is also doubled, we shorten the y1 lines,
969 so that the left-side line runs only to x1,
970 and the right-side line only to x2.
971 Otherwise, the horizontal line at y = y1 below would cut off
972 the intersection, which looks ugly:
974 y0 ________________________
979 y1 |######### ########|
982 y2 |######################|
985 y3 |______________________|
986 It is more of a judgment call when the horizontal line is
987 single. We actually choose to cut off the line anyhow, as
988 shown in the first diagram above.
990 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
991 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
992 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
993 int horz_line_ofs = double_vert ? double_line_ofs : 0;
994 int xc = (x0 + x3) / 2;
995 int x1 = xc - horz_line_ofs;
996 int x2 = xc + horz_line_ofs;
998 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
999 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
1000 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
1001 int vert_line_ofs = double_horz ? double_line_ofs : 0;
1002 int yc = (y0 + y3) / 2;
1003 int y1 = yc - vert_line_ofs;
1004 int y2 = yc + vert_line_ofs;
1007 horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
1008 start_color, end_color, shorten_yc_line);
1011 horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
1012 start_color, end_color, shorten_y1_lines);
1013 horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
1014 start_color, end_color, shorten_y2_lines);
1018 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
1022 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
1024 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
1030 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
1031 int *min_width, int *max_width)
1033 struct xr_driver *xr = xr_;
1034 int bb[TABLE_N_AXES][2];
1035 int clip[TABLE_N_AXES][2];
1042 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1043 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
1046 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
1049 *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
1050 + cell->style->cell_style.margin[H][1]);
1052 *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
1053 + cell->style->cell_style.margin[H][1]);
1057 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
1059 struct xr_driver *xr = xr_;
1060 int bb[TABLE_N_AXES][2];
1061 int clip[TABLE_N_AXES][2];
1065 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1066 + cell->style->cell_style.margin[H][1]);
1069 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1070 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
1071 h += px_to_xr (cell->style->cell_style.margin[V][0]
1072 + cell->style->cell_style.margin[V][1]);
1076 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
1079 xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
1080 int bb[TABLE_N_AXES][2], int valign_offset,
1081 int spill[TABLE_N_AXES][2],
1082 int clip[TABLE_N_AXES][2])
1084 struct xr_driver *xr = xr_;
1087 if (!xr->transparent)
1089 cairo_save (xr->cairo);
1090 int bg_clip[TABLE_N_AXES][2];
1091 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1093 bg_clip[axis][0] = clip[axis][0];
1094 if (bb[axis][0] == clip[axis][0])
1095 bg_clip[axis][0] -= spill[axis][0];
1097 bg_clip[axis][1] = clip[axis][1];
1098 if (bb[axis][1] == clip[axis][1])
1099 bg_clip[axis][1] += spill[axis][1];
1101 xr_clip (xr, bg_clip);
1102 set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]);
1104 bb[H][0] - spill[H][0],
1105 bb[V][0] - spill[V][0],
1106 bb[H][1] + spill[H][1],
1107 bb[V][1] + spill[V][1]);
1108 cairo_restore (xr->cairo);
1110 cairo_save (xr->cairo);
1111 if (!xr->systemcolors)
1112 set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]);
1114 bb[V][0] += valign_offset;
1116 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1118 bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
1119 bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
1121 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
1122 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1123 cairo_restore (xr->cairo);
1127 xr_adjust_break (void *xr_, const struct table_cell *cell,
1128 int width, int height)
1130 struct xr_driver *xr = xr_;
1131 int bb[TABLE_N_AXES][2];
1132 int clip[TABLE_N_AXES][2];
1135 if (xr_measure_cell_height (xr_, cell, width) < height)
1139 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1140 + cell->style->cell_style.margin[H][1]);
1144 bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
1145 + cell->style->cell_style.margin[V][1]);
1146 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1147 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1152 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
1154 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1156 double x0 = xr_to_pt (clip[H][0]);
1157 double y0 = xr_to_pt (clip[V][0] + xr->y);
1158 double x1 = xr_to_pt (clip[H][1]);
1159 double y1 = xr_to_pt (clip[V][1] + xr->y);
1161 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1162 cairo_clip (xr->cairo);
1167 add_attr (PangoAttrList *list, PangoAttribute *attr,
1168 guint start_index, guint end_index)
1170 attr->start_index = start_index;
1171 attr->end_index = end_index;
1172 pango_attr_list_insert (list, attr);
1176 markup_escape (struct string *out, unsigned int options,
1177 const char *in, size_t len)
1179 if (!(options & TAB_MARKUP))
1181 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
1193 ds_put_cstr (out, "&");
1196 ds_put_cstr (out, "<");
1199 ds_put_cstr (out, ">");
1202 ds_put_byte (out, c);
1209 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
1211 int size[TABLE_N_AXES];
1212 pango_layout_get_size (layout, &size[H], &size[V]);
1217 xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
1218 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1219 int *widthp, int *brk)
1221 const struct font_style *font_style = &cell->style->font_style;
1222 const struct cell_style *cell_style = &cell->style->cell_style;
1223 unsigned int options = cell->options;
1225 enum table_axis X = options & TAB_ROTATE ? V : H;
1226 enum table_axis Y = !X;
1227 int R = options & TAB_ROTATE ? 0 : 1;
1229 struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1230 : &xr->fonts[XR_FONT_PROPORTIONAL]);
1231 struct xr_font local_font;
1232 if (font_style->typeface)
1234 PangoFontDescription *desc = parse_font (
1235 font_style->typeface,
1236 font_style->size ? font_style->size * 1000 * xr->font_scale : 10000,
1237 font_style->bold, font_style->italic);
1240 PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1241 pango_layout_set_font_description (layout, desc);
1243 local_font.desc = desc;
1244 local_font.layout = layout;
1249 const char *text = cell->text;
1250 enum table_halign halign = table_halign_interpret (
1251 cell_style->halign, cell->options & TAB_NUMERIC);
1252 if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
1254 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
1256 const char *decimal = strrchr (text, cell_style->decimal_char);
1259 pango_layout_set_text (font->layout, decimal, strlen (decimal));
1260 pango_layout_set_width (font->layout, -1);
1261 margin_adjustment += get_layout_dimension (font->layout, H);
1264 if (margin_adjustment < 0)
1265 bb[H][1] += margin_adjustment;
1268 struct string tmp = DS_EMPTY_INITIALIZER;
1269 PangoAttrList *attrs = NULL;
1271 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
1272 Pango's implementation of it): it will break after a period or a comma
1273 that precedes a digit, e.g. in ".000" it will break after the period.
1274 This code looks for such a situation and inserts a U+2060 WORD JOINER
1275 to prevent the break.
1277 This isn't necessary when the decimal point is between two digits
1278 (e.g. "0.000" won't be broken) or when the display width is not limited so
1279 that word wrapping won't happen.
1281 It isn't necessary to look for more than one period or comma, as would
1282 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
1283 are present then there will always be a digit on both sides of every
1284 period and comma. */
1285 if (options & TAB_MARKUP)
1287 PangoAttrList *new_attrs;
1289 if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
1292 tmp.ss = ss_cstr (new_text);
1293 tmp.capacity = tmp.ss.length;
1297 /* XXX should we report the error? */
1298 ds_put_cstr (&tmp, text);
1301 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
1303 const char *decimal = text + strcspn (text, ".,");
1305 && c_isdigit (decimal[1])
1306 && (decimal == text || !c_isdigit (decimal[-1])))
1308 ds_extend (&tmp, strlen (text) + 16);
1309 markup_escape (&tmp, options, text, decimal - text + 1);
1310 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
1311 markup_escape (&tmp, options, decimal + 1, -1);
1315 if (font_style->underline)
1318 attrs = pango_attr_list_new ();
1319 pango_attr_list_insert (attrs, pango_attr_underline_new (
1320 PANGO_UNDERLINE_SINGLE));
1323 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
1325 /* If we haven't already put TEXT into tmp, do it now. */
1326 if (ds_is_empty (&tmp))
1328 ds_extend (&tmp, strlen (text) + 16);
1329 markup_escape (&tmp, options, text, -1);
1332 size_t subscript_ofs = ds_length (&tmp);
1333 for (size_t i = 0; i < cell->n_subscripts; i++)
1336 ds_put_byte (&tmp, ',');
1337 ds_put_cstr (&tmp, cell->subscripts[i]);
1340 size_t superscript_ofs = ds_length (&tmp);
1341 if (cell->superscript)
1342 ds_put_cstr (&tmp, cell->superscript);
1344 size_t footnote_ofs = ds_length (&tmp);
1345 for (size_t i = 0; i < cell->n_footnotes; i++)
1348 ds_put_byte (&tmp, ',');
1349 ds_put_cstr (&tmp, cell->footnotes[i]->marker);
1352 /* Allow footnote markers to occupy the right margin. That way, numbers
1353 in the column are still aligned. */
1354 if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
1356 /* Measure the width of the footnote marker, so we know how much we
1357 need to make room for. */
1358 pango_layout_set_text (font->layout, ds_cstr (&tmp) + footnote_ofs,
1359 ds_length (&tmp) - footnote_ofs);
1361 PangoAttrList *fn_attrs = pango_attr_list_new ();
1362 pango_attr_list_insert (
1363 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
1364 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
1365 pango_layout_set_attributes (font->layout, fn_attrs);
1366 pango_attr_list_unref (fn_attrs);
1367 int footnote_width = get_layout_dimension (font->layout, X);
1369 /* Bound the adjustment by the width of the right margin. */
1370 int right_margin = px_to_xr (cell_style->margin[X][R]);
1371 int footnote_adjustment = MIN (footnote_width, right_margin);
1373 /* Adjust the bounding box. */
1374 if (options & TAB_ROTATE)
1375 footnote_adjustment = -footnote_adjustment;
1376 bb[X][R] += footnote_adjustment;
1379 pango_layout_set_attributes (font->layout, NULL);
1382 /* Set attributes. */
1384 attrs = pango_attr_list_new ();
1385 add_attr (attrs, pango_attr_font_desc_new (font->desc), subscript_ofs,
1386 PANGO_ATTR_INDEX_TO_TEXT_END);
1387 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
1388 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
1389 if (cell->n_subscripts)
1390 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
1391 superscript_ofs - subscript_ofs);
1392 if (cell->superscript || cell->n_footnotes)
1393 add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
1394 PANGO_ATTR_INDEX_TO_TEXT_END);
1397 /* Set the attributes, if any. */
1400 pango_layout_set_attributes (font->layout, attrs);
1401 pango_attr_list_unref (attrs);
1405 if (ds_is_empty (&tmp))
1406 pango_layout_set_text (font->layout, text, -1);
1408 pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
1411 pango_layout_set_alignment (font->layout,
1412 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
1413 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
1414 : PANGO_ALIGN_CENTER));
1415 pango_layout_set_width (
1417 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
1418 pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1420 if (clip[H][0] != clip[H][1])
1422 cairo_save (xr->cairo);
1423 if (!(options & TAB_ROTATE))
1425 if (options & TAB_ROTATE)
1427 cairo_translate (xr->cairo,
1428 xr_to_pt (bb[H][0]),
1429 xr_to_pt (bb[V][1] + xr->y));
1430 cairo_rotate (xr->cairo, -M_PI_2);
1433 cairo_translate (xr->cairo,
1434 xr_to_pt (bb[H][0]),
1435 xr_to_pt (bb[V][0] + xr->y));
1436 pango_cairo_show_layout (xr->cairo, font->layout);
1438 /* If enabled, this draws a blue rectangle around the extents of each
1439 line of text, which can be rather useful for debugging layout
1443 PangoLayoutIter *iter;
1444 iter = pango_layout_get_iter (font->layout);
1447 PangoRectangle extents;
1449 pango_layout_iter_get_line_extents (iter, &extents, NULL);
1450 cairo_save (xr->cairo);
1451 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1453 pango_to_xr (extents.x),
1454 pango_to_xr (extents.y) - xr->y,
1455 pango_to_xr (extents.x + extents.width),
1456 pango_to_xr (extents.y + extents.height) - xr->y);
1457 cairo_restore (xr->cairo);
1459 while (pango_layout_iter_next_line (iter));
1460 pango_layout_iter_free (iter);
1463 cairo_restore (xr->cairo);
1466 int size[TABLE_N_AXES];
1467 pango_layout_get_size (font->layout, &size[H], &size[V]);
1468 int w = pango_to_xr (size[X]);
1469 int h = pango_to_xr (size[Y]);
1472 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
1474 PangoLayoutIter *iter;
1477 /* Choose a breakpoint between lines instead of in the middle of one. */
1478 iter = pango_layout_get_iter (font->layout);
1481 PangoRectangle extents;
1485 pango_layout_iter_get_line_extents (iter, NULL, &extents);
1486 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1487 extents.x = pango_to_xr (extents.x);
1488 extents.y = pango_to_xr (y0);
1489 extents.width = pango_to_xr (extents.width);
1490 extents.height = pango_to_xr (y1 - y0);
1491 bottom = bb[V][0] + extents.y + extents.height;
1492 if (bottom < bb[V][1])
1494 if (brk && clip[H][0] != clip[H][1])
1502 while (pango_layout_iter_next_line (iter));
1503 pango_layout_iter_free (iter);
1505 /* If enabled, draws a green line across the chosen breakpoint, which can
1506 be useful for debugging issues with breaking. */
1510 dump_line (xr, -xr->left_margin, best,
1511 xr->width + xr->right_margin, best,
1513 &(struct cell_color) CELL_COLOR (0, 255, 0));
1517 pango_layout_set_attributes (font->layout, NULL);
1519 if (font == &local_font)
1521 g_object_unref (G_OBJECT (font->layout));
1522 pango_font_description_free (font->desc);
1529 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1530 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1531 int *width, int *height, int *brk)
1536 /* If enabled, draws a blue rectangle around the cell extents, which can be
1537 useful for debugging layout. */
1540 if (clip[H][0] != clip[H][1])
1542 cairo_save (xr->cairo);
1543 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1544 dump_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
1545 cairo_restore (xr->cairo);
1551 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
1554 struct output_driver_factory pdf_driver_factory =
1555 { "pdf", "pspp.pdf", xr_pdf_create };
1556 struct output_driver_factory ps_driver_factory =
1557 { "ps", "pspp.ps", xr_ps_create };
1558 struct output_driver_factory svg_driver_factory =
1559 { "svg", "pspp.svg", xr_svg_create };
1561 static const struct output_driver_class cairo_driver_class =
1569 /* GUI rendering helpers. */
1573 struct output_item *item;
1576 struct render_pager *p;
1577 struct xr_driver *xr;
1580 #define CHART_WIDTH 500
1581 #define CHART_HEIGHT 375
1586 xr_driver_create (cairo_t *cairo, struct string_map *options)
1588 struct xr_driver *xr = xr_allocate ("cairo", 0, options, 1.0);
1589 xr_set_cairo (xr, cairo);
1593 /* Destroy XR, which should have been created with xr_driver_create(). Any
1594 cairo_t added to XR is not destroyed, because it is owned by the client. */
1596 xr_driver_destroy (struct xr_driver *xr)
1601 output_driver_destroy (&xr->driver);
1605 static struct xr_rendering *
1606 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1608 struct table_item *table_item;
1609 struct xr_rendering *r;
1611 table_item = table_item_create (table_from_string (text), NULL, NULL);
1612 r = xr_rendering_create (xr, &table_item->output_item, cr);
1613 table_item_unref (table_item);
1618 struct xr_rendering *
1619 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1622 struct xr_rendering *r = NULL;
1624 if (is_text_item (item))
1625 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1627 else if (is_message_item (item))
1629 const struct message_item *message_item = to_message_item (item);
1630 char *s = msg_to_string (message_item_get_msg (message_item));
1631 r = xr_rendering_create_text (xr, s, cr);
1634 else if (is_table_item (item))
1636 r = xzalloc (sizeof *r);
1637 r->item = output_item_ref (item);
1639 xr_set_cairo (xr, cr);
1640 r->p = render_pager_create (xr->params, to_table_item (item));
1642 else if (is_chart_item (item))
1644 r = xzalloc (sizeof *r);
1645 r->item = output_item_ref (item);
1647 else if (is_group_open_item (item))
1648 r = xr_rendering_create_text (xr, to_group_open_item (item)->command_name,
1655 xr_rendering_destroy (struct xr_rendering *r)
1659 output_item_unref (r->item);
1660 render_pager_destroy (r->p);
1666 xr_rendering_measure (const struct xr_rendering *r, int *wp, int *hp)
1670 if (is_table_item (r->item))
1672 w = render_pager_get_size (r->p, H) / XR_POINT;
1673 h = render_pager_get_size (r->p, V) / XR_POINT;
1687 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1688 double x, double y, double width, double height);
1692 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1693 int x0, int y0, int x1, int y1)
1695 if (is_table_item (r->item))
1697 struct xr_driver *xr = r->xr;
1699 xr_set_cairo (xr, cr);
1701 render_pager_draw_region (r->p, x0 * XR_POINT, y0 * XR_POINT,
1702 (x1 - x0) * XR_POINT, (y1 - y0) * XR_POINT);
1705 xr_draw_chart (to_chart_item (r->item), cr,
1706 0, 0, CHART_WIDTH, CHART_HEIGHT);
1710 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1711 double x, double y, double width, double height)
1713 struct xrchart_geometry geom;
1716 cairo_translate (cr, x, y + height);
1717 cairo_scale (cr, 1.0, -1.0);
1718 xrchart_geometry_init (cr, &geom, width, height);
1719 if (is_boxplot (chart_item))
1720 xrchart_draw_boxplot (chart_item, cr, &geom);
1721 else if (is_histogram_chart (chart_item))
1722 xrchart_draw_histogram (chart_item, cr, &geom);
1723 else if (is_np_plot_chart (chart_item))
1724 xrchart_draw_np_plot (chart_item, cr, &geom);
1725 else if (is_piechart (chart_item))
1726 xrchart_draw_piechart (chart_item, cr, &geom);
1727 else if (is_barchart (chart_item))
1728 xrchart_draw_barchart (chart_item, cr, &geom);
1729 else if (is_roc_chart (chart_item))
1730 xrchart_draw_roc (chart_item, cr, &geom);
1731 else if (is_scree (chart_item))
1732 xrchart_draw_scree (chart_item, cr, &geom);
1733 else if (is_spreadlevel_plot_chart (chart_item))
1734 xrchart_draw_spreadlevel (chart_item, cr, &geom);
1735 else if (is_scatterplot_chart (chart_item))
1736 xrchart_draw_scatterplot (chart_item, cr, &geom);
1739 xrchart_geometry_free (cr, &geom);
1745 xr_draw_png_chart (const struct chart_item *item,
1746 const char *file_name_template, int number,
1747 const struct cell_color *fg,
1748 const struct cell_color *bg)
1750 const int width = 640;
1751 const int length = 480;
1753 cairo_surface_t *surface;
1754 cairo_status_t status;
1755 const char *number_pos;
1759 number_pos = strchr (file_name_template, '#');
1760 if (number_pos != NULL)
1761 file_name = xasprintf ("%.*s%d%s.png", (int) (number_pos - file_name_template),
1762 file_name_template, number, number_pos + 1);
1764 file_name = xasprintf ("%s.png", file_name_template);
1766 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1767 cr = cairo_create (surface);
1769 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1772 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1774 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1776 status = cairo_surface_write_to_png (surface, file_name);
1777 if (status != CAIRO_STATUS_SUCCESS)
1778 msg (ME, _("error writing output file `%s': %s"),
1779 file_name, cairo_status_to_string (status));
1782 cairo_surface_destroy (surface);
1789 xr_draw_eps_chart (const struct chart_item *item,
1790 const char *file_name_template, int number,
1791 const struct cell_color *fg,
1792 const struct cell_color *bg)
1794 const int width = 640;
1795 const int length = 480;
1797 cairo_surface_t *surface;
1798 const char *number_pos;
1802 number_pos = strchr (file_name_template, '#');
1803 if (number_pos != NULL)
1804 file_name = xasprintf ("%.*s%d%s.eps", (int) (number_pos - file_name_template),
1805 file_name_template, number, number_pos + 1);
1807 file_name = xasprintf ("%s.eps", file_name_template);
1809 surface = cairo_ps_surface_create (file_name, width, length);
1810 cairo_ps_surface_set_eps (surface, true);
1811 cr = cairo_create (surface);
1813 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1816 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1818 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1821 cairo_surface_destroy (surface);
1828 struct xr_table_state
1830 struct xr_render_fsm fsm;
1831 struct render_pager *p;
1835 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1837 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1839 while (render_pager_has_next (ts->p))
1843 used = render_pager_draw_next (ts->p, xr->length - xr->y);
1856 xr_table_destroy (struct xr_render_fsm *fsm)
1858 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1860 render_pager_destroy (ts->p);
1864 static struct xr_render_fsm *
1865 xr_render_table (struct xr_driver *xr, struct table_item *table_item)
1867 struct xr_table_state *ts;
1869 ts = xmalloc (sizeof *ts);
1870 ts->fsm.render = xr_table_render;
1871 ts->fsm.destroy = xr_table_destroy;
1874 xr->y += xr->char_height;
1876 ts->p = render_pager_create (xr->params, table_item);
1877 table_item_unref (table_item);
1882 struct xr_chart_state
1884 struct xr_render_fsm fsm;
1885 struct chart_item *chart_item;
1889 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1891 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1893 const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
1895 if (xr->y > xr->length - chart_height)
1898 if (xr->cairo != NULL)
1900 xr_draw_chart (cs->chart_item, xr->cairo,
1903 xr_to_pt (xr->width),
1904 xr_to_pt (chart_height));
1906 xr->y += chart_height;
1912 xr_chart_destroy (struct xr_render_fsm *fsm)
1914 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1916 chart_item_unref (cs->chart_item);
1920 static struct xr_render_fsm *
1921 xr_render_chart (const struct chart_item *chart_item)
1923 struct xr_chart_state *cs;
1925 cs = xmalloc (sizeof *cs);
1926 cs->fsm.render = xr_chart_render;
1927 cs->fsm.destroy = xr_chart_destroy;
1928 cs->chart_item = chart_item_ref (chart_item);
1934 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1940 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1942 /* Nothing to do. */
1945 static struct xr_render_fsm *
1946 xr_render_eject (void)
1948 static struct xr_render_fsm eject_renderer =
1954 return &eject_renderer;
1957 static struct xr_render_fsm *
1958 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1960 enum text_item_type type = text_item_get_type (text_item);
1964 case TEXT_ITEM_PAGE_TITLE:
1967 case TEXT_ITEM_EJECT_PAGE:
1969 return xr_render_eject ();
1973 return xr_render_table (
1974 xr, text_item_to_table_item (text_item_ref (text_item)));
1980 static struct xr_render_fsm *
1981 xr_render_message (struct xr_driver *xr,
1982 const struct message_item *message_item)
1984 char *s = msg_to_string (message_item_get_msg (message_item));
1985 struct text_item *item = text_item_create (TEXT_ITEM_LOG, s);
1987 return xr_render_table (xr, text_item_to_table_item (item));
1990 static struct xr_render_fsm *
1991 xr_render_output_item (struct xr_driver *xr,
1992 const struct output_item *output_item)
1994 if (is_table_item (output_item))
1995 return xr_render_table (xr, table_item_ref (to_table_item (output_item)));
1996 else if (is_chart_item (output_item))
1997 return xr_render_chart (to_chart_item (output_item));
1998 else if (is_text_item (output_item))
1999 return xr_render_text (xr, to_text_item (output_item));
2000 else if (is_message_item (output_item))
2001 return xr_render_message (xr, to_message_item (output_item));
2007 xr_draw_svg_file (struct xr_rendering *r,
2008 const char *filename)
2012 xr_rendering_measure (r, &width, &height);
2013 cairo_surface_t *surface = cairo_svg_surface_create (filename, width, height);
2016 g_error ("Could not create cairo svg surface with file %s", filename);
2019 cairo_t *cr = cairo_create (surface);
2020 xr_rendering_draw (r, cr, 0, 0, width, height);
2022 cairo_surface_destroy (surface);