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);
1619 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1621 if (is_table_item (xr->item))
1622 apply_options (xr->xr, o);
1625 struct xr_rendering *
1626 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1629 struct xr_rendering *r = NULL;
1631 if (is_text_item (item))
1632 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1634 else if (is_message_item (item))
1636 const struct message_item *message_item = to_message_item (item);
1637 char *s = msg_to_string (message_item_get_msg (message_item));
1638 r = xr_rendering_create_text (xr, s, cr);
1641 else if (is_table_item (item))
1643 r = xzalloc (sizeof *r);
1644 r->item = output_item_ref (item);
1646 xr_set_cairo (xr, cr);
1647 r->p = render_pager_create (xr->params, to_table_item (item));
1649 else if (is_chart_item (item))
1651 r = xzalloc (sizeof *r);
1652 r->item = output_item_ref (item);
1654 else if (is_group_open_item (item))
1655 r = xr_rendering_create_text (xr, to_group_open_item (item)->command_name,
1662 xr_rendering_destroy (struct xr_rendering *r)
1666 output_item_unref (r->item);
1667 render_pager_destroy (r->p);
1673 xr_rendering_measure (const struct xr_rendering *r, int *wp, int *hp)
1677 if (is_table_item (r->item))
1679 w = render_pager_get_size (r->p, H) / XR_POINT;
1680 h = render_pager_get_size (r->p, V) / XR_POINT;
1694 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1695 double x, double y, double width, double height);
1699 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1700 int x0, int y0, int x1, int y1)
1702 if (is_table_item (r->item))
1704 struct xr_driver *xr = r->xr;
1706 xr_set_cairo (xr, cr);
1708 render_pager_draw_region (r->p, x0 * XR_POINT, y0 * XR_POINT,
1709 (x1 - x0) * XR_POINT, (y1 - y0) * XR_POINT);
1712 xr_draw_chart (to_chart_item (r->item), cr,
1713 0, 0, CHART_WIDTH, CHART_HEIGHT);
1717 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1718 double x, double y, double width, double height)
1720 struct xrchart_geometry geom;
1723 cairo_translate (cr, x, y + height);
1724 cairo_scale (cr, 1.0, -1.0);
1725 xrchart_geometry_init (cr, &geom, width, height);
1726 if (is_boxplot (chart_item))
1727 xrchart_draw_boxplot (chart_item, cr, &geom);
1728 else if (is_histogram_chart (chart_item))
1729 xrchart_draw_histogram (chart_item, cr, &geom);
1730 else if (is_np_plot_chart (chart_item))
1731 xrchart_draw_np_plot (chart_item, cr, &geom);
1732 else if (is_piechart (chart_item))
1733 xrchart_draw_piechart (chart_item, cr, &geom);
1734 else if (is_barchart (chart_item))
1735 xrchart_draw_barchart (chart_item, cr, &geom);
1736 else if (is_roc_chart (chart_item))
1737 xrchart_draw_roc (chart_item, cr, &geom);
1738 else if (is_scree (chart_item))
1739 xrchart_draw_scree (chart_item, cr, &geom);
1740 else if (is_spreadlevel_plot_chart (chart_item))
1741 xrchart_draw_spreadlevel (chart_item, cr, &geom);
1742 else if (is_scatterplot_chart (chart_item))
1743 xrchart_draw_scatterplot (chart_item, cr, &geom);
1746 xrchart_geometry_free (cr, &geom);
1752 xr_draw_png_chart (const struct chart_item *item,
1753 const char *file_name_template, int number,
1754 const struct cell_color *fg,
1755 const struct cell_color *bg)
1757 const int width = 640;
1758 const int length = 480;
1760 cairo_surface_t *surface;
1761 cairo_status_t status;
1762 const char *number_pos;
1766 number_pos = strchr (file_name_template, '#');
1767 if (number_pos != NULL)
1768 file_name = xasprintf ("%.*s%d%s.png", (int) (number_pos - file_name_template),
1769 file_name_template, number, number_pos + 1);
1771 file_name = xasprintf ("%s.png", file_name_template);
1773 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1774 cr = cairo_create (surface);
1776 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1779 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1781 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1783 status = cairo_surface_write_to_png (surface, file_name);
1784 if (status != CAIRO_STATUS_SUCCESS)
1785 msg (ME, _("error writing output file `%s': %s"),
1786 file_name, cairo_status_to_string (status));
1789 cairo_surface_destroy (surface);
1796 xr_draw_eps_chart (const struct chart_item *item,
1797 const char *file_name_template, int number,
1798 const struct cell_color *fg,
1799 const struct cell_color *bg)
1801 const int width = 640;
1802 const int length = 480;
1804 cairo_surface_t *surface;
1805 const char *number_pos;
1809 number_pos = strchr (file_name_template, '#');
1810 if (number_pos != NULL)
1811 file_name = xasprintf ("%.*s%d%s.eps", (int) (number_pos - file_name_template),
1812 file_name_template, number, number_pos + 1);
1814 file_name = xasprintf ("%s.eps", file_name_template);
1816 surface = cairo_ps_surface_create (file_name, width, length);
1817 cairo_ps_surface_set_eps (surface, true);
1818 cr = cairo_create (surface);
1820 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1823 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1825 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1828 cairo_surface_destroy (surface);
1835 struct xr_table_state
1837 struct xr_render_fsm fsm;
1838 struct render_pager *p;
1842 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1844 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1846 while (render_pager_has_next (ts->p))
1850 used = render_pager_draw_next (ts->p, xr->length - xr->y);
1863 xr_table_destroy (struct xr_render_fsm *fsm)
1865 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1867 render_pager_destroy (ts->p);
1871 static struct xr_render_fsm *
1872 xr_render_table (struct xr_driver *xr, struct table_item *table_item)
1874 struct xr_table_state *ts;
1876 ts = xmalloc (sizeof *ts);
1877 ts->fsm.render = xr_table_render;
1878 ts->fsm.destroy = xr_table_destroy;
1881 xr->y += xr->char_height;
1883 ts->p = render_pager_create (xr->params, table_item);
1884 table_item_unref (table_item);
1889 struct xr_chart_state
1891 struct xr_render_fsm fsm;
1892 struct chart_item *chart_item;
1896 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1898 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1900 const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
1902 if (xr->y > xr->length - chart_height)
1905 if (xr->cairo != NULL)
1907 xr_draw_chart (cs->chart_item, xr->cairo,
1910 xr_to_pt (xr->width),
1911 xr_to_pt (chart_height));
1913 xr->y += chart_height;
1919 xr_chart_destroy (struct xr_render_fsm *fsm)
1921 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1923 chart_item_unref (cs->chart_item);
1927 static struct xr_render_fsm *
1928 xr_render_chart (const struct chart_item *chart_item)
1930 struct xr_chart_state *cs;
1932 cs = xmalloc (sizeof *cs);
1933 cs->fsm.render = xr_chart_render;
1934 cs->fsm.destroy = xr_chart_destroy;
1935 cs->chart_item = chart_item_ref (chart_item);
1941 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1947 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1949 /* Nothing to do. */
1952 static struct xr_render_fsm *
1953 xr_render_eject (void)
1955 static struct xr_render_fsm eject_renderer =
1961 return &eject_renderer;
1964 static struct xr_render_fsm *
1965 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1967 enum text_item_type type = text_item_get_type (text_item);
1971 case TEXT_ITEM_PAGE_TITLE:
1974 case TEXT_ITEM_EJECT_PAGE:
1976 return xr_render_eject ();
1980 return xr_render_table (
1981 xr, text_item_to_table_item (text_item_ref (text_item)));
1987 static struct xr_render_fsm *
1988 xr_render_message (struct xr_driver *xr,
1989 const struct message_item *message_item)
1991 char *s = msg_to_string (message_item_get_msg (message_item));
1992 struct text_item *item = text_item_create (TEXT_ITEM_LOG, s);
1994 return xr_render_table (xr, text_item_to_table_item (item));
1997 static struct xr_render_fsm *
1998 xr_render_output_item (struct xr_driver *xr,
1999 const struct output_item *output_item)
2001 if (is_table_item (output_item))
2002 return xr_render_table (xr, table_item_ref (to_table_item (output_item)));
2003 else if (is_chart_item (output_item))
2004 return xr_render_chart (to_chart_item (output_item));
2005 else if (is_text_item (output_item))
2006 return xr_render_text (xr, to_text_item (output_item));
2007 else if (is_message_item (output_item))
2008 return xr_render_message (xr, to_message_item (output_item));
2014 xr_draw_svg_file (struct xr_rendering *r,
2015 const char *filename)
2019 xr_rendering_measure (r, &width, &height);
2020 cairo_surface_t *surface = cairo_svg_surface_create (filename, width, height);
2023 g_error ("Could not create cairo svg surface with file %s", filename);
2026 cairo_t *cr = cairo_create (surface);
2027 xr_rendering_draw (r, cr, 0, 0, width, height);
2029 cairo_surface_destroy (surface);