1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "output/cairo.h"
21 #include "libpspp/assertion.h"
22 #include "libpspp/cast.h"
23 #include "libpspp/message.h"
24 #include "libpspp/pool.h"
25 #include "libpspp/start-date.h"
26 #include "libpspp/str.h"
27 #include "libpspp/string-map.h"
28 #include "libpspp/version.h"
29 #include "data/file-handle-def.h"
30 #include "output/cairo-chart.h"
31 #include "output/chart-item-provider.h"
32 #include "output/charts/boxplot.h"
33 #include "output/charts/np-plot.h"
34 #include "output/charts/piechart.h"
35 #include "output/charts/barchart.h"
36 #include "output/charts/plot-hist.h"
37 #include "output/charts/roc-chart.h"
38 #include "output/charts/spreadlevel-plot.h"
39 #include "output/charts/scree.h"
40 #include "output/charts/scatterplot.h"
41 #include "output/driver-provider.h"
42 #include "output/message-item.h"
43 #include "output/options.h"
44 #include "output/render.h"
45 #include "output/tab.h"
46 #include "output/table-item.h"
47 #include "output/table.h"
48 #include "output/text-item.h"
50 #include <cairo/cairo-pdf.h>
51 #include <cairo/cairo-ps.h>
52 #include <cairo/cairo-svg.h>
53 #include <cairo/cairo.h>
55 #include <pango/pango-font.h>
56 #include <pango/pango-layout.h>
57 #include <pango/pango.h>
58 #include <pango/pangocairo.h>
61 #include "gl/c-strcase.h"
62 #include "gl/intprops.h"
63 #include "gl/minmax.h"
64 #include "gl/xalloc.h"
67 #define _(msgid) gettext (msgid)
69 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
73 /* The unit used for internal measurements is inch/(72 * XR_POINT). */
74 #define XR_POINT PANGO_SCALE
76 /* Conversions to and from points. */
80 return x / (double) XR_POINT;
83 /* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
87 return x * (PANGO_SCALE * 72 / 96);
101 XR_FONT_PROPORTIONAL,
107 /* A font for use with Cairo. */
110 PangoFontDescription *desc;
114 /* An output item whose rendering is in progress. */
117 /* Renders as much of itself as it can on the current page. Returns true
118 if rendering is complete, false if the output item needs another
120 bool (*render) (struct xr_render_fsm *, struct xr_driver *);
122 /* Destroys the output item. */
123 void (*destroy) (struct xr_render_fsm *);
126 /* Cairo output driver. */
129 struct output_driver driver;
131 /* User parameters. */
132 struct xr_font fonts[XR_N_FONTS];
134 int width; /* Page width minus margins. */
135 int length; /* Page length minus margins and header. */
137 int left_margin; /* Left margin in inch/(72 * XR_POINT). */
138 int right_margin; /* Right margin in inch/(72 * XR_POINT). */
139 int top_margin; /* Top margin in inch/(72 * XR_POINT). */
140 int bottom_margin; /* Bottom margin in inch/(72 * XR_POINT). */
142 int line_space; /* Space between lines. */
143 int line_width; /* Width of lines. */
145 int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
147 struct xr_color bg; /* Background color */
148 struct xr_color fg; /* Foreground color */
150 /* Internal state. */
151 struct render_params *params;
152 int char_width, char_height;
157 int page_number; /* Current page number. */
159 struct xr_render_fsm *fsm;
163 static const struct output_driver_class cairo_driver_class;
165 static void xr_driver_destroy_fsm (struct xr_driver *);
166 static void xr_driver_run_fsm (struct xr_driver *);
168 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
169 enum render_line_style styles[TABLE_N_AXES][2]);
170 static void xr_measure_cell_width (void *, const struct table_cell *,
172 static int xr_measure_cell_height (void *, const struct table_cell *,
174 static void xr_draw_cell (void *, const struct table_cell *,
175 int bb[TABLE_N_AXES][2],
176 int spill[TABLE_N_AXES][2],
177 int clip[TABLE_N_AXES][2]);
178 static int xr_adjust_break (void *, const struct table_cell *,
179 int width, int height);
181 static struct xr_render_fsm *xr_render_output_item (
182 struct xr_driver *, const struct output_item *);
184 /* Output driver basics. */
186 static struct xr_driver *
187 xr_driver_cast (struct output_driver *driver)
189 assert (driver->class == &cairo_driver_class);
190 return UP_CAST (driver, struct xr_driver, driver);
193 static struct driver_option *
194 opt (struct output_driver *d, struct string_map *options, const char *key,
195 const char *default_value)
197 return driver_option_get (d, options, key, default_value);
200 /* Parse color information specified by KEY into {RED,GREEN,BLUE}.
201 Currently, the input string must be of the form "#RRRRGGGGBBBB"
202 Future implementations might allow things like "yellow" and
203 "sky-blue-ultra-brown"
206 parse_color (struct output_driver *d, struct string_map *options,
207 const char *key, const char *default_value,
208 struct xr_color *color)
210 int red, green, blue;
211 char *string = parse_string (opt (d, options, key, default_value));
213 if (3 != sscanf (string, "#%04x%04x%04x", &red, &green, &blue))
215 /* If the parsed option string fails, then try the default value */
216 if ( 3 != sscanf (default_value, "#%04x%04x%04x", &red, &green, &blue))
218 /* ... and if that fails set everything to zero */
219 red = green = blue = 0;
225 /* Convert 16 bit ints to float */
226 color->red = red / (double) 0xFFFF;
227 color->green = green / (double) 0xFFFF;
228 color->blue = blue / (double) 0xFFFF;
231 static PangoFontDescription *
232 parse_font (const char *font, int default_size, bool bold, bool italic)
234 if (!c_strcasecmp (font, "Monospaced"))
237 PangoFontDescription *desc = pango_font_description_from_string (font);
241 /* If the font description didn't include an explicit font size, then set it
242 to DEFAULT_SIZE, which is in inch/72000 units. */
243 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
244 pango_font_description_set_size (desc,
245 (default_size / 1000.0) * PANGO_SCALE);
247 pango_font_description_set_weight (desc, (bold
249 : PANGO_WEIGHT_NORMAL));
250 pango_font_description_set_style (desc, (italic
252 : PANGO_STYLE_NORMAL));
257 static PangoFontDescription *
258 parse_font_option (struct output_driver *d, struct string_map *options,
259 const char *key, const char *default_value,
260 int default_size, bool bold, bool italic)
262 char *string = parse_string (opt (d, options, key, default_value));
263 PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
266 msg (MW, _("`%s': bad font specification"), string);
268 /* Fall back to DEFAULT_VALUE, which had better be a valid font
270 desc = parse_font (default_value, default_size, bold, italic);
271 assert (desc != NULL);
279 apply_options (struct xr_driver *xr, struct string_map *o)
281 struct output_driver *d = &xr->driver;
283 /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
284 int left_margin, right_margin;
285 int top_margin, bottom_margin;
286 int paper_width, paper_length;
288 int min_break[TABLE_N_AXES];
290 /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
291 const double scale = XR_POINT / 1000.;
295 for (i = 0; i < XR_N_FONTS; i++)
297 struct xr_font *font = &xr->fonts[i];
299 if (font->desc != NULL)
300 pango_font_description_free (font->desc);
303 font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
304 xr->fonts[XR_FONT_FIXED].desc = parse_font_option
305 (d, o, "fixed-font", "monospace", font_size, false, false);
306 xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font_option (
307 d, o, "prop-font", "sans serif", font_size, false, false);
308 xr->fonts[XR_FONT_EMPHASIS].desc = parse_font_option (
309 d, o, "emph-font", "sans serif", font_size, false, true);
311 xr->line_space = XR_POINT;
312 xr->line_width = XR_POINT / 2;
315 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg);
316 parse_color (d, o, "foreground-color", "#000000000000", &xr->fg);
318 /* Get dimensions. */
319 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
320 left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
321 right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
322 top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
323 bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
325 min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
326 min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
328 /* Convert to inch/(XR_POINT * 72). */
329 xr->left_margin = left_margin * scale;
330 xr->right_margin = right_margin * scale;
331 xr->top_margin = top_margin * scale;
332 xr->bottom_margin = bottom_margin * scale;
333 xr->width = (paper_width - left_margin - right_margin) * scale;
334 xr->length = (paper_length - top_margin - bottom_margin) * scale;
335 xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
336 xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
339 static struct xr_driver *
340 xr_allocate (const char *name, int device_type, struct string_map *o)
342 struct xr_driver *xr = xzalloc (sizeof *xr);
343 struct output_driver *d = &xr->driver;
345 output_driver_init (d, &cairo_driver_class, name, device_type);
347 apply_options (xr, o);
353 pango_to_xr (int pango)
355 return (XR_POINT != PANGO_SCALE
356 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
363 return (XR_POINT != PANGO_SCALE
364 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
369 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
375 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
379 for (i = 0; i < XR_N_FONTS; i++)
381 struct xr_font *font = &xr->fonts[i];
382 int char_width, char_height;
384 font->layout = pango_cairo_create_layout (cairo);
385 pango_layout_set_font_description (font->layout, font->desc);
387 pango_layout_set_text (font->layout, "0", 1);
388 pango_layout_get_size (font->layout, &char_width, &char_height);
389 xr->char_width = MAX (xr->char_width, pango_to_xr (char_width));
390 xr->char_height = MAX (xr->char_height, pango_to_xr (char_height));
393 if (xr->params == NULL)
395 xr->params = xmalloc (sizeof *xr->params);
396 xr->params->draw_line = xr_draw_line;
397 xr->params->measure_cell_width = xr_measure_cell_width;
398 xr->params->measure_cell_height = xr_measure_cell_height;
399 xr->params->adjust_break = xr_adjust_break;
400 xr->params->draw_cell = xr_draw_cell;
401 xr->params->aux = xr;
402 xr->params->size[H] = xr->width;
403 xr->params->size[V] = xr->length;
404 xr->params->font_size[H] = xr->char_width;
405 xr->params->font_size[V] = xr->char_height;
407 int lw = xr->line_width;
408 int ls = xr->line_space;
409 for (i = 0; i < TABLE_N_AXES; i++)
411 xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
412 xr->params->line_widths[i][RENDER_LINE_SINGLE] = lw;
413 xr->params->line_widths[i][RENDER_LINE_DASHED] = lw;
414 xr->params->line_widths[i][RENDER_LINE_THICK] = lw * 2;
415 xr->params->line_widths[i][RENDER_LINE_THIN] = lw / 2;
416 xr->params->line_widths[i][RENDER_LINE_DOUBLE] = 2 * lw + ls;
419 for (i = 0; i < TABLE_N_AXES; i++)
420 xr->params->min_break[i] = xr->min_break[i];
421 xr->params->supports_margins = true;
424 cairo_set_source_rgb (xr->cairo, xr->fg.red, xr->fg.green, xr->fg.blue);
429 static struct output_driver *
430 xr_create (const char *file_name, enum settings_output_devices device_type,
431 struct string_map *o, enum xr_output_type file_type)
433 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
434 struct xr_driver *xr;
435 cairo_surface_t *surface;
436 cairo_status_t status;
437 double width_pt, length_pt;
439 xr = xr_allocate (file_name, device_type, o);
441 width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
442 length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
443 if (file_type == XR_PDF)
444 surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
445 else if (file_type == XR_PS)
446 surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
447 else if (file_type == XR_SVG)
448 surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
452 status = cairo_surface_status (surface);
453 if (status != CAIRO_STATUS_SUCCESS)
455 msg (ME, _("error opening output file `%s': %s"),
456 file_name, cairo_status_to_string (status));
457 cairo_surface_destroy (surface);
461 xr->cairo = cairo_create (surface);
462 cairo_surface_destroy (surface);
464 if (!xr_set_cairo (xr, xr->cairo))
467 cairo_save (xr->cairo);
468 xr_driver_next_page (xr, xr->cairo);
470 if (xr->width / xr->char_width < MIN_WIDTH)
472 msg (ME, _("The defined page is not wide enough to hold at least %d "
473 "characters in the default font. In fact, there's only "
474 "room for %d characters."),
476 xr->width / xr->char_width);
480 if (xr->length / xr->char_height < MIN_LENGTH)
482 msg (ME, _("The defined page is not long enough to hold at least %d "
483 "lines in the default font. In fact, there's only "
484 "room for %d lines."),
486 xr->length / xr->char_height);
493 output_driver_destroy (&xr->driver);
497 static struct output_driver *
498 xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
499 struct string_map *o)
501 struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_PDF);
506 static struct output_driver *
507 xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
508 struct string_map *o)
510 struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_PS);
515 static struct output_driver *
516 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
517 struct string_map *o)
519 struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_SVG);
525 xr_destroy (struct output_driver *driver)
527 struct xr_driver *xr = xr_driver_cast (driver);
530 xr_driver_destroy_fsm (xr);
532 if (xr->cairo != NULL)
534 cairo_status_t status;
536 cairo_surface_finish (cairo_get_target (xr->cairo));
537 status = cairo_status (xr->cairo);
538 if (status != CAIRO_STATUS_SUCCESS)
539 msg (ME, _("error drawing output for %s driver: %s"),
540 output_driver_get_name (driver),
541 cairo_status_to_string (status));
542 cairo_destroy (xr->cairo);
545 for (i = 0; i < XR_N_FONTS; i++)
547 struct xr_font *font = &xr->fonts[i];
549 if (font->desc != NULL)
550 pango_font_description_free (font->desc);
551 if (font->layout != NULL)
552 g_object_unref (font->layout);
560 xr_flush (struct output_driver *driver)
562 struct xr_driver *xr = xr_driver_cast (driver);
564 cairo_surface_flush (cairo_get_target (xr->cairo));
568 xr_submit (struct output_driver *driver, const struct output_item *output_item)
570 struct xr_driver *xr = xr_driver_cast (driver);
572 xr_driver_output_item (xr, output_item);
573 while (xr_driver_need_new_page (xr))
575 cairo_restore (xr->cairo);
576 cairo_show_page (xr->cairo);
577 cairo_save (xr->cairo);
578 xr_driver_next_page (xr, xr->cairo);
582 /* Functions for rendering a series of output items to a series of Cairo
583 contexts, with pagination.
585 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
586 its underlying implementation.
588 See the big comment in cairo.h for intended usage. */
590 /* Gives new page CAIRO to XR for output. */
592 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
595 cairo_set_source_rgb (cairo, xr->bg.red, xr->bg.green, xr->bg.blue);
596 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
598 cairo_restore (cairo);
600 cairo_translate (cairo,
601 xr_to_pt (xr->left_margin),
602 xr_to_pt (xr->top_margin));
607 xr_driver_run_fsm (xr);
610 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
611 rendering a previous output item, that is, only if xr_driver_need_new_page()
614 xr_driver_output_item (struct xr_driver *xr,
615 const struct output_item *output_item)
617 assert (xr->fsm == NULL);
618 xr->fsm = xr_render_output_item (xr, output_item);
619 xr_driver_run_fsm (xr);
622 /* Returns true if XR is in the middle of rendering an output item and needs a
623 new page to be appended using xr_driver_next_page() to make progress,
626 xr_driver_need_new_page (const struct xr_driver *xr)
628 return xr->fsm != NULL;
631 /* Returns true if the current page doesn't have any content yet. */
633 xr_driver_is_page_blank (const struct xr_driver *xr)
639 xr_driver_destroy_fsm (struct xr_driver *xr)
643 xr->fsm->destroy (xr->fsm);
649 xr_driver_run_fsm (struct xr_driver *xr)
651 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
652 xr_driver_destroy_fsm (xr);
656 xr_layout_cell (struct xr_driver *, const struct table_cell *,
657 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
658 int *width, int *height, int *brk);
661 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style)
663 cairo_new_path (xr->cairo);
664 cairo_set_line_width (
666 xr_to_pt (style == RENDER_LINE_THICK ? xr->line_width * 2
667 : style == RENDER_LINE_THIN ? xr->line_width / 2
669 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
670 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
671 cairo_stroke (xr->cairo);
675 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
677 cairo_new_path (xr->cairo);
678 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
679 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
680 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y0 + xr->y));
681 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
682 cairo_line_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y1 + xr->y));
683 cairo_close_path (xr->cairo);
684 cairo_stroke (xr->cairo);
688 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
690 cairo_new_path (xr->cairo);
691 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
692 cairo_rectangle (xr->cairo,
693 xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y),
694 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
695 cairo_fill (xr->cairo);
698 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
699 shortening it to X0...X1 if SHORTEN is true.
700 Draws a horizontal line X1...X3 at Y if RIGHT says so,
701 shortening it to X2...X3 if SHORTEN is true. */
703 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
704 enum render_line_style left, enum render_line_style right,
707 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
708 dump_line (xr, x0, y, x3, y, left);
711 if (left != RENDER_LINE_NONE)
712 dump_line (xr, x0, y, shorten ? x1 : x2, y, left);
713 if (right != RENDER_LINE_NONE)
714 dump_line (xr, shorten ? x2 : x1, y, x3, y, right);
718 /* Draws a vertical line Y0...Y2 at X if TOP says so,
719 shortening it to Y0...Y1 if SHORTEN is true.
720 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
721 shortening it to Y2...Y3 if SHORTEN is true. */
723 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
724 enum render_line_style top, enum render_line_style bottom,
727 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
728 dump_line (xr, x, y0, x, y3, top);
731 if (top != RENDER_LINE_NONE)
732 dump_line (xr, x, y0, x, shorten ? y1 : y2, top);
733 if (bottom != RENDER_LINE_NONE)
734 dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom);
739 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
740 enum render_line_style styles[TABLE_N_AXES][2])
742 const int x0 = bb[H][0];
743 const int y0 = bb[V][0];
744 const int x3 = bb[H][1];
745 const int y3 = bb[V][1];
746 const int top = styles[H][0];
747 const int bottom = styles[H][1];
748 const int start_of_line = render_direction_rtl() ? styles[V][1]: styles[V][0];
749 const int end_of_line = render_direction_rtl() ? styles[V][0]: styles[V][1];
751 /* The algorithm here is somewhat subtle, to allow it to handle
752 all the kinds of intersections that we need.
754 Three additional ordinates are assigned along the x axis. The
755 first is xc, midway between x0 and x3. The others are x1 and
756 x2; for a single vertical line these are equal to xc, and for
757 a double vertical line they are the ordinates of the left and
758 right half of the double line.
760 yc, y1, and y2 are assigned similarly along the y axis.
762 The following diagram shows the coordinate system and output
763 for double top and bottom lines, single left line, and no
767 y0 ________________________
773 y1 = y2 = yc |######### # |
778 y3 |________#_____#_______|
780 struct xr_driver *xr = xr_;
782 /* Offset from center of each line in a pair of double lines. */
783 int double_line_ofs = (xr->line_space + xr->line_width) / 2;
785 /* Are the lines along each axis single or double?
786 (It doesn't make sense to have different kinds of line on the
787 same axis, so we don't try to gracefully handle that case.) */
788 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
789 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
791 /* When horizontal lines are doubled,
792 the left-side line along y1 normally runs from x0 to x2,
793 and the right-side line along y1 from x3 to x1.
794 If the top-side line is also doubled, we shorten the y1 lines,
795 so that the left-side line runs only to x1,
796 and the right-side line only to x2.
797 Otherwise, the horizontal line at y = y1 below would cut off
798 the intersection, which looks ugly:
800 y0 ________________________
805 y1 |######### ########|
808 y2 |######################|
811 y3 |______________________|
812 It is more of a judgment call when the horizontal line is
813 single. We actually choose to cut off the line anyhow, as
814 shown in the first diagram above.
816 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
817 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
818 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
819 int horz_line_ofs = double_vert ? double_line_ofs : 0;
820 int xc = (x0 + x3) / 2;
821 int x1 = xc - horz_line_ofs;
822 int x2 = xc + horz_line_ofs;
824 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
825 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
826 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
827 int vert_line_ofs = double_horz ? double_line_ofs : 0;
828 int yc = (y0 + y3) / 2;
829 int y1 = yc - vert_line_ofs;
830 int y2 = yc + vert_line_ofs;
833 horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line, shorten_yc_line);
836 horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line, shorten_y1_lines);
837 horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line, shorten_y2_lines);
841 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
844 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
845 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
850 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
851 int *min_width, int *max_width)
853 struct xr_driver *xr = xr_;
854 int bb[TABLE_N_AXES][2];
855 int clip[TABLE_N_AXES][2];
862 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
863 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
866 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
869 *min_width += px_to_xr (cell->style->margin[H][0]
870 + cell->style->margin[H][1]);
872 *max_width += px_to_xr (cell->style->margin[H][0]
873 + cell->style->margin[H][1]);
877 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
879 struct xr_driver *xr = xr_;
880 int bb[TABLE_N_AXES][2];
881 int clip[TABLE_N_AXES][2];
885 bb[H][1] = width - px_to_xr (cell->style->margin[H][0]
886 + cell->style->margin[H][1]);
889 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
890 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
891 h += px_to_xr (cell->style->margin[V][0] + cell->style->margin[V][1]);
895 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
898 xr_draw_cell (void *xr_, const struct table_cell *cell,
899 int bb[TABLE_N_AXES][2],
900 int spill[TABLE_N_AXES][2],
901 int clip[TABLE_N_AXES][2])
903 struct xr_driver *xr = xr_;
906 cairo_save (xr->cairo);
907 int bg_clip[TABLE_N_AXES][2];
908 for (int axis = 0; axis < TABLE_N_AXES; axis++)
910 bg_clip[axis][0] = clip[axis][0];
911 if (bb[axis][0] == clip[axis][0])
912 bg_clip[axis][0] -= spill[axis][0];
914 bg_clip[axis][1] = clip[axis][1];
915 if (bb[axis][1] == clip[axis][1])
916 bg_clip[axis][1] += spill[axis][1];
918 xr_clip (xr, bg_clip);
919 cairo_set_source_rgb (xr->cairo,
920 cell->style->bg.r / 255.,
921 cell->style->bg.g / 255.,
922 cell->style->bg.b / 255.);
924 bb[H][0] - spill[H][0],
925 bb[V][0] - spill[V][0],
926 bb[H][1] + spill[H][1],
927 bb[V][1] + spill[V][1]);
928 cairo_restore (xr->cairo);
930 cairo_save (xr->cairo);
931 cairo_set_source_rgb (xr->cairo,
932 cell->style->fg.r / 255.,
933 cell->style->fg.g / 255.,
934 cell->style->fg.b / 255.);
936 for (int axis = 0; axis < TABLE_N_AXES; axis++)
938 bb[axis][0] += px_to_xr (cell->style->margin[axis][0]);
939 bb[axis][1] -= px_to_xr (cell->style->margin[axis][1]);
941 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
942 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
943 cairo_restore (xr->cairo);
947 xr_adjust_break (void *xr_, const struct table_cell *cell,
948 int width, int height)
950 struct xr_driver *xr = xr_;
951 int bb[TABLE_N_AXES][2];
952 int clip[TABLE_N_AXES][2];
955 if (xr_measure_cell_height (xr_, cell, width) < height)
959 bb[H][1] = width - px_to_xr (cell->style->margin[H][0]
960 + cell->style->margin[H][1]);
964 bb[V][1] = height - px_to_xr (cell->style->margin[V][0]
965 + cell->style->margin[V][1]);
966 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
967 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
972 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
974 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
976 double x0 = xr_to_pt (clip[H][0] + xr->x);
977 double y0 = xr_to_pt (clip[V][0] + xr->y);
978 double x1 = xr_to_pt (clip[H][1] + xr->x);
979 double y1 = xr_to_pt (clip[V][1] + xr->y);
981 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
982 cairo_clip (xr->cairo);
987 add_attr_with_start (PangoAttrList *list, PangoAttribute *attr, guint start_index)
989 attr->start_index = start_index;
990 pango_attr_list_insert (list, attr);
994 xr_layout_cell_text (struct xr_driver *xr,
995 const struct cell_contents *contents,
996 const struct cell_style *style,
997 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
998 int *widthp, int *brk)
1000 unsigned int options = contents->options;
1004 struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1005 : options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
1006 : &xr->fonts[XR_FONT_PROPORTIONAL]);
1007 struct xr_font local_font;
1010 PangoFontDescription *desc = parse_font (
1012 style->font_size ? style->font_size * 1000 * 72 / 128 : 10000,
1013 style->bold, style->italic);
1016 PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1017 pango_layout_set_font_description (layout, desc);
1019 local_font.desc = desc;
1020 local_font.layout = layout;
1025 int footnote_adjustment;
1026 if (contents->n_footnotes == 0)
1027 footnote_adjustment = 0;
1028 else if (contents->n_footnotes == 1 && (options & TAB_HALIGN) == TAB_RIGHT)
1030 PangoAttrList *attrs;
1032 const char *marker = contents->footnotes[0]->marker;
1033 pango_layout_set_text (font->layout, marker, strlen (marker));
1035 attrs = pango_attr_list_new ();
1036 pango_attr_list_insert (attrs, pango_attr_rise_new (7000));
1037 pango_layout_set_attributes (font->layout, attrs);
1038 pango_attr_list_unref (attrs);
1040 pango_layout_get_size (font->layout, &w, &h);
1041 footnote_adjustment = MIN (w, px_to_xr (style->margin[H][1]));
1044 footnote_adjustment = px_to_xr (style->margin[H][1]);
1046 length = strlen (contents->text);
1047 if (footnote_adjustment)
1049 PangoAttrList *attrs;
1052 bb[H][1] += footnote_adjustment;
1055 ds_extend (&s, length + contents->n_footnotes * 10);
1056 ds_put_cstr (&s, contents->text);
1057 cell_contents_format_footnote_markers (contents, &s);
1058 pango_layout_set_text (font->layout, ds_cstr (&s), ds_length (&s));
1061 attrs = pango_attr_list_new ();
1062 if (style->underline)
1063 pango_attr_list_insert (attrs, pango_attr_underline_new (
1064 PANGO_UNDERLINE_SINGLE));
1065 add_attr_with_start (attrs, pango_attr_rise_new (7000), length);
1066 add_attr_with_start (
1067 attrs, pango_attr_font_desc_new (font->desc), length);
1068 pango_layout_set_attributes (font->layout, attrs);
1069 pango_attr_list_unref (attrs);
1073 pango_layout_set_text (font->layout, contents->text, -1);
1075 if (style->underline)
1077 PangoAttrList *attrs = pango_attr_list_new ();
1078 pango_attr_list_insert (attrs, pango_attr_underline_new (
1079 PANGO_UNDERLINE_SINGLE));
1080 pango_layout_set_attributes (font->layout, attrs);
1081 pango_attr_list_unref (attrs);
1085 pango_layout_set_alignment (
1087 ((options & TAB_HALIGN) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
1088 : (options & TAB_HALIGN) == TAB_LEFT ? PANGO_ALIGN_LEFT
1089 : PANGO_ALIGN_CENTER));
1090 pango_layout_set_width (
1092 bb[H][1] == INT_MAX ? -1 : xr_to_pango (bb[H][1] - bb[H][0]));
1093 pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1095 if (clip[H][0] != clip[H][1])
1097 cairo_save (xr->cairo);
1099 cairo_translate (xr->cairo,
1100 xr_to_pt (bb[H][0] + xr->x),
1101 xr_to_pt (bb[V][0] + xr->y));
1102 pango_cairo_show_layout (xr->cairo, font->layout);
1104 /* If enabled, this draws a blue rectangle around the extents of each
1105 line of text, which can be rather useful for debugging layout
1109 PangoLayoutIter *iter;
1110 iter = pango_layout_get_iter (font->layout);
1113 PangoRectangle extents;
1115 pango_layout_iter_get_line_extents (iter, &extents, NULL);
1116 cairo_save (xr->cairo);
1117 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1119 pango_to_xr (extents.x) - xr->x,
1120 pango_to_xr (extents.y) - xr->y,
1121 pango_to_xr (extents.x + extents.width) - xr->x,
1122 pango_to_xr (extents.y + extents.height) - xr->y);
1123 cairo_restore (xr->cairo);
1125 while (pango_layout_iter_next_line (iter));
1126 pango_layout_iter_free (iter);
1129 cairo_restore (xr->cairo);
1132 pango_layout_get_size (font->layout, &w, &h);
1133 w = pango_to_xr (w);
1134 h = pango_to_xr (h);
1137 if (bb[V][0] + h >= bb[V][1])
1139 PangoLayoutIter *iter;
1140 int best UNUSED = 0;
1142 /* Choose a breakpoint between lines instead of in the middle of one. */
1143 iter = pango_layout_get_iter (font->layout);
1146 PangoRectangle extents;
1150 pango_layout_iter_get_line_extents (iter, NULL, &extents);
1151 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1152 extents.x = pango_to_xr (extents.x);
1153 extents.y = pango_to_xr (y0);
1154 extents.width = pango_to_xr (extents.width);
1155 extents.height = pango_to_xr (y1 - y0);
1156 bottom = bb[V][0] + extents.y + extents.height;
1157 if (bottom < bb[V][1])
1159 if (brk && clip[H][0] != clip[H][1])
1167 while (pango_layout_iter_next_line (iter));
1168 pango_layout_iter_free (iter);
1170 /* If enabled, draws a green line across the chosen breakpoint, which can
1171 be useful for debugging issues with breaking. */
1174 if (best && !xr->nest)
1176 cairo_save (xr->cairo);
1177 cairo_set_source_rgb (xr->cairo, 0, 1, 0);
1178 dump_line (xr, -xr->left_margin, best,
1179 xr->width + xr->right_margin, best,
1180 RENDER_LINE_SINGLE);
1181 cairo_restore (xr->cairo);
1186 pango_layout_set_attributes (font->layout, NULL);
1188 if (font == &local_font)
1190 g_object_unref (G_OBJECT (font->layout));
1191 pango_font_description_free (font->desc);
1194 return bb[V][0] + h;
1198 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1199 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1200 int *width, int *height, int *brk)
1202 int bb[TABLE_N_AXES][2];
1210 memcpy (bb, bb_, sizeof bb);
1212 /* If enabled, draws a blue rectangle around the cell extents, which can be
1213 useful for debugging layout. */
1216 if (clip[H][0] != clip[H][1])
1218 int offset = (xr->nest) * XR_POINT;
1220 cairo_save (xr->cairo);
1221 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1223 bb[H][0] + offset, bb[V][0] + offset,
1224 bb[H][1] - offset, bb[V][1] - offset);
1225 cairo_restore (xr->cairo);
1229 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
1231 const struct cell_contents *contents = &cell->contents[i];
1237 bb[V][0] += xr->char_height / 2;
1238 if (bb[V][0] >= bb[V][1])
1244 bb[V][0] = xr_layout_cell_text (xr, contents, cell->style, bb, clip,
1247 *height = bb[V][0] - bb_[V][0];
1250 struct output_driver_factory pdf_driver_factory =
1251 { "pdf", "pspp.pdf", xr_pdf_create };
1252 struct output_driver_factory ps_driver_factory =
1253 { "ps", "pspp.ps", xr_ps_create };
1254 struct output_driver_factory svg_driver_factory =
1255 { "svg", "pspp.svg", xr_svg_create };
1257 static const struct output_driver_class cairo_driver_class =
1265 /* GUI rendering helpers. */
1269 struct output_item *item;
1272 struct render_pager *p;
1273 struct xr_driver *xr;
1276 #define CHART_WIDTH 500
1277 #define CHART_HEIGHT 375
1282 xr_driver_create (cairo_t *cairo, struct string_map *options)
1284 struct xr_driver *xr = xr_allocate ("cairo", 0, options);
1285 if (!xr_set_cairo (xr, cairo))
1287 output_driver_destroy (&xr->driver);
1293 /* Destroy XR, which should have been created with xr_driver_create(). Any
1294 cairo_t added to XR is not destroyed, because it is owned by the client. */
1296 xr_driver_destroy (struct xr_driver *xr)
1301 output_driver_destroy (&xr->driver);
1305 static struct xr_rendering *
1306 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1308 struct table_item *table_item;
1309 struct xr_rendering *r;
1311 table_item = table_item_create (table_from_string (TAB_LEFT, text),
1313 r = xr_rendering_create (xr, &table_item->output_item, cr);
1314 table_item_unref (table_item);
1320 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1322 if (is_table_item (xr->item))
1323 apply_options (xr->xr, o);
1326 struct xr_rendering *
1327 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1330 struct xr_rendering *r = NULL;
1332 if (is_text_item (item))
1333 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1335 else if (is_message_item (item))
1337 const struct message_item *message_item = to_message_item (item);
1338 const struct msg *msg = message_item_get_msg (message_item);
1339 char *s = msg_to_string (msg, NULL);
1340 r = xr_rendering_create_text (xr, s, cr);
1343 else if (is_table_item (item))
1345 r = xzalloc (sizeof *r);
1346 r->item = output_item_ref (item);
1348 xr_set_cairo (xr, cr);
1349 r->p = render_pager_create (xr->params, to_table_item (item));
1351 else if (is_chart_item (item))
1353 r = xzalloc (sizeof *r);
1354 r->item = output_item_ref (item);
1361 xr_rendering_destroy (struct xr_rendering *r)
1365 output_item_unref (r->item);
1366 render_pager_destroy (r->p);
1372 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
1374 if (is_table_item (r->item))
1376 *w = render_pager_get_size (r->p, H) / XR_POINT;
1377 *h = render_pager_get_size (r->p, V) / XR_POINT;
1386 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1387 double x, double y, double width, double height);
1391 xr_rendering_draw_all (struct xr_rendering *r, cairo_t *cr)
1393 if (is_table_item (r->item))
1395 struct xr_driver *xr = r->xr;
1397 xr_set_cairo (xr, cr);
1399 render_pager_draw (r->p);
1403 xr_draw_chart (to_chart_item (r->item), cr,
1404 0, 0, CHART_WIDTH, CHART_HEIGHT);
1408 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1409 double x, double y, double width, double height)
1411 struct xrchart_geometry geom;
1414 cairo_translate (cr, x, y + height);
1415 cairo_scale (cr, 1.0, -1.0);
1416 xrchart_geometry_init (cr, &geom, width, height);
1417 if (is_boxplot (chart_item))
1418 xrchart_draw_boxplot (chart_item, cr, &geom);
1419 else if (is_histogram_chart (chart_item))
1420 xrchart_draw_histogram (chart_item, cr, &geom);
1421 else if (is_np_plot_chart (chart_item))
1422 xrchart_draw_np_plot (chart_item, cr, &geom);
1423 else if (is_piechart (chart_item))
1424 xrchart_draw_piechart (chart_item, cr, &geom);
1425 else if (is_barchart (chart_item))
1426 xrchart_draw_barchart (chart_item, cr, &geom);
1427 else if (is_roc_chart (chart_item))
1428 xrchart_draw_roc (chart_item, cr, &geom);
1429 else if (is_scree (chart_item))
1430 xrchart_draw_scree (chart_item, cr, &geom);
1431 else if (is_spreadlevel_plot_chart (chart_item))
1432 xrchart_draw_spreadlevel (chart_item, cr, &geom);
1433 else if (is_scatterplot_chart (chart_item))
1434 xrchart_draw_scatterplot (chart_item, cr, &geom);
1437 xrchart_geometry_free (cr, &geom);
1443 xr_draw_png_chart (const struct chart_item *item,
1444 const char *file_name_template, int number,
1445 const struct xr_color *fg,
1446 const struct xr_color *bg
1449 const int width = 640;
1450 const int length = 480;
1452 cairo_surface_t *surface;
1453 cairo_status_t status;
1454 const char *number_pos;
1458 number_pos = strchr (file_name_template, '#');
1459 if (number_pos != NULL)
1460 file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1461 file_name_template, number, number_pos + 1);
1463 file_name = xstrdup (file_name_template);
1465 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1466 cr = cairo_create (surface);
1468 cairo_set_source_rgb (cr, bg->red, bg->green, bg->blue);
1471 cairo_set_source_rgb (cr, fg->red, fg->green, fg->blue);
1473 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1475 status = cairo_surface_write_to_png (surface, file_name);
1476 if (status != CAIRO_STATUS_SUCCESS)
1477 msg (ME, _("error writing output file `%s': %s"),
1478 file_name, cairo_status_to_string (status));
1481 cairo_surface_destroy (surface);
1486 struct xr_table_state
1488 struct xr_render_fsm fsm;
1489 struct table_item *table_item;
1490 struct render_pager *p;
1494 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1496 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1498 while (render_pager_has_next (ts->p))
1502 used = render_pager_draw_next (ts->p, xr->length - xr->y);
1515 xr_table_destroy (struct xr_render_fsm *fsm)
1517 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1519 table_item_unref (ts->table_item);
1520 render_pager_destroy (ts->p);
1524 static struct xr_render_fsm *
1525 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1527 struct xr_table_state *ts;
1529 ts = xmalloc (sizeof *ts);
1530 ts->fsm.render = xr_table_render;
1531 ts->fsm.destroy = xr_table_destroy;
1532 ts->table_item = table_item_ref (table_item);
1535 xr->y += xr->char_height;
1537 ts->p = render_pager_create (xr->params, table_item);
1542 struct xr_chart_state
1544 struct xr_render_fsm fsm;
1545 struct chart_item *chart_item;
1549 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1551 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1553 const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
1555 if (xr->y > xr->length - chart_height)
1558 if (xr->cairo != NULL)
1560 xr_draw_chart (cs->chart_item, xr->cairo,
1563 xr_to_pt (xr->width),
1564 xr_to_pt (chart_height));
1566 xr->y += chart_height;
1572 xr_chart_destroy (struct xr_render_fsm *fsm)
1574 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1576 chart_item_unref (cs->chart_item);
1580 static struct xr_render_fsm *
1581 xr_render_chart (const struct chart_item *chart_item)
1583 struct xr_chart_state *cs;
1585 cs = xmalloc (sizeof *cs);
1586 cs->fsm.render = xr_chart_render;
1587 cs->fsm.destroy = xr_chart_destroy;
1588 cs->chart_item = chart_item_ref (chart_item);
1594 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1600 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1602 /* Nothing to do. */
1605 static struct xr_render_fsm *
1606 xr_render_eject (void)
1608 static struct xr_render_fsm eject_renderer =
1614 return &eject_renderer;
1617 static struct xr_render_fsm *
1618 xr_create_text_renderer (struct xr_driver *xr, const struct text_item *item)
1620 struct tab_table *tab = tab_create (1, 1);
1622 struct cell_style *style = pool_alloc (tab->container, sizeof *style);
1623 *style = (struct cell_style) CELL_STYLE_INITIALIZER;
1627 style->font = pool_strdup (tab->container, item->font);
1629 style->font_size = item->font_size;
1630 style->bold = item->bold;
1631 style->italic = item->italic;
1632 style->underline = item->underline;
1633 tab->styles[0] = style;
1635 tab_text (tab, 0, 0, TAB_LEFT, text_item_get_text (item));
1636 struct table_item *table_item = table_item_create (&tab->table, NULL, NULL);
1637 struct xr_render_fsm *fsm = xr_render_table (xr, table_item);
1638 table_item_unref (table_item);
1643 static struct xr_render_fsm *
1644 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1646 enum text_item_type type = text_item_get_type (text_item);
1647 const char *text = text_item_get_text (text_item);
1651 case TEXT_ITEM_TITLE:
1653 xr->title = xstrdup (text);
1656 case TEXT_ITEM_SUBTITLE:
1657 free (xr->subtitle);
1658 xr->subtitle = xstrdup (text);
1661 case TEXT_ITEM_COMMAND_CLOSE:
1664 case TEXT_ITEM_BLANK_LINE:
1666 xr->y += xr->char_height;
1669 case TEXT_ITEM_EJECT_PAGE:
1671 return xr_render_eject ();
1675 return xr_create_text_renderer (xr, text_item);
1681 static struct xr_render_fsm *
1682 xr_render_message (struct xr_driver *xr,
1683 const struct message_item *message_item)
1685 const struct msg *msg = message_item_get_msg (message_item);
1686 char *s = msg_to_string (msg, message_item->command_name);
1687 struct text_item *item = text_item_create (TEXT_ITEM_PARAGRAPH, s);
1689 struct xr_render_fsm *fsm = xr_create_text_renderer (xr, item);
1690 text_item_unref (item);
1695 static struct xr_render_fsm *
1696 xr_render_output_item (struct xr_driver *xr,
1697 const struct output_item *output_item)
1699 if (is_table_item (output_item))
1700 return xr_render_table (xr, to_table_item (output_item));
1701 else if (is_chart_item (output_item))
1702 return xr_render_chart (to_chart_item (output_item));
1703 else if (is_text_item (output_item))
1704 return xr_render_text (xr, to_text_item (output_item));
1705 else if (is_message_item (output_item))
1706 return xr_render_message (xr, to_message_item (output_item));