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-eject-item.h"
47 #include "output/page-setup-item.h"
48 #include "output/render.h"
49 #include "output/table-item.h"
50 #include "output/table.h"
51 #include "output/text-item.h"
53 #include <cairo/cairo-pdf.h>
54 #include <cairo/cairo-ps.h>
55 #include <cairo/cairo-svg.h>
56 #include <cairo/cairo.h>
59 #include <pango/pango-font.h>
60 #include <pango/pango-layout.h>
61 #include <pango/pango.h>
62 #include <pango/pangocairo.h>
65 #include "gl/c-ctype.h"
66 #include "gl/c-strcase.h"
67 #include "gl/intprops.h"
68 #include "gl/minmax.h"
69 #include "gl/xalloc.h"
72 #define _(msgid) gettext (msgid)
74 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
78 /* The unit used for internal measurements is inch/(72 * XR_POINT).
79 (Thus, XR_POINT units represent one point.) */
80 #define XR_POINT PANGO_SCALE
82 /* Conversions to and from points. */
86 return x / (double) XR_POINT;
89 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
93 return x * (PANGO_SCALE * 72 / 96);
96 /* Dimensions for drawing lines in tables. */
97 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
98 #define XR_LINE_SPACE XR_POINT /* Space between double lines. */
111 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);
291 xr->fg = parse_color (opt (d, o, "foreground-color", "#000000000000"));
292 xr->bg = parse_color (opt (d, o, "background-color", "#FFFFFFFFFFFF"));
294 xr->transparent = parse_boolean (opt (d, o, "transparent", "false"));
295 xr->systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
297 /* Get dimensions. */
298 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
299 left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
300 right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
301 top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
302 bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
304 min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
305 min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
307 int object_spacing = (parse_dimension (opt (d, o, "object-spacing", NULL))
310 /* Convert to inch/(XR_POINT * 72). */
311 xr->left_margin = left_margin * scale;
312 xr->right_margin = right_margin * scale;
313 xr->top_margin = top_margin * scale;
314 xr->bottom_margin = bottom_margin * scale;
315 xr->width = (paper_width - left_margin - right_margin) * scale;
316 xr->length = (paper_length - top_margin - bottom_margin) * scale;
317 xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
318 xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
319 xr->object_spacing = object_spacing >= 0 ? object_spacing : XR_POINT * 12;
321 /* There are no headings so headings_height can stay 0. */
324 static struct xr_driver *
325 xr_allocate (const char *name, int device_type, struct string_map *o,
328 struct xr_driver *xr = xzalloc (sizeof *xr);
329 struct output_driver *d = &xr->driver;
331 output_driver_init (d, &cairo_driver_class, name, device_type);
333 /* This is a nasty kluge for an issue that does not make sense. On any
334 surface other than a screen (e.g. for output to PDF or PS or SVG), the
335 fonts are way too big by default. A "9-point" font seems to appear about
336 16 points tall. We use a scale factor for these surfaces to help, but the
337 underlying issue is a mystery. */
338 xr->font_scale = font_scale;
340 apply_options (xr, o);
346 pango_to_xr (int pango)
348 return (XR_POINT != PANGO_SCALE
349 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
356 return (XR_POINT != PANGO_SCALE
357 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
362 xr_measure_fonts (cairo_t *cairo, const struct xr_font fonts[XR_N_FONTS],
363 int *char_width, int *char_height)
367 for (int i = 0; i < XR_N_FONTS; i++)
369 PangoLayout *layout = pango_cairo_create_layout (cairo);
370 pango_layout_set_font_description (layout, fonts[i].desc);
372 pango_layout_set_text (layout, "0", 1);
375 pango_layout_get_size (layout, &cw, &ch);
376 *char_width = MAX (*char_width, pango_to_xr (cw));
377 *char_height = MAX (*char_height, pango_to_xr (ch));
379 g_object_unref (G_OBJECT (layout));
384 get_layout_height (PangoLayout *layout)
387 pango_layout_get_size (layout, &w, &h);
392 xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
393 const struct page_heading *ph, int page_number,
394 int width, bool draw, int base_y)
396 PangoLayout *layout = pango_cairo_create_layout (cairo);
397 pango_layout_set_font_description (layout, font);
400 for (size_t i = 0; i < ph->n; i++)
402 const struct page_paragraph *pp = &ph->paragraphs[i];
404 char *markup = output_driver_substitute_heading_vars (pp->markup,
406 pango_layout_set_markup (layout, markup, -1);
409 pango_layout_set_alignment (
411 (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
412 : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER
413 : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
414 : PANGO_ALIGN_RIGHT));
415 pango_layout_set_width (layout, xr_to_pango (width));
419 cairo_translate (cairo, 0, xr_to_pt (y + base_y));
420 pango_cairo_show_layout (cairo, layout);
421 cairo_restore (cairo);
424 y += pango_to_xr (get_layout_height (layout));
427 g_object_unref (G_OBJECT (layout));
433 xr_measure_headings (cairo_surface_t *surface,
434 const PangoFontDescription *font,
435 const struct page_heading headings[2],
436 int width, int object_spacing, int height[2])
438 cairo_t *cairo = cairo_create (surface);
440 for (int i = 0; i < 2; i++)
442 int h = xr_render_page_heading (cairo, font, &headings[i], -1,
445 /* If the top heading is nonempty, add some space below it. */
453 cairo_destroy (cairo);
458 xr_check_fonts (cairo_surface_t *surface,
459 const struct xr_font fonts[XR_N_FONTS],
460 int usable_width, int usable_length)
462 cairo_t *cairo = cairo_create (surface);
463 int char_width, char_height;
464 xr_measure_fonts (cairo, fonts, &char_width, &char_height);
465 cairo_destroy (cairo);
468 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
469 if (usable_width / char_width < MIN_WIDTH)
471 msg (ME, _("The defined page is not wide enough to hold at least %d "
472 "characters in the default font. In fact, there's only "
473 "room for %d characters."),
474 MIN_WIDTH, usable_width / char_width);
477 if (usable_length / char_height < MIN_LENGTH)
479 msg (ME, _("The defined page is not long enough to hold at least %d "
480 "lines in the default font. In fact, there's only "
481 "room for %d lines."),
482 MIN_LENGTH, usable_length / char_height);
489 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
493 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
495 xr_measure_fonts (xr->cairo, xr->fonts, &xr->char_width, &xr->char_height);
497 for (int i = 0; i < XR_N_FONTS; i++)
499 struct xr_font *font = &xr->fonts[i];
500 font->layout = pango_cairo_create_layout (cairo);
501 pango_layout_set_font_description (font->layout, font->desc);
504 if (xr->params == NULL)
506 static const struct render_ops xr_render_ops = {
507 .draw_line = xr_draw_line,
508 .measure_cell_width = xr_measure_cell_width,
509 .measure_cell_height = xr_measure_cell_height,
510 .adjust_break = xr_adjust_break,
511 .draw_cell = xr_draw_cell,
514 xr->params = xmalloc (sizeof *xr->params);
515 xr->params->ops = &xr_render_ops;
516 xr->params->aux = xr;
517 xr->params->size[H] = xr->width;
518 xr->params->size[V] = xr->length;
519 xr->params->font_size[H] = xr->char_width;
520 xr->params->font_size[V] = xr->char_height;
522 enum { LW = XR_LINE_WIDTH, LS = XR_LINE_SPACE };
523 static const int xr_line_widths[RENDER_N_LINES] =
525 [RENDER_LINE_NONE] = 0,
526 [RENDER_LINE_SINGLE] = LW,
527 [RENDER_LINE_DASHED] = LW,
528 [RENDER_LINE_THICK] = LW * 2,
529 [RENDER_LINE_THIN] = LW / 2,
530 [RENDER_LINE_DOUBLE] = 2 * LW + LS,
532 xr->params->line_widths = xr_line_widths;
534 for (int i = 0; i < TABLE_N_AXES; i++)
535 xr->params->min_break[i] = xr->min_break[i];
536 xr->params->supports_margins = true;
537 xr->params->rtl = render_direction_rtl ();
540 if (!xr->systemcolors)
541 cairo_set_source_rgb (xr->cairo,
542 xr->fg.r / 255.0, xr->fg.g / 255.0, xr->fg.b / 255.0);
545 static struct output_driver *
546 xr_create (struct file_handle *fh, enum settings_output_devices device_type,
547 struct string_map *o, enum xr_output_type file_type)
549 const char *file_name = fh_get_file_name (fh);
550 struct xr_driver *xr = xr_allocate (file_name, device_type, o, 72.0 / 128.0);
551 double width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
552 double length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
553 if (file_type == XR_PDF)
554 xr->surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
555 else if (file_type == XR_PS)
556 xr->surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
557 else if (file_type == XR_SVG)
558 xr->surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
562 cairo_status_t status = cairo_surface_status (xr->surface);
563 if (status != CAIRO_STATUS_SUCCESS)
565 msg (ME, _("error opening output file `%s': %s"),
566 file_name, cairo_status_to_string (status));
570 if (!xr_check_fonts (xr->surface, xr->fonts, xr->width, xr->length))
578 output_driver_destroy (&xr->driver);
582 static struct output_driver *
583 xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
584 struct string_map *o)
586 return xr_create (fh, device_type, o, XR_PDF);
589 static struct output_driver *
590 xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
591 struct string_map *o)
593 return xr_create (fh, device_type, o, XR_PS);
596 static struct output_driver *
597 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
598 struct string_map *o)
600 return xr_create (fh, device_type, o, XR_SVG);
604 xr_destroy (struct output_driver *driver)
606 struct xr_driver *xr = xr_driver_cast (driver);
609 xr_driver_destroy_fsm (xr);
611 if (xr->cairo != NULL)
613 cairo_surface_finish (xr->surface);
614 cairo_status_t status = cairo_status (xr->cairo);
615 if (status != CAIRO_STATUS_SUCCESS)
616 fprintf (stderr, _("error drawing output for %s driver: %s"),
617 output_driver_get_name (driver),
618 cairo_status_to_string (status));
619 cairo_surface_destroy (xr->surface);
621 cairo_destroy (xr->cairo);
624 for (i = 0; i < XR_N_FONTS; i++)
626 struct xr_font *font = &xr->fonts[i];
628 if (font->desc != NULL)
629 pango_font_description_free (font->desc);
630 if (font->layout != NULL)
631 g_object_unref (font->layout);
639 xr_flush (struct output_driver *driver)
641 struct xr_driver *xr = xr_driver_cast (driver);
643 cairo_surface_flush (cairo_get_target (xr->cairo));
647 xr_update_page_setup (struct output_driver *driver,
648 const struct page_setup *ps)
650 struct xr_driver *xr = xr_driver_cast (driver);
652 xr->initial_page_number = ps->initial_page_number;
653 xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
658 int usable[TABLE_N_AXES];
659 for (int i = 0; i < 2; i++)
660 usable[i] = (ps->paper[i]
661 - (ps->margins[i][0] + ps->margins[i][1])) * 72 * XR_POINT;
663 int headings_height[2];
664 usable[V] -= xr_measure_headings (
665 xr->surface, xr->fonts[XR_FONT_PROPORTIONAL].desc, ps->headings,
666 usable[H], xr->object_spacing, headings_height);
668 enum table_axis h = ps->orientation == PAGE_LANDSCAPE;
669 enum table_axis v = !h;
670 if (!xr_check_fonts (xr->surface, xr->fonts, usable[h], usable[v]))
673 for (int i = 0; i < 2; i++)
675 page_heading_uninit (&xr->headings[i]);
676 page_heading_copy (&xr->headings[i], &ps->headings[i]);
677 xr->headings_height[i] = headings_height[i];
679 xr->width = usable[h];
680 xr->length = usable[v];
681 xr->left_margin = ps->margins[h][0] * 72 * XR_POINT;
682 xr->right_margin = ps->margins[h][1] * 72 * XR_POINT;
683 xr->top_margin = ps->margins[v][0] * 72 * XR_POINT;
684 xr->bottom_margin = ps->margins[v][1] * 72 * XR_POINT;
685 cairo_pdf_surface_set_size (xr->surface,
686 ps->paper[h] * 72.0, ps->paper[v] * 72.0);
690 xr_submit (struct output_driver *driver, const struct output_item *output_item)
692 struct xr_driver *xr = xr_driver_cast (driver);
694 if (is_page_setup_item (output_item))
696 xr_update_page_setup (driver,
697 to_page_setup_item (output_item)->page_setup);
703 xr->page_number = xr->initial_page_number - 1;
704 xr_set_cairo (xr, cairo_create (xr->surface));
705 cairo_save (xr->cairo);
706 xr_driver_next_page (xr, xr->cairo);
709 xr_driver_output_item (xr, output_item);
710 while (xr_driver_need_new_page (xr))
712 cairo_restore (xr->cairo);
713 cairo_show_page (xr->cairo);
714 cairo_save (xr->cairo);
715 xr_driver_next_page (xr, xr->cairo);
719 /* Functions for rendering a series of output items to a series of Cairo
720 contexts, with pagination.
722 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
723 its underlying implementation.
725 See the big comment in cairo.h for intended usage. */
727 /* Gives new page CAIRO to XR for output. */
729 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
731 if (!xr->transparent)
734 cairo_set_source_rgb (cairo,
735 xr->bg.r / 255.0, xr->bg.g / 255.0, xr->bg.b / 255.0);
736 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
738 cairo_restore (cairo);
740 cairo_translate (cairo,
741 xr_to_pt (xr->left_margin),
742 xr_to_pt (xr->top_margin + xr->headings_height[0]));
748 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
749 &xr->headings[0], xr->page_number, xr->width, true,
750 -xr->headings_height[0]);
751 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
752 &xr->headings[1], xr->page_number, xr->width, true,
755 xr_driver_run_fsm (xr);
758 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
759 rendering a previous output item, that is, only if xr_driver_need_new_page()
762 xr_driver_output_item (struct xr_driver *xr,
763 const struct output_item *output_item)
765 assert (xr->fsm == NULL);
766 xr->fsm = xr_render_output_item (xr, output_item);
767 xr_driver_run_fsm (xr);
770 /* Returns true if XR is in the middle of rendering an output item and needs a
771 new page to be appended using xr_driver_next_page() to make progress,
774 xr_driver_need_new_page (const struct xr_driver *xr)
776 return xr->fsm != NULL;
779 /* Returns true if the current page doesn't have any content yet. */
781 xr_driver_is_page_blank (const struct xr_driver *xr)
787 xr_driver_destroy_fsm (struct xr_driver *xr)
791 xr->fsm->destroy (xr->fsm);
797 xr_driver_run_fsm (struct xr_driver *xr)
799 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
800 xr_driver_destroy_fsm (xr);
804 xr_layout_cell (struct xr_driver *, const struct table_cell *,
805 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
806 int *width, int *height, int *brk);
809 set_source_rgba (cairo_t *cairo, const struct cell_color *color)
811 cairo_set_source_rgba (cairo,
812 color->r / 255., color->g / 255., color->b / 255.,
813 color->alpha / 255.);
817 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
818 const struct cell_color *color)
820 cairo_new_path (xr->cairo);
821 if (!xr->systemcolors)
822 set_source_rgba (xr->cairo, color);
823 cairo_set_line_width (
825 xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
826 : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
828 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
829 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
830 cairo_stroke (xr->cairo);
834 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
836 cairo_new_path (xr->cairo);
837 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
838 cairo_close_path (xr->cairo);
839 cairo_stroke (xr->cairo);
840 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
841 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y0 + xr->y));
842 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
843 cairo_line_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y1 + xr->y));
847 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
849 cairo_new_path (xr->cairo);
850 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
851 cairo_rectangle (xr->cairo,
852 xr_to_pt (x0), xr_to_pt (y0 + xr->y),
853 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
854 cairo_fill (xr->cairo);
857 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
858 shortening it to X0...X1 if SHORTEN is true.
859 Draws a horizontal line X1...X3 at Y if RIGHT says so,
860 shortening it to X2...X3 if SHORTEN is true. */
862 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
863 enum render_line_style left, enum render_line_style right,
864 const struct cell_color *left_color,
865 const struct cell_color *right_color,
868 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
869 && cell_color_equal (left_color, right_color))
870 dump_line (xr, x0, y, x3, y, left, left_color);
873 if (left != RENDER_LINE_NONE)
874 dump_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
875 if (right != RENDER_LINE_NONE)
876 dump_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
880 /* Draws a vertical line Y0...Y2 at X if TOP says so,
881 shortening it to Y0...Y1 if SHORTEN is true.
882 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
883 shortening it to Y2...Y3 if SHORTEN is true. */
885 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
886 enum render_line_style top, enum render_line_style bottom,
887 const struct cell_color *top_color,
888 const struct cell_color *bottom_color,
891 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
892 && cell_color_equal (top_color, bottom_color))
893 dump_line (xr, x, y0, x, y3, top, top_color);
896 if (top != RENDER_LINE_NONE)
897 dump_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
898 if (bottom != RENDER_LINE_NONE)
899 dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
904 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
905 enum render_line_style styles[TABLE_N_AXES][2],
906 struct cell_color colors[TABLE_N_AXES][2])
908 const int x0 = bb[H][0];
909 const int y0 = bb[V][0];
910 const int x3 = bb[H][1];
911 const int y3 = bb[V][1];
912 const int top = styles[H][0];
913 const int bottom = styles[H][1];
915 int start_side = render_direction_rtl();
916 int end_side = !start_side;
917 const int start_of_line = styles[V][start_side];
918 const int end_of_line = styles[V][end_side];
919 const struct cell_color *top_color = &colors[H][0];
920 const struct cell_color *bottom_color = &colors[H][1];
921 const struct cell_color *start_color = &colors[V][start_side];
922 const struct cell_color *end_color = &colors[V][end_side];
924 /* The algorithm here is somewhat subtle, to allow it to handle
925 all the kinds of intersections that we need.
927 Three additional ordinates are assigned along the x axis. The
928 first is xc, midway between x0 and x3. The others are x1 and
929 x2; for a single vertical line these are equal to xc, and for
930 a double vertical line they are the ordinates of the left and
931 right half of the double line.
933 yc, y1, and y2 are assigned similarly along the y axis.
935 The following diagram shows the coordinate system and output
936 for double top and bottom lines, single left line, and no
940 y0 ________________________
946 y1 = y2 = yc |######### # |
951 y3 |________#_____#_______|
953 struct xr_driver *xr = xr_;
955 /* Offset from center of each line in a pair of double lines. */
956 int double_line_ofs = (XR_LINE_SPACE + XR_LINE_WIDTH) / 2;
958 /* Are the lines along each axis single or double?
959 (It doesn't make sense to have different kinds of line on the
960 same axis, so we don't try to gracefully handle that case.) */
961 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
962 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
964 /* When horizontal lines are doubled,
965 the left-side line along y1 normally runs from x0 to x2,
966 and the right-side line along y1 from x3 to x1.
967 If the top-side line is also doubled, we shorten the y1 lines,
968 so that the left-side line runs only to x1,
969 and the right-side line only to x2.
970 Otherwise, the horizontal line at y = y1 below would cut off
971 the intersection, which looks ugly:
973 y0 ________________________
978 y1 |######### ########|
981 y2 |######################|
984 y3 |______________________|
985 It is more of a judgment call when the horizontal line is
986 single. We actually choose to cut off the line anyhow, as
987 shown in the first diagram above.
989 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
990 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
991 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
992 int horz_line_ofs = double_vert ? double_line_ofs : 0;
993 int xc = (x0 + x3) / 2;
994 int x1 = xc - horz_line_ofs;
995 int x2 = xc + horz_line_ofs;
997 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
998 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
999 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
1000 int vert_line_ofs = double_horz ? double_line_ofs : 0;
1001 int yc = (y0 + y3) / 2;
1002 int y1 = yc - vert_line_ofs;
1003 int y2 = yc + vert_line_ofs;
1006 horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
1007 start_color, end_color, shorten_yc_line);
1010 horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
1011 start_color, end_color, shorten_y1_lines);
1012 horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
1013 start_color, end_color, shorten_y2_lines);
1017 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
1021 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
1023 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
1029 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
1030 int *min_width, int *max_width)
1032 struct xr_driver *xr = xr_;
1033 int bb[TABLE_N_AXES][2];
1034 int clip[TABLE_N_AXES][2];
1041 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1042 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
1045 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
1048 *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
1049 + cell->style->cell_style.margin[H][1]);
1051 *max_width += px_to_xr (cell->style->cell_style.margin[H][0]
1052 + cell->style->cell_style.margin[H][1]);
1056 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
1058 struct xr_driver *xr = xr_;
1059 int bb[TABLE_N_AXES][2];
1060 int clip[TABLE_N_AXES][2];
1064 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1065 + cell->style->cell_style.margin[H][1]);
1068 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1069 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
1070 h += px_to_xr (cell->style->cell_style.margin[V][0]
1071 + cell->style->cell_style.margin[V][1]);
1075 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
1078 xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
1079 int bb[TABLE_N_AXES][2], int valign_offset,
1080 int spill[TABLE_N_AXES][2],
1081 int clip[TABLE_N_AXES][2])
1083 struct xr_driver *xr = xr_;
1086 if (!xr->transparent)
1088 cairo_save (xr->cairo);
1089 int bg_clip[TABLE_N_AXES][2];
1090 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1092 bg_clip[axis][0] = clip[axis][0];
1093 if (bb[axis][0] == clip[axis][0])
1094 bg_clip[axis][0] -= spill[axis][0];
1096 bg_clip[axis][1] = clip[axis][1];
1097 if (bb[axis][1] == clip[axis][1])
1098 bg_clip[axis][1] += spill[axis][1];
1100 xr_clip (xr, bg_clip);
1101 set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]);
1103 bb[H][0] - spill[H][0],
1104 bb[V][0] - spill[V][0],
1105 bb[H][1] + spill[H][1],
1106 bb[V][1] + spill[V][1]);
1107 cairo_restore (xr->cairo);
1109 cairo_save (xr->cairo);
1110 if (!xr->systemcolors)
1111 set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]);
1113 bb[V][0] += valign_offset;
1115 for (int axis = 0; axis < TABLE_N_AXES; axis++)
1117 bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]);
1118 bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]);
1120 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
1121 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1122 cairo_restore (xr->cairo);
1126 xr_adjust_break (void *xr_, const struct table_cell *cell,
1127 int width, int height)
1129 struct xr_driver *xr = xr_;
1130 int bb[TABLE_N_AXES][2];
1131 int clip[TABLE_N_AXES][2];
1134 if (xr_measure_cell_height (xr_, cell, width) < height)
1138 bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0]
1139 + cell->style->cell_style.margin[H][1]);
1143 bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0]
1144 + cell->style->cell_style.margin[V][1]);
1145 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
1146 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
1151 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
1153 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1155 double x0 = xr_to_pt (clip[H][0]);
1156 double y0 = xr_to_pt (clip[V][0] + xr->y);
1157 double x1 = xr_to_pt (clip[H][1]);
1158 double y1 = xr_to_pt (clip[V][1] + xr->y);
1160 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1161 cairo_clip (xr->cairo);
1166 add_attr (PangoAttrList *list, PangoAttribute *attr,
1167 guint start_index, guint end_index)
1169 attr->start_index = start_index;
1170 attr->end_index = end_index;
1171 pango_attr_list_insert (list, attr);
1175 markup_escape (struct string *out, unsigned int options,
1176 const char *in, size_t len)
1178 if (!(options & TAB_MARKUP))
1180 ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
1192 ds_put_cstr (out, "&");
1195 ds_put_cstr (out, "<");
1198 ds_put_cstr (out, ">");
1201 ds_put_byte (out, c);
1208 get_layout_dimension (PangoLayout *layout, enum table_axis axis)
1210 int size[TABLE_N_AXES];
1211 pango_layout_get_size (layout, &size[H], &size[V]);
1216 xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
1217 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1218 int *widthp, int *brk)
1220 const struct font_style *font_style = &cell->style->font_style;
1221 const struct cell_style *cell_style = &cell->style->cell_style;
1222 unsigned int options = cell->options;
1224 enum table_axis X = options & TAB_ROTATE ? V : H;
1225 enum table_axis Y = !X;
1226 int R = options & TAB_ROTATE ? 0 : 1;
1228 struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1229 : &xr->fonts[XR_FONT_PROPORTIONAL]);
1230 struct xr_font local_font;
1231 if (font_style->typeface)
1233 PangoFontDescription *desc = parse_font (
1234 font_style->typeface,
1235 font_style->size ? font_style->size * 1000 * xr->font_scale : 10000,
1236 font_style->bold, font_style->italic);
1239 PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1240 pango_layout_set_font_description (layout, desc);
1242 local_font.desc = desc;
1243 local_font.layout = layout;
1248 const char *text = cell->text;
1249 enum table_halign halign = table_halign_interpret (
1250 cell_style->halign, cell->options & TAB_NUMERIC);
1251 if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
1253 int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
1255 const char *decimal = strrchr (text, cell_style->decimal_char);
1258 pango_layout_set_text (font->layout, decimal, strlen (decimal));
1259 pango_layout_set_width (font->layout, -1);
1260 margin_adjustment += get_layout_dimension (font->layout, H);
1263 if (margin_adjustment < 0)
1264 bb[H][1] += margin_adjustment;
1267 struct string tmp = DS_EMPTY_INITIALIZER;
1268 PangoAttrList *attrs = NULL;
1270 /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
1271 Pango's implementation of it): it will break after a period or a comma
1272 that precedes a digit, e.g. in ".000" it will break after the period.
1273 This code looks for such a situation and inserts a U+2060 WORD JOINER
1274 to prevent the break.
1276 This isn't necessary when the decimal point is between two digits
1277 (e.g. "0.000" won't be broken) or when the display width is not limited so
1278 that word wrapping won't happen.
1280 It isn't necessary to look for more than one period or comma, as would
1281 happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
1282 are present then there will always be a digit on both sides of every
1283 period and comma. */
1284 if (options & TAB_MARKUP)
1286 PangoAttrList *new_attrs;
1288 if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
1291 tmp.ss = ss_cstr (new_text);
1292 tmp.capacity = tmp.ss.length;
1296 /* XXX should we report the error? */
1297 ds_put_cstr (&tmp, text);
1300 else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
1302 const char *decimal = text + strcspn (text, ".,");
1304 && c_isdigit (decimal[1])
1305 && (decimal == text || !c_isdigit (decimal[-1])))
1307 ds_extend (&tmp, strlen (text) + 16);
1308 markup_escape (&tmp, options, text, decimal - text + 1);
1309 ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
1310 markup_escape (&tmp, options, decimal + 1, -1);
1314 if (font_style->underline)
1317 attrs = pango_attr_list_new ();
1318 pango_attr_list_insert (attrs, pango_attr_underline_new (
1319 PANGO_UNDERLINE_SINGLE));
1322 if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
1324 /* If we haven't already put TEXT into tmp, do it now. */
1325 if (ds_is_empty (&tmp))
1327 ds_extend (&tmp, strlen (text) + 16);
1328 markup_escape (&tmp, options, text, -1);
1331 size_t subscript_ofs = ds_length (&tmp);
1332 for (size_t i = 0; i < cell->n_subscripts; i++)
1335 ds_put_byte (&tmp, ',');
1336 ds_put_cstr (&tmp, cell->subscripts[i]);
1339 size_t superscript_ofs = ds_length (&tmp);
1340 if (cell->superscript)
1341 ds_put_cstr (&tmp, cell->superscript);
1343 size_t footnote_ofs = ds_length (&tmp);
1344 for (size_t i = 0; i < cell->n_footnotes; i++)
1347 ds_put_byte (&tmp, ',');
1348 ds_put_cstr (&tmp, cell->footnotes[i]->marker);
1351 /* Allow footnote markers to occupy the right margin. That way, numbers
1352 in the column are still aligned. */
1353 if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
1355 /* Measure the width of the footnote marker, so we know how much we
1356 need to make room for. */
1357 pango_layout_set_text (font->layout, ds_cstr (&tmp) + footnote_ofs,
1358 ds_length (&tmp) - footnote_ofs);
1360 PangoAttrList *fn_attrs = pango_attr_list_new ();
1361 pango_attr_list_insert (
1362 fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
1363 pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
1364 pango_layout_set_attributes (font->layout, fn_attrs);
1365 pango_attr_list_unref (fn_attrs);
1366 int footnote_width = get_layout_dimension (font->layout, X);
1368 /* Bound the adjustment by the width of the right margin. */
1369 int right_margin = px_to_xr (cell_style->margin[X][R]);
1370 int footnote_adjustment = MIN (footnote_width, right_margin);
1372 /* Adjust the bounding box. */
1373 if (options & TAB_ROTATE)
1374 footnote_adjustment = -footnote_adjustment;
1375 bb[X][R] += footnote_adjustment;
1378 pango_layout_set_attributes (font->layout, NULL);
1381 /* Set attributes. */
1383 attrs = pango_attr_list_new ();
1384 add_attr (attrs, pango_attr_font_desc_new (font->desc), subscript_ofs,
1385 PANGO_ATTR_INDEX_TO_TEXT_END);
1386 add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
1387 subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
1388 if (cell->n_subscripts)
1389 add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
1390 superscript_ofs - subscript_ofs);
1391 if (cell->superscript || cell->n_footnotes)
1392 add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
1393 PANGO_ATTR_INDEX_TO_TEXT_END);
1396 /* Set the attributes, if any. */
1399 pango_layout_set_attributes (font->layout, attrs);
1400 pango_attr_list_unref (attrs);
1404 if (ds_is_empty (&tmp))
1405 pango_layout_set_text (font->layout, text, -1);
1407 pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
1410 pango_layout_set_alignment (font->layout,
1411 (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
1412 : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
1413 : PANGO_ALIGN_CENTER));
1414 pango_layout_set_width (
1416 bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
1417 pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1419 if (clip[H][0] != clip[H][1])
1421 cairo_save (xr->cairo);
1422 if (!(options & TAB_ROTATE))
1424 if (options & TAB_ROTATE)
1426 cairo_translate (xr->cairo,
1427 xr_to_pt (bb[H][0]),
1428 xr_to_pt (bb[V][1] + xr->y));
1429 cairo_rotate (xr->cairo, -M_PI_2);
1432 cairo_translate (xr->cairo,
1433 xr_to_pt (bb[H][0]),
1434 xr_to_pt (bb[V][0] + xr->y));
1435 pango_cairo_show_layout (xr->cairo, font->layout);
1437 /* If enabled, this draws a blue rectangle around the extents of each
1438 line of text, which can be rather useful for debugging layout
1442 PangoLayoutIter *iter;
1443 iter = pango_layout_get_iter (font->layout);
1446 PangoRectangle extents;
1448 pango_layout_iter_get_line_extents (iter, &extents, NULL);
1449 cairo_save (xr->cairo);
1450 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1452 pango_to_xr (extents.x),
1453 pango_to_xr (extents.y) - xr->y,
1454 pango_to_xr (extents.x + extents.width),
1455 pango_to_xr (extents.y + extents.height) - xr->y);
1456 cairo_restore (xr->cairo);
1458 while (pango_layout_iter_next_line (iter));
1459 pango_layout_iter_free (iter);
1462 cairo_restore (xr->cairo);
1465 int size[TABLE_N_AXES];
1466 pango_layout_get_size (font->layout, &size[H], &size[V]);
1467 int w = pango_to_xr (size[X]);
1468 int h = pango_to_xr (size[Y]);
1471 if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
1473 PangoLayoutIter *iter;
1476 /* Choose a breakpoint between lines instead of in the middle of one. */
1477 iter = pango_layout_get_iter (font->layout);
1480 PangoRectangle extents;
1484 pango_layout_iter_get_line_extents (iter, NULL, &extents);
1485 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1486 extents.x = pango_to_xr (extents.x);
1487 extents.y = pango_to_xr (y0);
1488 extents.width = pango_to_xr (extents.width);
1489 extents.height = pango_to_xr (y1 - y0);
1490 bottom = bb[V][0] + extents.y + extents.height;
1491 if (bottom < bb[V][1])
1493 if (brk && clip[H][0] != clip[H][1])
1501 while (pango_layout_iter_next_line (iter));
1502 pango_layout_iter_free (iter);
1504 /* If enabled, draws a green line across the chosen breakpoint, which can
1505 be useful for debugging issues with breaking. */
1509 dump_line (xr, -xr->left_margin, best,
1510 xr->width + xr->right_margin, best,
1512 &(struct cell_color) CELL_COLOR (0, 255, 0));
1516 pango_layout_set_attributes (font->layout, NULL);
1518 if (font == &local_font)
1520 g_object_unref (G_OBJECT (font->layout));
1521 pango_font_description_free (font->desc);
1528 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1529 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1530 int *width, int *height, int *brk)
1535 /* If enabled, draws a blue rectangle around the cell extents, which can be
1536 useful for debugging layout. */
1539 if (clip[H][0] != clip[H][1])
1541 cairo_save (xr->cairo);
1542 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1543 dump_rectangle (xr, bb[H][0], bb[V][0], bb[H][1], bb[V][1]);
1544 cairo_restore (xr->cairo);
1550 *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
1553 struct output_driver_factory pdf_driver_factory =
1554 { "pdf", "pspp.pdf", xr_pdf_create };
1555 struct output_driver_factory ps_driver_factory =
1556 { "ps", "pspp.ps", xr_ps_create };
1557 struct output_driver_factory svg_driver_factory =
1558 { "svg", "pspp.svg", xr_svg_create };
1560 static const struct output_driver_class cairo_driver_class =
1568 /* GUI rendering helpers. */
1572 struct output_item *item;
1575 struct render_pager *p;
1576 struct xr_driver *xr;
1579 #define CHART_WIDTH 500
1580 #define CHART_HEIGHT 375
1585 xr_driver_create (cairo_t *cairo, struct string_map *options)
1587 struct xr_driver *xr = xr_allocate ("cairo", 0, options, 1.0);
1588 xr_set_cairo (xr, cairo);
1592 /* Destroy XR, which should have been created with xr_driver_create(). Any
1593 cairo_t added to XR is not destroyed, because it is owned by the client. */
1595 xr_driver_destroy (struct xr_driver *xr)
1600 output_driver_destroy (&xr->driver);
1604 static struct xr_rendering *
1605 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1607 struct table_item *table_item;
1608 struct xr_rendering *r;
1610 table_item = table_item_create (table_from_string (text), NULL, NULL);
1611 r = xr_rendering_create (xr, &table_item->output_item, cr);
1612 table_item_unref (table_item);
1617 struct xr_rendering *
1618 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1621 struct xr_rendering *r = NULL;
1623 if (is_text_item (item))
1624 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1626 else if (is_message_item (item))
1628 const struct message_item *message_item = to_message_item (item);
1629 char *s = msg_to_string (message_item_get_msg (message_item));
1630 r = xr_rendering_create_text (xr, s, cr);
1633 else if (is_table_item (item))
1635 r = xzalloc (sizeof *r);
1636 r->item = output_item_ref (item);
1638 xr_set_cairo (xr, cr);
1639 r->p = render_pager_create (xr->params, to_table_item (item));
1641 else if (is_chart_item (item))
1643 r = xzalloc (sizeof *r);
1644 r->item = output_item_ref (item);
1646 else if (is_group_open_item (item))
1647 r = xr_rendering_create_text (xr, to_group_open_item (item)->command_name,
1654 xr_rendering_destroy (struct xr_rendering *r)
1658 output_item_unref (r->item);
1659 render_pager_destroy (r->p);
1665 xr_rendering_measure (const struct xr_rendering *r, int *wp, int *hp)
1669 if (is_table_item (r->item))
1671 w = render_pager_get_size (r->p, H) / XR_POINT;
1672 h = render_pager_get_size (r->p, V) / XR_POINT;
1686 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1687 double x, double y, double width, double height);
1691 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
1692 int x0, int y0, int x1, int y1)
1694 if (is_table_item (r->item))
1696 struct xr_driver *xr = r->xr;
1698 xr_set_cairo (xr, cr);
1700 render_pager_draw_region (r->p, x0 * XR_POINT, y0 * XR_POINT,
1701 (x1 - x0) * XR_POINT, (y1 - y0) * XR_POINT);
1704 xr_draw_chart (to_chart_item (r->item), cr,
1705 0, 0, CHART_WIDTH, CHART_HEIGHT);
1709 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1710 double x, double y, double width, double height)
1712 struct xrchart_geometry geom;
1715 cairo_translate (cr, x, y + height);
1716 cairo_scale (cr, 1.0, -1.0);
1717 xrchart_geometry_init (cr, &geom, width, height);
1718 if (is_boxplot (chart_item))
1719 xrchart_draw_boxplot (chart_item, cr, &geom);
1720 else if (is_histogram_chart (chart_item))
1721 xrchart_draw_histogram (chart_item, cr, &geom);
1722 else if (is_np_plot_chart (chart_item))
1723 xrchart_draw_np_plot (chart_item, cr, &geom);
1724 else if (is_piechart (chart_item))
1725 xrchart_draw_piechart (chart_item, cr, &geom);
1726 else if (is_barchart (chart_item))
1727 xrchart_draw_barchart (chart_item, cr, &geom);
1728 else if (is_roc_chart (chart_item))
1729 xrchart_draw_roc (chart_item, cr, &geom);
1730 else if (is_scree (chart_item))
1731 xrchart_draw_scree (chart_item, cr, &geom);
1732 else if (is_spreadlevel_plot_chart (chart_item))
1733 xrchart_draw_spreadlevel (chart_item, cr, &geom);
1734 else if (is_scatterplot_chart (chart_item))
1735 xrchart_draw_scatterplot (chart_item, cr, &geom);
1738 xrchart_geometry_free (cr, &geom);
1744 xr_draw_png_chart (const struct chart_item *item,
1745 const char *file_name_template, int number,
1746 const struct cell_color *fg,
1747 const struct cell_color *bg)
1749 const int width = 640;
1750 const int length = 480;
1752 cairo_surface_t *surface;
1753 cairo_status_t status;
1754 const char *number_pos;
1758 number_pos = strchr (file_name_template, '#');
1759 if (number_pos != NULL)
1760 file_name = xasprintf ("%.*s%d%s.png", (int) (number_pos - file_name_template),
1761 file_name_template, number, number_pos + 1);
1763 file_name = xasprintf ("%s.png", file_name_template);
1765 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1766 cr = cairo_create (surface);
1768 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1771 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1773 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1775 status = cairo_surface_write_to_png (surface, file_name);
1776 if (status != CAIRO_STATUS_SUCCESS)
1777 msg (ME, _("error writing output file `%s': %s"),
1778 file_name, cairo_status_to_string (status));
1781 cairo_surface_destroy (surface);
1788 xr_draw_eps_chart (const struct chart_item *item,
1789 const char *file_name_template, int number,
1790 const struct cell_color *fg,
1791 const struct cell_color *bg)
1793 const int width = 640;
1794 const int length = 480;
1796 cairo_surface_t *surface;
1797 const char *number_pos;
1801 number_pos = strchr (file_name_template, '#');
1802 if (number_pos != NULL)
1803 file_name = xasprintf ("%.*s%d%s.eps", (int) (number_pos - file_name_template),
1804 file_name_template, number, number_pos + 1);
1806 file_name = xasprintf ("%s.eps", file_name_template);
1808 surface = cairo_ps_surface_create (file_name, width, length);
1809 cairo_ps_surface_set_eps (surface, true);
1810 cr = cairo_create (surface);
1812 cairo_set_source_rgb (cr, bg->r / 255.0, bg->g / 255.0, bg->b / 255.0);
1815 cairo_set_source_rgb (cr, fg->r / 255.0, fg->g / 255.0, fg->b / 255.0);
1817 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1820 cairo_surface_destroy (surface);
1827 struct xr_table_state
1829 struct xr_render_fsm fsm;
1830 struct render_pager *p;
1834 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1836 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1838 while (render_pager_has_next (ts->p))
1842 used = render_pager_draw_next (ts->p, xr->length - xr->y);
1855 xr_table_destroy (struct xr_render_fsm *fsm)
1857 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1859 render_pager_destroy (ts->p);
1863 static struct xr_render_fsm *
1864 xr_render_table (struct xr_driver *xr, struct table_item *table_item)
1866 struct xr_table_state *ts;
1868 ts = xmalloc (sizeof *ts);
1869 ts->fsm.render = xr_table_render;
1870 ts->fsm.destroy = xr_table_destroy;
1873 xr->y += xr->char_height;
1875 ts->p = render_pager_create (xr->params, table_item);
1876 table_item_unref (table_item);
1881 struct xr_chart_state
1883 struct xr_render_fsm fsm;
1884 struct chart_item *chart_item;
1888 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1890 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1892 const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
1894 if (xr->y > xr->length - chart_height)
1897 if (xr->cairo != NULL)
1899 xr_draw_chart (cs->chart_item, xr->cairo,
1902 xr_to_pt (xr->width),
1903 xr_to_pt (chart_height));
1905 xr->y += chart_height;
1911 xr_chart_destroy (struct xr_render_fsm *fsm)
1913 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1915 chart_item_unref (cs->chart_item);
1919 static struct xr_render_fsm *
1920 xr_render_chart (const struct chart_item *chart_item)
1922 struct xr_chart_state *cs;
1924 cs = xmalloc (sizeof *cs);
1925 cs->fsm.render = xr_chart_render;
1926 cs->fsm.destroy = xr_chart_destroy;
1927 cs->chart_item = chart_item_ref (chart_item);
1933 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1939 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1941 /* Nothing to do. */
1944 static struct xr_render_fsm *
1945 xr_render_eject (void)
1947 static struct xr_render_fsm eject_renderer =
1953 return &eject_renderer;
1956 static struct xr_render_fsm *
1957 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1959 enum text_item_type type = text_item_get_type (text_item);
1963 case TEXT_ITEM_PAGE_TITLE:
1967 return xr_render_table (
1968 xr, text_item_to_table_item (text_item_ref (text_item)));
1974 static struct xr_render_fsm *
1975 xr_render_message (struct xr_driver *xr,
1976 const struct message_item *message_item)
1978 char *s = msg_to_string (message_item_get_msg (message_item));
1979 struct text_item *item = text_item_create (TEXT_ITEM_LOG, s);
1981 return xr_render_table (xr, text_item_to_table_item (item));
1984 static struct xr_render_fsm *
1985 xr_render_output_item (struct xr_driver *xr,
1986 const struct output_item *output_item)
1988 if (is_table_item (output_item))
1989 return xr_render_table (xr, table_item_ref (to_table_item (output_item)));
1990 else if (is_chart_item (output_item))
1991 return xr_render_chart (to_chart_item (output_item));
1992 else if (is_text_item (output_item))
1993 return xr_render_text (xr, to_text_item (output_item));
1994 else if (is_page_eject_item (output_item))
1995 return xr->y > 0 ? xr_render_eject () : NULL;
1996 else if (is_message_item (output_item))
1997 return xr_render_message (xr, to_message_item (output_item));
2003 xr_draw_svg_file (struct xr_rendering *r,
2004 const char *filename)
2008 xr_rendering_measure (r, &width, &height);
2009 cairo_surface_t *surface = cairo_svg_surface_create (filename, width, height);
2012 g_error ("Could not create cairo svg surface with file %s", filename);
2015 cairo_t *cr = cairo_create (surface);
2016 xr_rendering_draw (r, cr, 0, 0, width, height);
2018 cairo_surface_destroy (surface);