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 min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
152 int object_spacing; /* Space between output objects. */
154 struct cell_color bg; /* Background color */
155 struct cell_color fg; /* Foreground color */
156 bool transparent; /* true -> do not render background */
157 bool systemcolors; /* true -> do not change colors */
159 int initial_page_number;
161 struct page_heading headings[2]; /* Top and bottom headings. */
162 int headings_height[2];
164 /* Internal state. */
165 struct render_params *params;
167 int char_width, char_height;
169 cairo_surface_t *surface;
170 int page_number; /* Current page number. */
172 struct xr_render_fsm *fsm;
175 static const struct output_driver_class cairo_driver_class;
177 static void xr_driver_destroy_fsm (struct xr_driver *);
178 static void xr_driver_run_fsm (struct xr_driver *);
180 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
181 enum render_line_style styles[TABLE_N_AXES][2],
182 struct cell_color colors[TABLE_N_AXES][2]);
183 static void xr_measure_cell_width (void *, const struct table_cell *,
185 static int xr_measure_cell_height (void *, const struct table_cell *,
187 static void xr_draw_cell (void *, const struct table_cell *, int color_idx,
188 int bb[TABLE_N_AXES][2], int valign_offset,
189 int spill[TABLE_N_AXES][2],
190 int clip[TABLE_N_AXES][2]);
191 static int xr_adjust_break (void *, const struct table_cell *,
192 int width, int height);
194 static struct xr_render_fsm *xr_render_output_item (
195 struct xr_driver *, const struct output_item *);
197 /* Output driver basics. */
199 static struct xr_driver *
200 xr_driver_cast (struct output_driver *driver)
202 assert (driver->class == &cairo_driver_class);
203 return UP_CAST (driver, struct xr_driver, driver);
206 static struct driver_option *
207 opt (struct output_driver *d, struct string_map *options, const char *key,
208 const char *default_value)
210 return driver_option_get (d, options, key, default_value);
213 static PangoFontDescription *
214 parse_font (const char *font, int default_size, bool bold, bool italic)
216 if (!c_strcasecmp (font, "Monospaced"))
219 PangoFontDescription *desc = pango_font_description_from_string (font);
223 /* If the font description didn't include an explicit font size, then set it
224 to DEFAULT_SIZE, which is in inch/72000 units. */
225 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
226 pango_font_description_set_size (desc,
227 (default_size / 1000.0) * PANGO_SCALE);
229 pango_font_description_set_weight (desc, (bold
231 : PANGO_WEIGHT_NORMAL));
232 pango_font_description_set_style (desc, (italic
234 : PANGO_STYLE_NORMAL));
239 static PangoFontDescription *
240 parse_font_option (struct output_driver *d, struct string_map *options,
241 const char *key, const char *default_value,
242 int default_size, bool bold, bool italic)
244 char *string = parse_string (opt (d, options, key, default_value));
245 PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
248 msg (MW, _("`%s': bad font specification"), string);
250 /* Fall back to DEFAULT_VALUE, which had better be a valid font
252 desc = parse_font (default_value, default_size, bold, italic);
253 assert (desc != NULL);
261 apply_options (struct xr_driver *xr, struct string_map *o)
263 struct output_driver *d = &xr->driver;
265 /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
266 int left_margin, right_margin;
267 int top_margin, bottom_margin;
268 int paper_width, paper_length;
270 int min_break[TABLE_N_AXES];
272 /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
273 const double scale = XR_POINT / 1000.;
277 for (i = 0; i < XR_N_FONTS; i++)
279 struct xr_font *font = &xr->fonts[i];
281 if (font->desc != NULL)
282 pango_font_description_free (font->desc);
285 font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
286 xr->fonts[XR_FONT_FIXED].desc = parse_font_option
287 (d, o, "fixed-font", "monospace", font_size, false, false);
288 xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font_option (
289 d, o, "prop-font", "sans serif", font_size, false, false);
290 xr->fonts[XR_FONT_EMPHASIS].desc = parse_font_option (
291 d, o, "emph-font", "sans serif", font_size, false, true);
293 xr->fg = parse_color (opt (d, o, "foreground-color", "#000000000000"));
294 xr->bg = parse_color (opt (d, o, "background-color", "#FFFFFFFFFFFF"));
296 xr->transparent = parse_boolean (opt (d, o, "transparent", "false"));
297 xr->systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
299 /* Get dimensions. */
300 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
301 left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
302 right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
303 top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
304 bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
306 min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
307 min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
309 int object_spacing = (parse_dimension (opt (d, o, "object-spacing", NULL))
312 /* Convert to inch/(XR_POINT * 72). */
313 xr->left_margin = left_margin * scale;
314 xr->right_margin = right_margin * scale;
315 xr->top_margin = top_margin * scale;
316 xr->bottom_margin = bottom_margin * scale;
317 xr->width = (paper_width - left_margin - right_margin) * scale;
318 xr->length = (paper_length - top_margin - bottom_margin) * scale;
319 xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
320 xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
321 xr->object_spacing = object_spacing >= 0 ? object_spacing : XR_POINT * 12;
323 /* There are no headings so headings_height can stay 0. */
326 static struct xr_driver *
327 xr_allocate (const char *name, int device_type, struct string_map *o,
330 struct xr_driver *xr = xzalloc (sizeof *xr);
331 struct output_driver *d = &xr->driver;
333 output_driver_init (d, &cairo_driver_class, name, device_type);
335 /* This is a nasty kluge for an issue that does not make sense. On any
336 surface other than a screen (e.g. for output to PDF or PS or SVG), the
337 fonts are way too big by default. A "9-point" font seems to appear about
338 16 points tall. We use a scale factor for these surfaces to help, but the
339 underlying issue is a mystery. */
340 xr->font_scale = font_scale;
342 apply_options (xr, o);
348 pango_to_xr (int pango)
350 return (XR_POINT != PANGO_SCALE
351 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
358 return (XR_POINT != PANGO_SCALE
359 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
364 xr_measure_fonts (cairo_t *cairo, const struct xr_font fonts[XR_N_FONTS],
365 int *char_width, int *char_height)
369 for (int i = 0; i < XR_N_FONTS; i++)
371 PangoLayout *layout = pango_cairo_create_layout (cairo);
372 pango_layout_set_font_description (layout, fonts[i].desc);
374 pango_layout_set_text (layout, "0", 1);
377 pango_layout_get_size (layout, &cw, &ch);
378 *char_width = MAX (*char_width, pango_to_xr (cw));
379 *char_height = MAX (*char_height, pango_to_xr (ch));
381 g_object_unref (G_OBJECT (layout));
386 get_layout_height (PangoLayout *layout)
389 pango_layout_get_size (layout, &w, &h);
394 xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
395 const struct page_heading *ph, int page_number,
396 int width, bool draw, int base_y)
398 PangoLayout *layout = pango_cairo_create_layout (cairo);
399 pango_layout_set_font_description (layout, font);
402 for (size_t i = 0; i < ph->n; i++)
404 const struct page_paragraph *pp = &ph->paragraphs[i];
406 char *markup = output_driver_substitute_heading_vars (pp->markup,
408 pango_layout_set_markup (layout, markup, -1);
411 pango_layout_set_alignment (
413 (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
414 : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER
415 : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
416 : PANGO_ALIGN_RIGHT));
417 pango_layout_set_width (layout, xr_to_pango (width));
421 cairo_translate (cairo, 0, xr_to_pt (y + base_y));
422 pango_cairo_show_layout (cairo, layout);
423 cairo_restore (cairo);
426 y += pango_to_xr (get_layout_height (layout));
429 g_object_unref (G_OBJECT (layout));
435 xr_measure_headings (cairo_surface_t *surface,
436 const PangoFontDescription *font,
437 const struct page_heading headings[2],
438 int width, int object_spacing, int height[2])
440 cairo_t *cairo = cairo_create (surface);
442 for (int i = 0; i < 2; i++)
444 int h = xr_render_page_heading (cairo, font, &headings[i], -1,
447 /* If the top heading is nonempty, add some space below it. */
455 cairo_destroy (cairo);
460 xr_check_fonts (cairo_surface_t *surface,
461 const struct xr_font fonts[XR_N_FONTS],
462 int usable_width, int usable_length)
464 cairo_t *cairo = cairo_create (surface);
465 int char_width, char_height;
466 xr_measure_fonts (cairo, fonts, &char_width, &char_height);
467 cairo_destroy (cairo);
470 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
471 if (usable_width / char_width < MIN_WIDTH)
473 msg (ME, _("The defined page is not wide enough to hold at least %d "
474 "characters in the default font. In fact, there's only "
475 "room for %d characters."),
476 MIN_WIDTH, usable_width / char_width);
479 if (usable_length / char_height < MIN_LENGTH)
481 msg (ME, _("The defined page is not long enough to hold at least %d "
482 "lines in the default font. In fact, there's only "
483 "room for %d lines."),
484 MIN_LENGTH, usable_length / char_height);
491 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
495 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
497 xr_measure_fonts (xr->cairo, xr->fonts, &xr->char_width, &xr->char_height);
499 for (int i = 0; i < XR_N_FONTS; i++)
501 struct xr_font *font = &xr->fonts[i];
502 font->layout = pango_cairo_create_layout (cairo);
503 pango_layout_set_font_description (font->layout, font->desc);
506 if (xr->params == NULL)
508 xr->params = xmalloc (sizeof *xr->params);
509 xr->params->draw_line = xr_draw_line;
510 xr->params->measure_cell_width = xr_measure_cell_width;
511 xr->params->measure_cell_height = xr_measure_cell_height;
512 xr->params->adjust_break = xr_adjust_break;
513 xr->params->draw_cell = xr_draw_cell;
514 xr->params->aux = xr;
515 xr->params->size[H] = xr->width;
516 xr->params->size[V] = xr->length;
517 xr->params->font_size[H] = xr->char_width;
518 xr->params->font_size[V] = xr->char_height;
520 int lw = XR_LINE_WIDTH;
521 int ls = XR_LINE_SPACE;
522 for (int i = 0; i < TABLE_N_AXES; i++)
524 xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
525 xr->params->line_widths[i][RENDER_LINE_SINGLE] = lw;
526 xr->params->line_widths[i][RENDER_LINE_DASHED] = lw;
527 xr->params->line_widths[i][RENDER_LINE_THICK] = lw * 2;
528 xr->params->line_widths[i][RENDER_LINE_THIN] = lw / 2;
529 xr->params->line_widths[i][RENDER_LINE_DOUBLE] = 2 * lw + ls;
532 for (int i = 0; i < TABLE_N_AXES; i++)
533 xr->params->min_break[i] = xr->min_break[i];
534 xr->params->supports_margins = true;
535 xr->params->rtl = render_direction_rtl ();
538 if (!xr->systemcolors)
539 cairo_set_source_rgb (xr->cairo,
540 xr->fg.r / 255.0, xr->fg.g / 255.0, xr->fg.b / 255.0);
543 static struct output_driver *
544 xr_create (struct file_handle *fh, enum settings_output_devices device_type,
545 struct string_map *o, enum xr_output_type file_type)
547 const char *file_name = fh_get_file_name (fh);
548 struct xr_driver *xr = xr_allocate (file_name, device_type, o, 72.0 / 128.0);
549 double width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
550 double length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
551 if (file_type == XR_PDF)
552 xr->surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
553 else if (file_type == XR_PS)
554 xr->surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
555 else if (file_type == XR_SVG)
556 xr->surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
560 cairo_status_t status = cairo_surface_status (xr->surface);
561 if (status != CAIRO_STATUS_SUCCESS)
563 msg (ME, _("error opening output file `%s': %s"),
564 file_name, cairo_status_to_string (status));
568 if (!xr_check_fonts (xr->surface, xr->fonts, xr->width, xr->length))
576 output_driver_destroy (&xr->driver);
580 static struct output_driver *
581 xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
582 struct string_map *o)
584 return xr_create (fh, device_type, o, XR_PDF);
587 static struct output_driver *
588 xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
589 struct string_map *o)
591 return xr_create (fh, device_type, o, XR_PS);
594 static struct output_driver *
595 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
596 struct string_map *o)
598 return xr_create (fh, device_type, o, XR_SVG);
602 xr_destroy (struct output_driver *driver)
604 struct xr_driver *xr = xr_driver_cast (driver);
607 xr_driver_destroy_fsm (xr);
609 if (xr->cairo != NULL)
611 cairo_surface_finish (xr->surface);
612 cairo_status_t status = cairo_status (xr->cairo);
613 if (status != CAIRO_STATUS_SUCCESS)
614 fprintf (stderr, _("error drawing output for %s driver: %s"),
615 output_driver_get_name (driver),
616 cairo_status_to_string (status));
617 cairo_surface_destroy (xr->surface);
619 cairo_destroy (xr->cairo);
622 for (i = 0; i < XR_N_FONTS; i++)
624 struct xr_font *font = &xr->fonts[i];
626 if (font->desc != NULL)
627 pango_font_description_free (font->desc);
628 if (font->layout != NULL)
629 g_object_unref (font->layout);
637 xr_flush (struct output_driver *driver)
639 struct xr_driver *xr = xr_driver_cast (driver);
641 cairo_surface_flush (cairo_get_target (xr->cairo));
645 xr_update_page_setup (struct output_driver *driver,
646 const struct page_setup *ps)
648 struct xr_driver *xr = xr_driver_cast (driver);
650 xr->initial_page_number = ps->initial_page_number;
651 xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
656 int usable[TABLE_N_AXES];
657 for (int i = 0; i < 2; i++)
658 usable[i] = (ps->paper[i]
659 - (ps->margins[i][0] + ps->margins[i][1])) * 72 * XR_POINT;
661 int headings_height[2];
662 usable[V] -= xr_measure_headings (
663 xr->surface, xr->fonts[XR_FONT_PROPORTIONAL].desc, ps->headings,
664 usable[H], xr->object_spacing, headings_height);
666 enum table_axis h = ps->orientation == PAGE_LANDSCAPE;
667 enum table_axis v = !h;
668 if (!xr_check_fonts (xr->surface, xr->fonts, usable[h], usable[v]))
671 for (int i = 0; i < 2; i++)
673 page_heading_uninit (&xr->headings[i]);
674 page_heading_copy (&xr->headings[i], &ps->headings[i]);
675 xr->headings_height[i] = headings_height[i];
677 xr->width = usable[h];
678 xr->length = usable[v];
679 xr->left_margin = ps->margins[h][0] * 72 * XR_POINT;
680 xr->right_margin = ps->margins[h][1] * 72 * XR_POINT;
681 xr->top_margin = ps->margins[v][0] * 72 * XR_POINT;
682 xr->bottom_margin = ps->margins[v][1] * 72 * XR_POINT;
683 cairo_pdf_surface_set_size (xr->surface,
684 ps->paper[h] * 72.0, ps->paper[v] * 72.0);
688 xr_submit (struct output_driver *driver, const struct output_item *output_item)
690 struct xr_driver *xr = xr_driver_cast (driver);
692 if (is_page_setup_item (output_item))
694 xr_update_page_setup (driver,
695 to_page_setup_item (output_item)->page_setup);
701 xr->page_number = xr->initial_page_number - 1;
702 xr_set_cairo (xr, cairo_create (xr->surface));
703 cairo_save (xr->cairo);
704 xr_driver_next_page (xr, xr->cairo);
707 xr_driver_output_item (xr, output_item);
708 while (xr_driver_need_new_page (xr))
710 cairo_restore (xr->cairo);
711 cairo_show_page (xr->cairo);
712 cairo_save (xr->cairo);
713 xr_driver_next_page (xr, xr->cairo);
717 /* Functions for rendering a series of output items to a series of Cairo
718 contexts, with pagination.
720 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
721 its underlying implementation.
723 See the big comment in cairo.h for intended usage. */
725 /* Gives new page CAIRO to XR for output. */
727 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
729 if (!xr->transparent)
732 cairo_set_source_rgb (cairo,
733 xr->bg.r / 255.0, xr->bg.g / 255.0, xr->bg.b / 255.0);
734 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
736 cairo_restore (cairo);
738 cairo_translate (cairo,
739 xr_to_pt (xr->left_margin),
740 xr_to_pt (xr->top_margin + xr->headings_height[0]));
746 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
747 &xr->headings[0], xr->page_number, xr->width, true,
748 -xr->headings_height[0]);
749 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
750 &xr->headings[1], xr->page_number, xr->width, true,
753 xr_driver_run_fsm (xr);
756 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
757 rendering a previous output item, that is, only if xr_driver_need_new_page()
760 xr_driver_output_item (struct xr_driver *xr,
761 const struct output_item *output_item)
763 assert (xr->fsm == NULL);
764 xr->fsm = xr_render_output_item (xr, output_item);
765 xr_driver_run_fsm (xr);
768 /* Returns true if XR is in the middle of rendering an output item and needs a
769 new page to be appended using xr_driver_next_page() to make progress,
772 xr_driver_need_new_page (const struct xr_driver *xr)
774 return xr->fsm != NULL;
777 /* Returns true if the current page doesn't have any content yet. */
779 xr_driver_is_page_blank (const struct xr_driver *xr)
785 xr_driver_destroy_fsm (struct xr_driver *xr)
789 xr->fsm->destroy (xr->fsm);
795 xr_driver_run_fsm (struct xr_driver *xr)
797 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
798 xr_driver_destroy_fsm (xr);
802 xr_layout_cell (struct xr_driver *, const struct table_cell *,
803 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
804 int *width, int *height, int *brk);
807 set_source_rgba (cairo_t *cairo, const struct cell_color *color)
809 cairo_set_source_rgba (cairo,
810 color->r / 255., color->g / 255., color->b / 255.,
811 color->alpha / 255.);
815 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
816 const struct cell_color *color)
818 cairo_new_path (xr->cairo);
819 if (!xr->systemcolors)
820 set_source_rgba (xr->cairo, color);
821 cairo_set_line_width (
823 xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
824 : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
826 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
827 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
828 cairo_stroke (xr->cairo);
832 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
834 cairo_new_path (xr->cairo);
835 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
836 cairo_close_path (xr->cairo);
837 cairo_stroke (xr->cairo);
838 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
839 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y0 + xr->y));
840 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
841 cairo_line_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y1 + xr->y));
845 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
847 cairo_new_path (xr->cairo);
848 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
849 cairo_rectangle (xr->cairo,
850 xr_to_pt (x0), xr_to_pt (y0 + xr->y),
851 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
852 cairo_fill (xr->cairo);
855 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
856 shortening it to X0...X1 if SHORTEN is true.
857 Draws a horizontal line X1...X3 at Y if RIGHT says so,
858 shortening it to X2...X3 if SHORTEN is true. */
860 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
861 enum render_line_style left, enum render_line_style right,
862 const struct cell_color *left_color,
863 const struct cell_color *right_color,
866 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
867 && cell_color_equal (left_color, right_color))
868 dump_line (xr, x0, y, x3, y, left, left_color);
871 if (left != RENDER_LINE_NONE)
872 dump_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
873 if (right != RENDER_LINE_NONE)
874 dump_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
878 /* Draws a vertical line Y0...Y2 at X if TOP says so,
879 shortening it to Y0...Y1 if SHORTEN is true.
880 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
881 shortening it to Y2...Y3 if SHORTEN is true. */
883 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
884 enum render_line_style top, enum render_line_style bottom,
885 const struct cell_color *top_color,
886 const struct cell_color *bottom_color,
889 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
890 && cell_color_equal (top_color, bottom_color))
891 dump_line (xr, x, y0, x, y3, top, top_color);
894 if (top != RENDER_LINE_NONE)
895 dump_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
896 if (bottom != RENDER_LINE_NONE)
897 dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
902 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
903 enum render_line_style styles[TABLE_N_AXES][2],
904 struct cell_color colors[TABLE_N_AXES][2])
906 const int x0 = bb[H][0];
907 const int y0 = bb[V][0];
908 const int x3 = bb[H][1];
909 const int y3 = bb[V][1];
910 const int top = styles[H][0];
911 const int bottom = styles[H][1];
913 int start_side = render_direction_rtl();
914 int end_side = !start_side;
915 const int start_of_line = styles[V][start_side];
916 const int end_of_line = styles[V][end_side];
917 const struct cell_color *top_color = &colors[H][0];
918 const struct cell_color *bottom_color = &colors[H][1];
919 const struct cell_color *start_color = &colors[V][start_side];
920 const struct cell_color *end_color = &colors[V][end_side];
922 /* The algorithm here is somewhat subtle, to allow it to handle
923 all the kinds of intersections that we need.
925 Three additional ordinates are assigned along the x axis. The
926 first is xc, midway between x0 and x3. The others are x1 and
927 x2; for a single vertical line these are equal to xc, and for
928 a double vertical line they are the ordinates of the left and
929 right half of the double line.
931 yc, y1, and y2 are assigned similarly along the y axis.
933 The following diagram shows the coordinate system and output
934 for double top and bottom lines, single left line, and no
938 y0 ________________________
944 y1 = y2 = yc |######### # |
949 y3 |________#_____#_______|
951 struct xr_driver *xr = xr_;
953 /* Offset from center of each line in a pair of double lines. */
954 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
956 /* Are the lines along each axis single or double?
957 (It doesn't make sense to have different kinds of line on the
958 same axis, so we don't try to gracefully handle that case.) */
959 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
960 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
962 /* When horizontal lines are doubled,
963 the left-side line along y1 normally runs from x0 to x2,
964 and the right-side line along y1 from x3 to x1.
965 If the top-side line is also doubled, we shorten the y1 lines,
966 so that the left-side line runs only to x1,
967 and the right-side line only to x2.
968 Otherwise, the horizontal line at y = y1 below would cut off
969 the intersection, which looks ugly:
971 y0 ________________________
976 y1 |######### ########|
979 y2 |######################|
982 y3 |______________________|
983 It is more of a judgment call when the horizontal line is
984 single. We actually choose to cut off the line anyhow, as
985 shown in the first diagram above.
987 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
988 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
989 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
990 int horz_line_ofs = double_vert ? double_line_ofs : 0;
991 int xc = (x0 + x3) / 2;
992 int x1 = xc - horz_line_ofs;
993 int x2 = xc + horz_line_ofs;
995 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
996 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
997 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
998 int vert_line_ofs = double_horz ? double_line_ofs : 0;
999 int yc = (y0 + y3) / 2;
1000 int y1 = yc - vert_line_ofs;
1001 int y2 = yc + vert_line_ofs;
1004 horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
1005 start_color, end_color, shorten_yc_line);
1008 horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
1009 start_color, end_color, shorten_y1_lines);
1010 horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
1011 start_color, end_color, shorten_y2_lines);
1015 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
1019 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
1021 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
1027 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
1028 int *min_width, int *max_width)
1030 struct xr_driver *xr = xr_;
1031 int bb[TABLE_N_AXES][2];
1032 int clip[TABLE_N_AXES][2];
1039 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1040 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
1043 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
1046 *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
1047 + cell->style->cell_style.margin[H][1]);
1049 *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
1050 + cell->style->cell_style.margin[H][1]);
1054 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
1056 struct xr_driver *xr = xr_;
1057 int bb[TABLE_N_AXES][2];
1058 int clip[TABLE_N_AXES][2];
1062 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1063 + cell->style->cell_style.margin[H][1]);
1066 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1067 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
1068 h += px_to_xr (cell->style->cell_style.margin[V][0]
1069 + cell->style->cell_style.margin[V][1]);
1073 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
1076 xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
1077 int bb[TABLE_N_AXES][2], int valign_offset,
1078 int spill[TABLE_N_AXES][2],
1079 int clip[TABLE_N_AXES][2])
1081 struct xr_driver *xr = xr_;
1084 if (!xr->transparent)
1086 cairo_save (xr->cairo);
1087 int bg_clip[TABLE_N_AXES][2];
1088 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1090 bg_clip[axis][0] = clip[axis][0];
1091 if (bb[axis][0] == clip[axis][0])
1092 bg_clip[axis][0] -= spill[axis][0];
1094 bg_clip[axis][1] = clip[axis][1];
1095 if (bb[axis][1] == clip[axis][1])
1096 bg_clip[axis][1] += spill[axis][1];
1098 xr_clip (xr, bg_clip);
1099 set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]);
1101 bb[H][0] - spill[H][0],
1102 bb[V][0] - spill[V][0],
1103 bb[H][1] + spill[H][1],
1104 bb[V][1] + spill[V][1]);
1105 cairo_restore (xr->cairo);
1107 cairo_save (xr->cairo);
1108 if (!xr->systemcolors)
1109 set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]);
1111 bb[V][0] += valign_offset;
1113 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1115 bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
1116 bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
1118 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
1119 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1120 cairo_restore (xr->cairo);
1124 xr_adjust_break (void *xr_, const struct table_cell *cell,
1125 int width, int height)
1127 struct xr_driver *xr = xr_;
1128 int bb[TABLE_N_AXES][2];
1129 int clip[TABLE_N_AXES][2];
1132 if (xr_measure_cell_height (xr_, cell, width) < height)
1136 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1137 + cell->style->cell_style.margin[H][1]);
1141 bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
1142 + cell->style->cell_style.margin[V][1]);
1143 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1144 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1149 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
1151 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1153 double x0 = xr_to_pt (clip[H][0]);
1154 double y0 = xr_to_pt (clip[V][0] + xr->y);
1155 double x1 = xr_to_pt (clip[H][1]);
1156 double y1 = xr_to_pt (clip[V][1] + xr->y);
1158 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1159 cairo_clip (xr->cairo);
1164 add_attr (PangoAttrList *list, PangoAttribute *attr,
1165 guint start_index, guint end_index)
1167 attr->start_index = start_index;
1168 attr->end_index = end_index;
1169 pango_attr_list_insert (list, attr);
1173 markup_escape (struct string *out, unsigned int options,
1174 const char *in, size_t len)
1176 if (!(options & TAB_MARKUP))
1178 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
1190 ds_put_cstr (out, "&");
1193 ds_put_cstr (out, "<");
1196 ds_put_cstr (out, ">");
1199 ds_put_byte (out, c);
1206 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
1208 int size[TABLE_N_AXES];
1209 pango_layout_get_size (layout, &size[H], &size[V]);
1214 xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
1215 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1216 int *widthp, int *brk)
1218 const struct font_style *font_style = &cell->style->font_style;
1219 const struct cell_style *cell_style = &cell->style->cell_style;
1220 unsigned int options = cell->options;
1222 enum table_axis X = options & TAB_ROTATE ? V : H;
1223 enum table_axis Y = !X;
1224 int R = options & TAB_ROTATE ? 0 : 1;
1226 struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1227 : &xr->fonts[XR_FONT_PROPORTIONAL]);
1228 struct xr_font local_font;
1229 if (font_style->typeface)
1231 PangoFontDescription *desc = parse_font (
1232 font_style->typeface,
1233 font_style->size ? font_style->size * 1000 * xr->font_scale : 10000,
1234 font_style->bold, font_style->italic);
1237 PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1238 pango_layout_set_font_description (layout, desc);
1240 local_font.desc = desc;
1241 local_font.layout = layout;
1246 const char *text = cell->text;
1247 enum table_halign halign = table_halign_interpret (
1248 cell_style->halign, cell->options & TAB_NUMERIC);
1249 if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
1251 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
1253 const char *decimal = strrchr (text, cell_style->decimal_char);
1256 pango_layout_set_text (font->layout, decimal, strlen (decimal));
1257 pango_layout_set_width (font->layout, -1);
1258 margin_adjustment += get_layout_dimension (font->layout, H);
1261 if (margin_adjustment < 0)
1262 bb[H][1] += margin_adjustment;
1265 struct string tmp = DS_EMPTY_INITIALIZER;
1266 PangoAttrList *attrs = NULL;
1268 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
1269 Pango's implementation of it): it will break after a period or a comma
1270 that precedes a digit, e.g. in ".000" it will break after the period.
1271 This code looks for such a situation and inserts a U+2060 WORD JOINER
1272 to prevent the break.
1274 This isn't necessary when the decimal point is between two digits
1275 (e.g. "0.000" won't be broken) or when the display width is not limited so
1276 that word wrapping won't happen.
1278 It isn't necessary to look for more than one period or comma, as would
1279 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
1280 are present then there will always be a digit on both sides of every
1281 period and comma. */
1282 if (options & TAB_MARKUP)
1284 PangoAttrList *new_attrs;
1286 if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
1289 tmp.ss = ss_cstr (new_text);
1290 tmp.capacity = tmp.ss.length;
1294 /* XXX should we report the error? */
1295 ds_put_cstr (&tmp, text);
1298 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
1300 const char *decimal = text + strcspn (text, ".,");
1302 && c_isdigit (decimal[1])
1303 && (decimal == text || !c_isdigit (decimal[-1])))
1305 ds_extend (&tmp, strlen (text) + 16);
1306 markup_escape (&tmp, options, text, decimal - text + 1);
1307 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
1308 markup_escape (&tmp, options, decimal + 1, -1);
1312 if (font_style->underline)
1315 attrs = pango_attr_list_new ();
1316 pango_attr_list_insert (attrs, pango_attr_underline_new (
1317 PANGO_UNDERLINE_SINGLE));
1320 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
1322 /* If we haven't already put TEXT into tmp, do it now. */
1323 if (ds_is_empty (&tmp))
1325 ds_extend (&tmp, strlen (text) + 16);
1326 markup_escape (&tmp, options, text, -1);
1329 size_t subscript_ofs = ds_length (&tmp);
1330 for (size_t i = 0; i < cell->n_subscripts; i++)
1333 ds_put_byte (&tmp, ',');
1334 ds_put_cstr (&tmp, cell->subscripts[i]);
1337 size_t superscript_ofs = ds_length (&tmp);
1338 if (cell->superscript)
1339 ds_put_cstr (&tmp, cell->superscript);
1341 size_t footnote_ofs = ds_length (&tmp);
1342 for (size_t i = 0; i < cell->n_footnotes; i++)
1345 ds_put_byte (&tmp, ',');
1346 ds_put_cstr (&tmp, cell->footnotes[i]->marker);
1349 /* Allow footnote markers to occupy the right margin. That way, numbers
1350 in the column are still aligned. */
1351 if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
1353 /* Measure the width of the footnote marker, so we know how much we
1354 need to make room for. */
1355 pango_layout_set_text (font->layout, ds_cstr (&tmp) + footnote_ofs,
1356 ds_length (&tmp) - footnote_ofs);
1358 PangoAttrList *fn_attrs = pango_attr_list_new ();
1359 pango_attr_list_insert (
1360 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
1361 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
1362 pango_layout_set_attributes (font->layout, fn_attrs);
1363 pango_attr_list_unref (fn_attrs);
1364 int footnote_width = get_layout_dimension (font->layout, X);
1366 /* Bound the adjustment by the width of the right margin. */
1367 int right_margin = px_to_xr (cell_style->margin[X][R]);
1368 int footnote_adjustment = MIN (footnote_width, right_margin);
1370 /* Adjust the bounding box. */
1371 if (options & TAB_ROTATE)
1372 footnote_adjustment = -footnote_adjustment;
1373 bb[X][R] += footnote_adjustment;
1376 pango_layout_set_attributes (font->layout, NULL);
1379 /* Set attributes. */
1381 attrs = pango_attr_list_new ();
1382 add_attr (attrs, pango_attr_font_desc_new (font->desc), subscript_ofs,
1383 PANGO_ATTR_INDEX_TO_TEXT_END);
1384 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
1385 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
1386 if (cell->n_subscripts)
1387 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
1388 superscript_ofs - subscript_ofs);
1389 if (cell->superscript || cell->n_footnotes)
1390 add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
1391 PANGO_ATTR_INDEX_TO_TEXT_END);
1394 /* Set the attributes, if any. */
1397 pango_layout_set_attributes (font->layout, attrs);
1398 pango_attr_list_unref (attrs);
1402 if (ds_is_empty (&tmp))
1403 pango_layout_set_text (font->layout, text, -1);
1405 pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
1408 pango_layout_set_alignment (font->layout,
1409 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
1410 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
1411 : PANGO_ALIGN_CENTER));
1412 pango_layout_set_width (
1414 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
1415 pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1417 if (clip[H][0] != clip[H][1])
1419 cairo_save (xr->cairo);
1420 if (!(options & TAB_ROTATE))
1422 if (options & TAB_ROTATE)
1424 cairo_translate (xr->cairo,
1425 xr_to_pt (bb[H][0]),
1426 xr_to_pt (bb[V][1] + xr->y));
1427 cairo_rotate (xr->cairo, -M_PI_2);
1430 cairo_translate (xr->cairo,
1431 xr_to_pt (bb[H][0]),
1432 xr_to_pt (bb[V][0] + xr->y));
1433 pango_cairo_show_layout (xr->cairo, font->layout);
1435 /* If enabled, this draws a blue rectangle around the extents of each
1436 line of text, which can be rather useful for debugging layout
1440 PangoLayoutIter *iter;
1441 iter = pango_layout_get_iter (font->layout);
1444 PangoRectangle extents;
1446 pango_layout_iter_get_line_extents (iter, &extents, NULL);
1447 cairo_save (xr->cairo);
1448 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1450 pango_to_xr (extents.x),
1451 pango_to_xr (extents.y) - xr->y,
1452 pango_to_xr (extents.x + extents.width),
1453 pango_to_xr (extents.y + extents.height) - xr->y);
1454 cairo_restore (xr->cairo);
1456 while (pango_layout_iter_next_line (iter));
1457 pango_layout_iter_free (iter);
1460 cairo_restore (xr->cairo);
1463 int size[TABLE_N_AXES];
1464 pango_layout_get_size (font->layout, &size[H], &size[V]);
1465 int w = pango_to_xr (size[X]);
1466 int h = pango_to_xr (size[Y]);
1469 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
1471 PangoLayoutIter *iter;
1474 /* Choose a breakpoint between lines instead of in the middle of one. */
1475 iter = pango_layout_get_iter (font->layout);
1478 PangoRectangle extents;
1482 pango_layout_iter_get_line_extents (iter, NULL, &extents);
1483 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1484 extents.x = pango_to_xr (extents.x);
1485 extents.y = pango_to_xr (y0);
1486 extents.width = pango_to_xr (extents.width);
1487 extents.height = pango_to_xr (y1 - y0);
1488 bottom = bb[V][0] + extents.y + extents.height;
1489 if (bottom < bb[V][1])
1491 if (brk && clip[H][0] != clip[H][1])
1499 while (pango_layout_iter_next_line (iter));
1500 pango_layout_iter_free (iter);
1502 /* If enabled, draws a green line across the chosen breakpoint, which can
1503 be useful for debugging issues with breaking. */
1507 dump_line (xr, -xr->left_margin, best,
1508 xr->width + xr->right_margin, best,
1510 &(struct cell_color) CELL_COLOR (0, 255, 0));
1514 pango_layout_set_attributes (font->layout, NULL);
1516 if (font == &local_font)
1518 g_object_unref (G_OBJECT (font->layout));
1519 pango_font_description_free (font->desc);
1526 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1527 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1528 int *width, int *height, int *brk)
1533 /* If enabled, draws a blue rectangle around the cell extents, which can be
1534 useful for debugging layout. */
1537 if (clip[H][0] != clip[H][1])
1539 cairo_save (xr->cairo);
1540 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1541 dump_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
1542 cairo_restore (xr->cairo);
1548 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
1551 struct output_driver_factory pdf_driver_factory =
1552 { "pdf", "pspp.pdf", xr_pdf_create };
1553 struct output_driver_factory ps_driver_factory =
1554 { "ps", "pspp.ps", xr_ps_create };
1555 struct output_driver_factory svg_driver_factory =
1556 { "svg", "pspp.svg", xr_svg_create };
1558 static const struct output_driver_class cairo_driver_class =
1566 /* GUI rendering helpers. */
1570 struct output_item *item;
1573 struct render_pager *p;
1574 struct xr_driver *xr;
1577 #define CHART_WIDTH 500
1578 #define CHART_HEIGHT 375
1583 xr_driver_create (cairo_t *cairo, struct string_map *options)
1585 struct xr_driver *xr = xr_allocate ("cairo", 0, options, 1.0);
1586 xr_set_cairo (xr, cairo);
1590 /* Destroy XR, which should have been created with xr_driver_create(). Any
1591 cairo_t added to XR is not destroyed, because it is owned by the client. */
1593 xr_driver_destroy (struct xr_driver *xr)
1598 output_driver_destroy (&xr->driver);
1602 static struct xr_rendering *
1603 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1605 struct table_item *table_item;
1606 struct xr_rendering *r;
1608 table_item = table_item_create (table_from_string (text), NULL, NULL);
1609 r = xr_rendering_create (xr, &table_item->output_item, cr);
1610 table_item_unref (table_item);
1615 struct xr_rendering *
1616 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1619 struct xr_rendering *r = NULL;
1621 if (is_text_item (item))
1622 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1624 else if (is_message_item (item))
1626 const struct message_item *message_item = to_message_item (item);
1627 char *s = msg_to_string (message_item_get_msg (message_item));
1628 r = xr_rendering_create_text (xr, s, cr);
1631 else if (is_table_item (item))
1633 r = xzalloc (sizeof *r);
1634 r->item = output_item_ref (item);
1636 xr_set_cairo (xr, cr);
1637 r->p = render_pager_create (xr->params, to_table_item (item));
1639 else if (is_chart_item (item))
1641 r = xzalloc (sizeof *r);
1642 r->item = output_item_ref (item);
1644 else if (is_group_open_item (item))
1645 r = xr_rendering_create_text (xr, to_group_open_item (item)->command_name,
1652 xr_rendering_destroy (struct xr_rendering *r)
1656 output_item_unref (r->item);
1657 render_pager_destroy (r->p);
1663 xr_rendering_measure (const struct xr_rendering *r, int *wp, int *hp)
1667 if (is_table_item (r->item))
1669 w = render_pager_get_size (r->p, H) / XR_POINT;
1670 h = render_pager_get_size (r->p, V) / XR_POINT;
1684 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1685 double x, double y, double width, double height);
1689 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1690 int x0, int y0, int x1, int y1)
1692 if (is_table_item (r->item))
1694 struct xr_driver *xr = r->xr;
1696 xr_set_cairo (xr, cr);
1698 render_pager_draw_region (r->p, x0 * XR_POINT, y0 * XR_POINT,
1699 (x1 - x0) * XR_POINT, (y1 - y0) * XR_POINT);
1702 xr_draw_chart (to_chart_item (r->item), cr,
1703 0, 0, CHART_WIDTH, CHART_HEIGHT);
1707 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1708 double x, double y, double width, double height)
1710 struct xrchart_geometry geom;
1713 cairo_translate (cr, x, y + height);
1714 cairo_scale (cr, 1.0, -1.0);
1715 xrchart_geometry_init (cr, &geom, width, height);
1716 if (is_boxplot (chart_item))
1717 xrchart_draw_boxplot (chart_item, cr, &geom);
1718 else if (is_histogram_chart (chart_item))
1719 xrchart_draw_histogram (chart_item, cr, &geom);
1720 else if (is_np_plot_chart (chart_item))
1721 xrchart_draw_np_plot (chart_item, cr, &geom);
1722 else if (is_piechart (chart_item))
1723 xrchart_draw_piechart (chart_item, cr, &geom);
1724 else if (is_barchart (chart_item))
1725 xrchart_draw_barchart (chart_item, cr, &geom);
1726 else if (is_roc_chart (chart_item))
1727 xrchart_draw_roc (chart_item, cr, &geom);
1728 else if (is_scree (chart_item))
1729 xrchart_draw_scree (chart_item, cr, &geom);
1730 else if (is_spreadlevel_plot_chart (chart_item))
1731 xrchart_draw_spreadlevel (chart_item, cr, &geom);
1732 else if (is_scatterplot_chart (chart_item))
1733 xrchart_draw_scatterplot (chart_item, cr, &geom);
1736 xrchart_geometry_free (cr, &geom);
1742 xr_draw_png_chart (const struct chart_item *item,
1743 const char *file_name_template, int number,
1744 const struct cell_color *fg,
1745 const struct cell_color *bg)
1747 const int width = 640;
1748 const int length = 480;
1750 cairo_surface_t *surface;
1751 cairo_status_t status;
1752 const char *number_pos;
1756 number_pos = strchr (file_name_template, '#');
1757 if (number_pos != NULL)
1758 file_name = xasprintf ("%.*s%d%s.png", (int) (number_pos - file_name_template),
1759 file_name_template, number, number_pos + 1);
1761 file_name = xasprintf ("%s.png", file_name_template);
1763 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1764 cr = cairo_create (surface);
1766 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1769 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1771 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1773 status = cairo_surface_write_to_png (surface, file_name);
1774 if (status != CAIRO_STATUS_SUCCESS)
1775 msg (ME, _("error writing output file `%s': %s"),
1776 file_name, cairo_status_to_string (status));
1779 cairo_surface_destroy (surface);
1786 xr_draw_eps_chart (const struct chart_item *item,
1787 const char *file_name_template, int number,
1788 const struct cell_color *fg,
1789 const struct cell_color *bg)
1791 const int width = 640;
1792 const int length = 480;
1794 cairo_surface_t *surface;
1795 const char *number_pos;
1799 number_pos = strchr (file_name_template, '#');
1800 if (number_pos != NULL)
1801 file_name = xasprintf ("%.*s%d%s.eps", (int) (number_pos - file_name_template),
1802 file_name_template, number, number_pos + 1);
1804 file_name = xasprintf ("%s.eps", file_name_template);
1806 surface = cairo_ps_surface_create (file_name, width, length);
1807 cairo_ps_surface_set_eps (surface, true);
1808 cr = cairo_create (surface);
1810 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1813 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1815 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1818 cairo_surface_destroy (surface);
1825 struct xr_table_state
1827 struct xr_render_fsm fsm;
1828 struct render_pager *p;
1832 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1834 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1836 while (render_pager_has_next (ts->p))
1840 used = render_pager_draw_next (ts->p, xr->length - xr->y);
1853 xr_table_destroy (struct xr_render_fsm *fsm)
1855 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1857 render_pager_destroy (ts->p);
1861 static struct xr_render_fsm *
1862 xr_render_table (struct xr_driver *xr, struct table_item *table_item)
1864 struct xr_table_state *ts;
1866 ts = xmalloc (sizeof *ts);
1867 ts->fsm.render = xr_table_render;
1868 ts->fsm.destroy = xr_table_destroy;
1871 xr->y += xr->char_height;
1873 ts->p = render_pager_create (xr->params, table_item);
1874 table_item_unref (table_item);
1879 struct xr_chart_state
1881 struct xr_render_fsm fsm;
1882 struct chart_item *chart_item;
1886 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1888 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1890 const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
1892 if (xr->y > xr->length - chart_height)
1895 if (xr->cairo != NULL)
1897 xr_draw_chart (cs->chart_item, xr->cairo,
1900 xr_to_pt (xr->width),
1901 xr_to_pt (chart_height));
1903 xr->y += chart_height;
1909 xr_chart_destroy (struct xr_render_fsm *fsm)
1911 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1913 chart_item_unref (cs->chart_item);
1917 static struct xr_render_fsm *
1918 xr_render_chart (const struct chart_item *chart_item)
1920 struct xr_chart_state *cs;
1922 cs = xmalloc (sizeof *cs);
1923 cs->fsm.render = xr_chart_render;
1924 cs->fsm.destroy = xr_chart_destroy;
1925 cs->chart_item = chart_item_ref (chart_item);
1931 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1937 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1939 /* Nothing to do. */
1942 static struct xr_render_fsm *
1943 xr_render_eject (void)
1945 static struct xr_render_fsm eject_renderer =
1951 return &eject_renderer;
1954 static struct xr_render_fsm *
1955 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1957 enum text_item_type type = text_item_get_type (text_item);
1961 case TEXT_ITEM_PAGE_TITLE:
1964 case TEXT_ITEM_EJECT_PAGE:
1966 return xr_render_eject ();
1970 return xr_render_table (
1971 xr, text_item_to_table_item (text_item_ref (text_item)));
1977 static struct xr_render_fsm *
1978 xr_render_message (struct xr_driver *xr,
1979 const struct message_item *message_item)
1981 char *s = msg_to_string (message_item_get_msg (message_item));
1982 struct text_item *item = text_item_create (TEXT_ITEM_LOG, s);
1984 return xr_render_table (xr, text_item_to_table_item (item));
1987 static struct xr_render_fsm *
1988 xr_render_output_item (struct xr_driver *xr,
1989 const struct output_item *output_item)
1991 if (is_table_item (output_item))
1992 return xr_render_table (xr, table_item_ref (to_table_item (output_item)));
1993 else if (is_chart_item (output_item))
1994 return xr_render_chart (to_chart_item (output_item));
1995 else if (is_text_item (output_item))
1996 return xr_render_text (xr, to_text_item (output_item));
1997 else if (is_message_item (output_item))
1998 return xr_render_message (xr, to_message_item (output_item));
2004 xr_draw_svg_file (struct xr_rendering *r,
2005 const char *filename)
2009 xr_rendering_measure (r, &width, &height);
2010 cairo_surface_t *surface = cairo_svg_surface_create (filename, width, height);
2013 g_error ("Could not create cairo svg surface with file %s", filename);
2016 cairo_t *cr = cairo_create (surface);
2017 xr_rendering_draw (r, cr, 0, 0, width, height);
2019 cairo_surface_destroy (surface);