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 struct cell_color colors[TABLE_N_AXES][2]);
171 static void xr_measure_cell_width (void *, const struct table_cell *,
173 static int xr_measure_cell_height (void *, const struct table_cell *,
175 static void xr_draw_cell (void *, const struct table_cell *, int color_idx,
176 int bb[TABLE_N_AXES][2],
177 int spill[TABLE_N_AXES][2],
178 int clip[TABLE_N_AXES][2]);
179 static int xr_adjust_break (void *, const struct table_cell *,
180 int width, int height);
182 static struct xr_render_fsm *xr_render_output_item (
183 struct xr_driver *, const struct output_item *);
185 /* Output driver basics. */
187 static struct xr_driver *
188 xr_driver_cast (struct output_driver *driver)
190 assert (driver->class == &cairo_driver_class);
191 return UP_CAST (driver, struct xr_driver, driver);
194 static struct driver_option *
195 opt (struct output_driver *d, struct string_map *options, const char *key,
196 const char *default_value)
198 return driver_option_get (d, options, key, default_value);
201 /* Parse color information specified by KEY into {RED,GREEN,BLUE}.
202 Currently, the input string must be of the form "#RRRRGGGGBBBB"
203 Future implementations might allow things like "yellow" and
204 "sky-blue-ultra-brown"
207 parse_color (struct output_driver *d, struct string_map *options,
208 const char *key, const char *default_value,
209 struct xr_color *color)
211 int red, green, blue;
212 char *string = parse_string (opt (d, options, key, default_value));
214 if (3 != sscanf (string, "#%04x%04x%04x", &red, &green, &blue))
216 /* If the parsed option string fails, then try the default value */
217 if ( 3 != sscanf (default_value, "#%04x%04x%04x", &red, &green, &blue))
219 /* ... and if that fails set everything to zero */
220 red = green = blue = 0;
226 /* Convert 16 bit ints to float */
227 color->red = red / (double) 0xFFFF;
228 color->green = green / (double) 0xFFFF;
229 color->blue = blue / (double) 0xFFFF;
232 static PangoFontDescription *
233 parse_font (const char *font, int default_size, bool bold, bool italic)
235 if (!c_strcasecmp (font, "Monospaced"))
238 PangoFontDescription *desc = pango_font_description_from_string (font);
242 /* If the font description didn't include an explicit font size, then set it
243 to DEFAULT_SIZE, which is in inch/72000 units. */
244 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
245 pango_font_description_set_size (desc,
246 (default_size / 1000.0) * PANGO_SCALE);
248 pango_font_description_set_weight (desc, (bold
250 : PANGO_WEIGHT_NORMAL));
251 pango_font_description_set_style (desc, (italic
253 : PANGO_STYLE_NORMAL));
258 static PangoFontDescription *
259 parse_font_option (struct output_driver *d, struct string_map *options,
260 const char *key, const char *default_value,
261 int default_size, bool bold, bool italic)
263 char *string = parse_string (opt (d, options, key, default_value));
264 PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
267 msg (MW, _("`%s': bad font specification"), string);
269 /* Fall back to DEFAULT_VALUE, which had better be a valid font
271 desc = parse_font (default_value, default_size, bold, italic);
272 assert (desc != NULL);
280 apply_options (struct xr_driver *xr, struct string_map *o)
282 struct output_driver *d = &xr->driver;
284 /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
285 int left_margin, right_margin;
286 int top_margin, bottom_margin;
287 int paper_width, paper_length;
289 int min_break[TABLE_N_AXES];
291 /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
292 const double scale = XR_POINT / 1000.;
296 for (i = 0; i < XR_N_FONTS; i++)
298 struct xr_font *font = &xr->fonts[i];
300 if (font->desc != NULL)
301 pango_font_description_free (font->desc);
304 font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
305 xr->fonts[XR_FONT_FIXED].desc = parse_font_option
306 (d, o, "fixed-font", "monospace", font_size, false, false);
307 xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font_option (
308 d, o, "prop-font", "sans serif", font_size, false, false);
309 xr->fonts[XR_FONT_EMPHASIS].desc = parse_font_option (
310 d, o, "emph-font", "sans serif", font_size, false, true);
312 xr->line_space = XR_POINT;
313 xr->line_width = XR_POINT / 2;
316 parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg);
317 parse_color (d, o, "foreground-color", "#000000000000", &xr->fg);
319 /* Get dimensions. */
320 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
321 left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
322 right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
323 top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
324 bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
326 min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
327 min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
329 /* Convert to inch/(XR_POINT * 72). */
330 xr->left_margin = left_margin * scale;
331 xr->right_margin = right_margin * scale;
332 xr->top_margin = top_margin * scale;
333 xr->bottom_margin = bottom_margin * scale;
334 xr->width = (paper_width - left_margin - right_margin) * scale;
335 xr->length = (paper_length - top_margin - bottom_margin) * scale;
336 xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
337 xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
340 static struct xr_driver *
341 xr_allocate (const char *name, int device_type, struct string_map *o)
343 struct xr_driver *xr = xzalloc (sizeof *xr);
344 struct output_driver *d = &xr->driver;
346 output_driver_init (d, &cairo_driver_class, name, device_type);
348 apply_options (xr, o);
354 pango_to_xr (int pango)
356 return (XR_POINT != PANGO_SCALE
357 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
364 return (XR_POINT != PANGO_SCALE
365 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
370 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
376 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
380 for (i = 0; i < XR_N_FONTS; i++)
382 struct xr_font *font = &xr->fonts[i];
383 int char_width, char_height;
385 font->layout = pango_cairo_create_layout (cairo);
386 pango_layout_set_font_description (font->layout, font->desc);
388 pango_layout_set_text (font->layout, "0", 1);
389 pango_layout_get_size (font->layout, &char_width, &char_height);
390 xr->char_width = MAX (xr->char_width, pango_to_xr (char_width));
391 xr->char_height = MAX (xr->char_height, pango_to_xr (char_height));
394 if (xr->params == NULL)
396 xr->params = xmalloc (sizeof *xr->params);
397 xr->params->draw_line = xr_draw_line;
398 xr->params->measure_cell_width = xr_measure_cell_width;
399 xr->params->measure_cell_height = xr_measure_cell_height;
400 xr->params->adjust_break = xr_adjust_break;
401 xr->params->draw_cell = xr_draw_cell;
402 xr->params->aux = xr;
403 xr->params->size[H] = xr->width;
404 xr->params->size[V] = xr->length;
405 xr->params->font_size[H] = xr->char_width;
406 xr->params->font_size[V] = xr->char_height;
408 int lw = xr->line_width;
409 int ls = xr->line_space;
410 for (i = 0; i < TABLE_N_AXES; i++)
412 xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
413 xr->params->line_widths[i][RENDER_LINE_SINGLE] = lw;
414 xr->params->line_widths[i][RENDER_LINE_DASHED] = lw;
415 xr->params->line_widths[i][RENDER_LINE_THICK] = lw * 2;
416 xr->params->line_widths[i][RENDER_LINE_THIN] = lw / 2;
417 xr->params->line_widths[i][RENDER_LINE_DOUBLE] = 2 * lw + ls;
420 for (i = 0; i < TABLE_N_AXES; i++)
421 xr->params->min_break[i] = xr->min_break[i];
422 xr->params->supports_margins = true;
425 cairo_set_source_rgb (xr->cairo, xr->fg.red, xr->fg.green, xr->fg.blue);
430 static struct output_driver *
431 xr_create (const char *file_name, enum settings_output_devices device_type,
432 struct string_map *o, enum xr_output_type file_type)
434 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
435 struct xr_driver *xr;
436 cairo_surface_t *surface;
437 cairo_status_t status;
438 double width_pt, length_pt;
440 xr = xr_allocate (file_name, device_type, o);
442 width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
443 length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
444 if (file_type == XR_PDF)
445 surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
446 else if (file_type == XR_PS)
447 surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
448 else if (file_type == XR_SVG)
449 surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
453 status = cairo_surface_status (surface);
454 if (status != CAIRO_STATUS_SUCCESS)
456 msg (ME, _("error opening output file `%s': %s"),
457 file_name, cairo_status_to_string (status));
458 cairo_surface_destroy (surface);
462 xr->cairo = cairo_create (surface);
463 cairo_surface_destroy (surface);
465 if (!xr_set_cairo (xr, xr->cairo))
468 cairo_save (xr->cairo);
469 xr_driver_next_page (xr, xr->cairo);
471 if (xr->width / xr->char_width < MIN_WIDTH)
473 msg (ME, _("The defined page is not wide enough to hold at least %d "
474 "characters in the default font. In fact, there's only "
475 "room for %d characters."),
477 xr->width / xr->char_width);
481 if (xr->length / xr->char_height < MIN_LENGTH)
483 msg (ME, _("The defined page is not long enough to hold at least %d "
484 "lines in the default font. In fact, there's only "
485 "room for %d lines."),
487 xr->length / xr->char_height);
494 output_driver_destroy (&xr->driver);
498 static struct output_driver *
499 xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
500 struct string_map *o)
502 struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_PDF);
507 static struct output_driver *
508 xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
509 struct string_map *o)
511 struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_PS);
516 static struct output_driver *
517 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
518 struct string_map *o)
520 struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_SVG);
526 xr_destroy (struct output_driver *driver)
528 struct xr_driver *xr = xr_driver_cast (driver);
531 xr_driver_destroy_fsm (xr);
533 if (xr->cairo != NULL)
535 cairo_status_t status;
537 cairo_surface_finish (cairo_get_target (xr->cairo));
538 status = cairo_status (xr->cairo);
539 if (status != CAIRO_STATUS_SUCCESS)
540 msg (ME, _("error drawing output for %s driver: %s"),
541 output_driver_get_name (driver),
542 cairo_status_to_string (status));
543 cairo_destroy (xr->cairo);
546 for (i = 0; i < XR_N_FONTS; i++)
548 struct xr_font *font = &xr->fonts[i];
550 if (font->desc != NULL)
551 pango_font_description_free (font->desc);
552 if (font->layout != NULL)
553 g_object_unref (font->layout);
561 xr_flush (struct output_driver *driver)
563 struct xr_driver *xr = xr_driver_cast (driver);
565 cairo_surface_flush (cairo_get_target (xr->cairo));
569 xr_submit (struct output_driver *driver, const struct output_item *output_item)
571 struct xr_driver *xr = xr_driver_cast (driver);
573 xr_driver_output_item (xr, output_item);
574 while (xr_driver_need_new_page (xr))
576 cairo_restore (xr->cairo);
577 cairo_show_page (xr->cairo);
578 cairo_save (xr->cairo);
579 xr_driver_next_page (xr, xr->cairo);
583 /* Functions for rendering a series of output items to a series of Cairo
584 contexts, with pagination.
586 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
587 its underlying implementation.
589 See the big comment in cairo.h for intended usage. */
591 /* Gives new page CAIRO to XR for output. */
593 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
596 cairo_set_source_rgb (cairo, xr->bg.red, xr->bg.green, xr->bg.blue);
597 cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
599 cairo_restore (cairo);
601 cairo_translate (cairo,
602 xr_to_pt (xr->left_margin),
603 xr_to_pt (xr->top_margin));
608 xr_driver_run_fsm (xr);
611 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
612 rendering a previous output item, that is, only if xr_driver_need_new_page()
615 xr_driver_output_item (struct xr_driver *xr,
616 const struct output_item *output_item)
618 assert (xr->fsm == NULL);
619 xr->fsm = xr_render_output_item (xr, output_item);
620 xr_driver_run_fsm (xr);
623 /* Returns true if XR is in the middle of rendering an output item and needs a
624 new page to be appended using xr_driver_next_page() to make progress,
627 xr_driver_need_new_page (const struct xr_driver *xr)
629 return xr->fsm != NULL;
632 /* Returns true if the current page doesn't have any content yet. */
634 xr_driver_is_page_blank (const struct xr_driver *xr)
640 xr_driver_destroy_fsm (struct xr_driver *xr)
644 xr->fsm->destroy (xr->fsm);
650 xr_driver_run_fsm (struct xr_driver *xr)
652 if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
653 xr_driver_destroy_fsm (xr);
657 xr_layout_cell (struct xr_driver *, const struct table_cell *,
658 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
659 int *width, int *height, int *brk);
662 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
663 const struct cell_color *color)
665 cairo_new_path (xr->cairo);
666 cairo_set_source_rgb (xr->cairo,
667 color->r / 255.0, color->g / 255.0, color->b / 255.0);
668 cairo_set_line_width (
670 xr_to_pt (style == RENDER_LINE_THICK ? xr->line_width * 2
671 : style == RENDER_LINE_THIN ? xr->line_width / 2
673 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
674 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
675 cairo_stroke (xr->cairo);
679 dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
681 cairo_new_path (xr->cairo);
682 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
683 cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
684 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y0 + xr->y));
685 cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
686 cairo_line_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y1 + xr->y));
687 cairo_close_path (xr->cairo);
688 cairo_stroke (xr->cairo);
692 fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
694 cairo_new_path (xr->cairo);
695 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
696 cairo_rectangle (xr->cairo,
697 xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y),
698 xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
699 cairo_fill (xr->cairo);
702 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
703 shortening it to X0...X1 if SHORTEN is true.
704 Draws a horizontal line X1...X3 at Y if RIGHT says so,
705 shortening it to X2...X3 if SHORTEN is true. */
707 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
708 enum render_line_style left, enum render_line_style right,
709 const struct cell_color *left_color,
710 const struct cell_color *right_color,
713 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
714 && cell_color_equal (left_color, right_color))
715 dump_line (xr, x0, y, x3, y, left, left_color);
718 if (left != RENDER_LINE_NONE)
719 dump_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
720 if (right != RENDER_LINE_NONE)
721 dump_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
725 /* Draws a vertical line Y0...Y2 at X if TOP says so,
726 shortening it to Y0...Y1 if SHORTEN is true.
727 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
728 shortening it to Y2...Y3 if SHORTEN is true. */
730 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
731 enum render_line_style top, enum render_line_style bottom,
732 const struct cell_color *top_color,
733 const struct cell_color *bottom_color,
736 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
737 && cell_color_equal (top_color, bottom_color))
738 dump_line (xr, x, y0, x, y3, top, top_color);
741 if (top != RENDER_LINE_NONE)
742 dump_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
743 if (bottom != RENDER_LINE_NONE)
744 dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
749 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
750 enum render_line_style styles[TABLE_N_AXES][2],
751 struct cell_color colors[TABLE_N_AXES][2])
753 const int x0 = bb[H][0];
754 const int y0 = bb[V][0];
755 const int x3 = bb[H][1];
756 const int y3 = bb[V][1];
757 const int top = styles[H][0];
758 const int bottom = styles[H][1];
760 int start_side = render_direction_rtl();
761 int end_side = !start_side;
762 const int start_of_line = styles[V][start_side];
763 const int end_of_line = styles[V][end_side];
764 const struct cell_color *top_color = &colors[H][0];
765 const struct cell_color *bottom_color = &colors[H][1];
766 const struct cell_color *start_color = &colors[V][start_side];
767 const struct cell_color *end_color = &colors[V][end_side];
769 /* The algorithm here is somewhat subtle, to allow it to handle
770 all the kinds of intersections that we need.
772 Three additional ordinates are assigned along the x axis. The
773 first is xc, midway between x0 and x3. The others are x1 and
774 x2; for a single vertical line these are equal to xc, and for
775 a double vertical line they are the ordinates of the left and
776 right half of the double line.
778 yc, y1, and y2 are assigned similarly along the y axis.
780 The following diagram shows the coordinate system and output
781 for double top and bottom lines, single left line, and no
785 y0 ________________________
791 y1 = y2 = yc |######### # |
796 y3 |________#_____#_______|
798 struct xr_driver *xr = xr_;
800 /* Offset from center of each line in a pair of double lines. */
801 int double_line_ofs = (xr->line_space + xr->line_width) / 2;
803 /* Are the lines along each axis single or double?
804 (It doesn't make sense to have different kinds of line on the
805 same axis, so we don't try to gracefully handle that case.) */
806 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
807 bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
809 /* When horizontal lines are doubled,
810 the left-side line along y1 normally runs from x0 to x2,
811 and the right-side line along y1 from x3 to x1.
812 If the top-side line is also doubled, we shorten the y1 lines,
813 so that the left-side line runs only to x1,
814 and the right-side line only to x2.
815 Otherwise, the horizontal line at y = y1 below would cut off
816 the intersection, which looks ugly:
818 y0 ________________________
823 y1 |######### ########|
826 y2 |######################|
829 y3 |______________________|
830 It is more of a judgment call when the horizontal line is
831 single. We actually choose to cut off the line anyhow, as
832 shown in the first diagram above.
834 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
835 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
836 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
837 int horz_line_ofs = double_vert ? double_line_ofs : 0;
838 int xc = (x0 + x3) / 2;
839 int x1 = xc - horz_line_ofs;
840 int x2 = xc + horz_line_ofs;
842 bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
843 bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
844 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
845 int vert_line_ofs = double_horz ? double_line_ofs : 0;
846 int yc = (y0 + y3) / 2;
847 int y1 = yc - vert_line_ofs;
848 int y2 = yc + vert_line_ofs;
851 horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
852 start_color, end_color, shorten_yc_line);
855 horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
856 start_color, end_color, shorten_y1_lines);
857 horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
858 start_color, end_color, shorten_y2_lines);
862 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
866 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
868 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
874 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
875 int *min_width, int *max_width)
877 struct xr_driver *xr = xr_;
878 int bb[TABLE_N_AXES][2];
879 int clip[TABLE_N_AXES][2];
886 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
887 xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
890 xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
893 *min_width += px_to_xr (cell->style->margin[H][0]
894 + cell->style->margin[H][1]);
896 *max_width += px_to_xr (cell->style->margin[H][0]
897 + cell->style->margin[H][1]);
901 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
903 struct xr_driver *xr = xr_;
904 int bb[TABLE_N_AXES][2];
905 int clip[TABLE_N_AXES][2];
909 bb[H][1] = width - px_to_xr (cell->style->margin[H][0]
910 + cell->style->margin[H][1]);
913 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
914 xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
915 h += px_to_xr (cell->style->margin[V][0] + cell->style->margin[V][1]);
919 static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
922 xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
923 int bb[TABLE_N_AXES][2],
924 int spill[TABLE_N_AXES][2],
925 int clip[TABLE_N_AXES][2])
927 struct xr_driver *xr = xr_;
930 cairo_save (xr->cairo);
931 int bg_clip[TABLE_N_AXES][2];
932 for (int axis = 0; axis < TABLE_N_AXES; axis++)
934 bg_clip[axis][0] = clip[axis][0];
935 if (bb[axis][0] == clip[axis][0])
936 bg_clip[axis][0] -= spill[axis][0];
938 bg_clip[axis][1] = clip[axis][1];
939 if (bb[axis][1] == clip[axis][1])
940 bg_clip[axis][1] += spill[axis][1];
942 xr_clip (xr, bg_clip);
943 cairo_set_source_rgb (xr->cairo,
944 cell->style->bg[color_idx].r / 255.,
945 cell->style->bg[color_idx].g / 255.,
946 cell->style->bg[color_idx].b / 255.);
948 bb[H][0] - spill[H][0],
949 bb[V][0] - spill[V][0],
950 bb[H][1] + spill[H][1],
951 bb[V][1] + spill[V][1]);
952 cairo_restore (xr->cairo);
954 cairo_save (xr->cairo);
955 cairo_set_source_rgb (xr->cairo,
956 cell->style->fg[color_idx].r / 255.,
957 cell->style->fg[color_idx].g / 255.,
958 cell->style->fg[color_idx].b / 255.);
960 for (int axis = 0; axis < TABLE_N_AXES; axis++)
962 bb[axis][0] += px_to_xr (cell->style->margin[axis][0]);
963 bb[axis][1] -= px_to_xr (cell->style->margin[axis][1]);
965 if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
966 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
967 cairo_restore (xr->cairo);
971 xr_adjust_break (void *xr_, const struct table_cell *cell,
972 int width, int height)
974 struct xr_driver *xr = xr_;
975 int bb[TABLE_N_AXES][2];
976 int clip[TABLE_N_AXES][2];
979 if (xr_measure_cell_height (xr_, cell, width) < height)
983 bb[H][1] = width - px_to_xr (cell->style->margin[H][0]
984 + cell->style->margin[H][1]);
988 bb[V][1] = height - px_to_xr (cell->style->margin[V][0]
989 + cell->style->margin[V][1]);
990 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
991 xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
996 xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
998 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
1000 double x0 = xr_to_pt (clip[H][0] + xr->x);
1001 double y0 = xr_to_pt (clip[V][0] + xr->y);
1002 double x1 = xr_to_pt (clip[H][1] + xr->x);
1003 double y1 = xr_to_pt (clip[V][1] + xr->y);
1005 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
1006 cairo_clip (xr->cairo);
1011 add_attr_with_start (PangoAttrList *list, PangoAttribute *attr, guint start_index)
1013 attr->start_index = start_index;
1014 pango_attr_list_insert (list, attr);
1018 xr_layout_cell_text (struct xr_driver *xr,
1019 const struct cell_contents *contents,
1020 const struct cell_style *style,
1021 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1022 int *widthp, int *brk)
1024 unsigned int options = contents->options;
1028 struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
1029 : options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
1030 : &xr->fonts[XR_FONT_PROPORTIONAL]);
1031 struct xr_font local_font;
1034 PangoFontDescription *desc = parse_font (
1036 style->font_size ? style->font_size * 1000 * 72 / 128 : 10000,
1037 style->bold, style->italic);
1040 PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
1041 pango_layout_set_font_description (layout, desc);
1043 local_font.desc = desc;
1044 local_font.layout = layout;
1049 int footnote_adjustment;
1050 if (contents->n_footnotes == 0)
1051 footnote_adjustment = 0;
1052 else if (contents->n_footnotes == 1 && (options & TAB_HALIGN) == TAB_RIGHT)
1054 PangoAttrList *attrs;
1056 const char *marker = contents->footnotes[0]->marker;
1057 pango_layout_set_text (font->layout, marker, strlen (marker));
1059 attrs = pango_attr_list_new ();
1060 pango_attr_list_insert (attrs, pango_attr_rise_new (7000));
1061 pango_layout_set_attributes (font->layout, attrs);
1062 pango_attr_list_unref (attrs);
1064 pango_layout_get_size (font->layout, &w, &h);
1065 footnote_adjustment = MIN (w, px_to_xr (style->margin[H][1]));
1068 footnote_adjustment = px_to_xr (style->margin[H][1]);
1070 length = strlen (contents->text);
1071 if (footnote_adjustment)
1073 PangoAttrList *attrs;
1076 bb[H][1] += footnote_adjustment;
1079 ds_extend (&s, length + contents->n_footnotes * 10);
1080 ds_put_cstr (&s, contents->text);
1081 cell_contents_format_footnote_markers (contents, &s);
1082 pango_layout_set_text (font->layout, ds_cstr (&s), ds_length (&s));
1085 attrs = pango_attr_list_new ();
1086 if (style->underline)
1087 pango_attr_list_insert (attrs, pango_attr_underline_new (
1088 PANGO_UNDERLINE_SINGLE));
1089 add_attr_with_start (attrs, pango_attr_rise_new (7000), length);
1090 add_attr_with_start (
1091 attrs, pango_attr_font_desc_new (font->desc), length);
1092 pango_layout_set_attributes (font->layout, attrs);
1093 pango_attr_list_unref (attrs);
1097 pango_layout_set_text (font->layout, contents->text, -1);
1099 if (style->underline)
1101 PangoAttrList *attrs = pango_attr_list_new ();
1102 pango_attr_list_insert (attrs, pango_attr_underline_new (
1103 PANGO_UNDERLINE_SINGLE));
1104 pango_layout_set_attributes (font->layout, attrs);
1105 pango_attr_list_unref (attrs);
1109 pango_layout_set_alignment (
1111 ((options & TAB_HALIGN) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
1112 : (options & TAB_HALIGN) == TAB_LEFT ? PANGO_ALIGN_LEFT
1113 : PANGO_ALIGN_CENTER));
1114 pango_layout_set_width (
1116 bb[H][1] == INT_MAX ? -1 : xr_to_pango (bb[H][1] - bb[H][0]));
1117 pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
1119 if (clip[H][0] != clip[H][1])
1121 cairo_save (xr->cairo);
1123 cairo_translate (xr->cairo,
1124 xr_to_pt (bb[H][0] + xr->x),
1125 xr_to_pt (bb[V][0] + xr->y));
1126 pango_cairo_show_layout (xr->cairo, font->layout);
1128 /* If enabled, this draws a blue rectangle around the extents of each
1129 line of text, which can be rather useful for debugging layout
1133 PangoLayoutIter *iter;
1134 iter = pango_layout_get_iter (font->layout);
1137 PangoRectangle extents;
1139 pango_layout_iter_get_line_extents (iter, &extents, NULL);
1140 cairo_save (xr->cairo);
1141 cairo_set_source_rgb (xr->cairo, 1, 0, 0);
1143 pango_to_xr (extents.x) - xr->x,
1144 pango_to_xr (extents.y) - xr->y,
1145 pango_to_xr (extents.x + extents.width) - xr->x,
1146 pango_to_xr (extents.y + extents.height) - xr->y);
1147 cairo_restore (xr->cairo);
1149 while (pango_layout_iter_next_line (iter));
1150 pango_layout_iter_free (iter);
1153 cairo_restore (xr->cairo);
1156 pango_layout_get_size (font->layout, &w, &h);
1157 w = pango_to_xr (w);
1158 h = pango_to_xr (h);
1161 if (bb[V][0] + h >= bb[V][1])
1163 PangoLayoutIter *iter;
1164 int best UNUSED = 0;
1166 /* Choose a breakpoint between lines instead of in the middle of one. */
1167 iter = pango_layout_get_iter (font->layout);
1170 PangoRectangle extents;
1174 pango_layout_iter_get_line_extents (iter, NULL, &extents);
1175 pango_layout_iter_get_line_yrange (iter, &y0, &y1);
1176 extents.x = pango_to_xr (extents.x);
1177 extents.y = pango_to_xr (y0);
1178 extents.width = pango_to_xr (extents.width);
1179 extents.height = pango_to_xr (y1 - y0);
1180 bottom = bb[V][0] + extents.y + extents.height;
1181 if (bottom < bb[V][1])
1183 if (brk && clip[H][0] != clip[H][1])
1191 while (pango_layout_iter_next_line (iter));
1192 pango_layout_iter_free (iter);
1194 /* If enabled, draws a green line across the chosen breakpoint, which can
1195 be useful for debugging issues with breaking. */
1198 if (best && !xr->nest)
1199 dump_line (xr, -xr->left_margin, best,
1200 xr->width + xr->right_margin, best,
1201 RENDER_LINE_SINGLE, &CELL_COLOR (0, 255, 0));
1205 pango_layout_set_attributes (font->layout, NULL);
1207 if (font == &local_font)
1209 g_object_unref (G_OBJECT (font->layout));
1210 pango_font_description_free (font->desc);
1213 return bb[V][0] + h;
1217 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
1218 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
1219 int *width, int *height, int *brk)
1221 int bb[TABLE_N_AXES][2];
1229 memcpy (bb, bb_, sizeof bb);
1231 /* If enabled, draws a blue rectangle around the cell extents, which can be
1232 useful for debugging layout. */
1235 if (clip[H][0] != clip[H][1])
1237 int offset = (xr->nest) * XR_POINT;
1239 cairo_save (xr->cairo);
1240 cairo_set_source_rgb (xr->cairo, 0, 0, 1);
1242 bb[H][0] + offset, bb[V][0] + offset,
1243 bb[H][1] - offset, bb[V][1] - offset);
1244 cairo_restore (xr->cairo);
1248 for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
1250 const struct cell_contents *contents = &cell->contents[i];
1256 bb[V][0] += xr->char_height / 2;
1257 if (bb[V][0] >= bb[V][1])
1263 bb[V][0] = xr_layout_cell_text (xr, contents, cell->style, bb, clip,
1266 *height = bb[V][0] - bb_[V][0];
1269 struct output_driver_factory pdf_driver_factory =
1270 { "pdf", "pspp.pdf", xr_pdf_create };
1271 struct output_driver_factory ps_driver_factory =
1272 { "ps", "pspp.ps", xr_ps_create };
1273 struct output_driver_factory svg_driver_factory =
1274 { "svg", "pspp.svg", xr_svg_create };
1276 static const struct output_driver_class cairo_driver_class =
1284 /* GUI rendering helpers. */
1288 struct output_item *item;
1291 struct render_pager *p;
1292 struct xr_driver *xr;
1295 #define CHART_WIDTH 500
1296 #define CHART_HEIGHT 375
1301 xr_driver_create (cairo_t *cairo, struct string_map *options)
1303 struct xr_driver *xr = xr_allocate ("cairo", 0, options);
1304 if (!xr_set_cairo (xr, cairo))
1306 output_driver_destroy (&xr->driver);
1312 /* Destroy XR, which should have been created with xr_driver_create(). Any
1313 cairo_t added to XR is not destroyed, because it is owned by the client. */
1315 xr_driver_destroy (struct xr_driver *xr)
1320 output_driver_destroy (&xr->driver);
1324 static struct xr_rendering *
1325 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
1327 struct table_item *table_item;
1328 struct xr_rendering *r;
1330 table_item = table_item_create (table_from_string (TAB_LEFT, text),
1332 r = xr_rendering_create (xr, &table_item->output_item, cr);
1333 table_item_unref (table_item);
1339 xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
1341 if (is_table_item (xr->item))
1342 apply_options (xr->xr, o);
1345 struct xr_rendering *
1346 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
1349 struct xr_rendering *r = NULL;
1351 if (is_text_item (item))
1352 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1354 else if (is_message_item (item))
1356 const struct message_item *message_item = to_message_item (item);
1357 const struct msg *msg = message_item_get_msg (message_item);
1358 char *s = msg_to_string (msg, NULL);
1359 r = xr_rendering_create_text (xr, s, cr);
1362 else if (is_table_item (item))
1364 r = xzalloc (sizeof *r);
1365 r->item = output_item_ref (item);
1367 xr_set_cairo (xr, cr);
1368 r->p = render_pager_create (xr->params, to_table_item (item));
1370 else if (is_chart_item (item))
1372 r = xzalloc (sizeof *r);
1373 r->item = output_item_ref (item);
1380 xr_rendering_destroy (struct xr_rendering *r)
1384 output_item_unref (r->item);
1385 render_pager_destroy (r->p);
1391 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
1393 if (is_table_item (r->item))
1395 *w = render_pager_get_size (r->p, H) / XR_POINT;
1396 *h = render_pager_get_size (r->p, V) / XR_POINT;
1405 static void xr_draw_chart (const struct chart_item *, cairo_t *,
1406 double x, double y, double width, double height);
1410 xr_rendering_draw_all (struct xr_rendering *r, cairo_t *cr)
1412 if (is_table_item (r->item))
1414 struct xr_driver *xr = r->xr;
1416 xr_set_cairo (xr, cr);
1418 render_pager_draw (r->p);
1422 xr_draw_chart (to_chart_item (r->item), cr,
1423 0, 0, CHART_WIDTH, CHART_HEIGHT);
1427 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1428 double x, double y, double width, double height)
1430 struct xrchart_geometry geom;
1433 cairo_translate (cr, x, y + height);
1434 cairo_scale (cr, 1.0, -1.0);
1435 xrchart_geometry_init (cr, &geom, width, height);
1436 if (is_boxplot (chart_item))
1437 xrchart_draw_boxplot (chart_item, cr, &geom);
1438 else if (is_histogram_chart (chart_item))
1439 xrchart_draw_histogram (chart_item, cr, &geom);
1440 else if (is_np_plot_chart (chart_item))
1441 xrchart_draw_np_plot (chart_item, cr, &geom);
1442 else if (is_piechart (chart_item))
1443 xrchart_draw_piechart (chart_item, cr, &geom);
1444 else if (is_barchart (chart_item))
1445 xrchart_draw_barchart (chart_item, cr, &geom);
1446 else if (is_roc_chart (chart_item))
1447 xrchart_draw_roc (chart_item, cr, &geom);
1448 else if (is_scree (chart_item))
1449 xrchart_draw_scree (chart_item, cr, &geom);
1450 else if (is_spreadlevel_plot_chart (chart_item))
1451 xrchart_draw_spreadlevel (chart_item, cr, &geom);
1452 else if (is_scatterplot_chart (chart_item))
1453 xrchart_draw_scatterplot (chart_item, cr, &geom);
1456 xrchart_geometry_free (cr, &geom);
1462 xr_draw_png_chart (const struct chart_item *item,
1463 const char *file_name_template, int number,
1464 const struct xr_color *fg,
1465 const struct xr_color *bg
1468 const int width = 640;
1469 const int length = 480;
1471 cairo_surface_t *surface;
1472 cairo_status_t status;
1473 const char *number_pos;
1477 number_pos = strchr (file_name_template, '#');
1478 if (number_pos != NULL)
1479 file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1480 file_name_template, number, number_pos + 1);
1482 file_name = xstrdup (file_name_template);
1484 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1485 cr = cairo_create (surface);
1487 cairo_set_source_rgb (cr, bg->red, bg->green, bg->blue);
1490 cairo_set_source_rgb (cr, fg->red, fg->green, fg->blue);
1492 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1494 status = cairo_surface_write_to_png (surface, file_name);
1495 if (status != CAIRO_STATUS_SUCCESS)
1496 msg (ME, _("error writing output file `%s': %s"),
1497 file_name, cairo_status_to_string (status));
1500 cairo_surface_destroy (surface);
1505 struct xr_table_state
1507 struct xr_render_fsm fsm;
1508 struct table_item *table_item;
1509 struct render_pager *p;
1513 xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1515 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1517 while (render_pager_has_next (ts->p))
1521 used = render_pager_draw_next (ts->p, xr->length - xr->y);
1534 xr_table_destroy (struct xr_render_fsm *fsm)
1536 struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
1538 table_item_unref (ts->table_item);
1539 render_pager_destroy (ts->p);
1543 static struct xr_render_fsm *
1544 xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
1546 struct xr_table_state *ts;
1548 ts = xmalloc (sizeof *ts);
1549 ts->fsm.render = xr_table_render;
1550 ts->fsm.destroy = xr_table_destroy;
1551 ts->table_item = table_item_ref (table_item);
1554 xr->y += xr->char_height;
1556 ts->p = render_pager_create (xr->params, table_item);
1561 struct xr_chart_state
1563 struct xr_render_fsm fsm;
1564 struct chart_item *chart_item;
1568 xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
1570 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1572 const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
1574 if (xr->y > xr->length - chart_height)
1577 if (xr->cairo != NULL)
1579 xr_draw_chart (cs->chart_item, xr->cairo,
1582 xr_to_pt (xr->width),
1583 xr_to_pt (chart_height));
1585 xr->y += chart_height;
1591 xr_chart_destroy (struct xr_render_fsm *fsm)
1593 struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
1595 chart_item_unref (cs->chart_item);
1599 static struct xr_render_fsm *
1600 xr_render_chart (const struct chart_item *chart_item)
1602 struct xr_chart_state *cs;
1604 cs = xmalloc (sizeof *cs);
1605 cs->fsm.render = xr_chart_render;
1606 cs->fsm.destroy = xr_chart_destroy;
1607 cs->chart_item = chart_item_ref (chart_item);
1613 xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
1619 xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
1621 /* Nothing to do. */
1624 static struct xr_render_fsm *
1625 xr_render_eject (void)
1627 static struct xr_render_fsm eject_renderer =
1633 return &eject_renderer;
1636 static struct xr_render_fsm *
1637 xr_create_text_renderer (struct xr_driver *xr, const struct text_item *item)
1639 struct tab_table *tab = tab_create (1, 1);
1641 struct cell_style *style = pool_alloc (tab->container, sizeof *style);
1642 *style = (struct cell_style) CELL_STYLE_INITIALIZER;
1644 style->font = pool_strdup (tab->container, item->font);
1645 style->font_size = item->font_size;
1646 style->bold = item->bold;
1647 style->italic = item->italic;
1648 style->underline = item->underline;
1649 tab->styles[0] = style;
1651 tab_text (tab, 0, 0, TAB_LEFT, text_item_get_text (item));
1652 struct table_item *table_item = table_item_create (&tab->table, NULL, NULL);
1653 struct xr_render_fsm *fsm = xr_render_table (xr, table_item);
1654 table_item_unref (table_item);
1659 static struct xr_render_fsm *
1660 xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
1662 enum text_item_type type = text_item_get_type (text_item);
1663 const char *text = text_item_get_text (text_item);
1667 case TEXT_ITEM_TITLE:
1669 xr->title = xstrdup (text);
1672 case TEXT_ITEM_SUBTITLE:
1673 free (xr->subtitle);
1674 xr->subtitle = xstrdup (text);
1677 case TEXT_ITEM_COMMAND_CLOSE:
1680 case TEXT_ITEM_BLANK_LINE:
1682 xr->y += xr->char_height;
1685 case TEXT_ITEM_EJECT_PAGE:
1687 return xr_render_eject ();
1691 return xr_create_text_renderer (xr, text_item);
1697 static struct xr_render_fsm *
1698 xr_render_message (struct xr_driver *xr,
1699 const struct message_item *message_item)
1701 const struct msg *msg = message_item_get_msg (message_item);
1702 char *s = msg_to_string (msg, message_item->command_name);
1703 struct text_item *item = text_item_create (TEXT_ITEM_PARAGRAPH, s);
1705 struct xr_render_fsm *fsm = xr_create_text_renderer (xr, item);
1706 text_item_unref (item);
1711 static struct xr_render_fsm *
1712 xr_render_output_item (struct xr_driver *xr,
1713 const struct output_item *output_item)
1715 if (is_table_item (output_item))
1716 return xr_render_table (xr, to_table_item (output_item));
1717 else if (is_chart_item (output_item))
1718 return xr_render_chart (to_chart_item (output_item));
1719 else if (is_text_item (output_item))
1720 return xr_render_text (xr, to_text_item (output_item));
1721 else if (is_message_item (output_item))
1722 return xr_render_message (xr, to_message_item (output_item));