1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010 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/start-date.h>
25 #include <libpspp/str.h>
26 #include <libpspp/string-map.h>
27 #include <libpspp/version.h>
28 #include <output/cairo-chart.h>
29 #include <output/chart-item-provider.h>
30 #include <output/charts/boxplot.h>
31 #include <output/charts/np-plot.h>
32 #include <output/charts/piechart.h>
33 #include <output/charts/plot-hist.h>
34 #include <output/charts/roc-chart.h>
35 #include <output/charts/scree.h>
36 #include <output/driver-provider.h>
37 #include <output/message-item.h>
38 #include <output/options.h>
39 #include <output/render.h>
40 #include <output/tab.h>
41 #include <output/table-item.h>
42 #include <output/table.h>
43 #include <output/text-item.h>
45 #include <cairo/cairo-pdf.h>
46 #include <cairo/cairo-ps.h>
47 #include <cairo/cairo-svg.h>
48 #include <cairo/cairo.h>
49 #include <pango/pango-font.h>
50 #include <pango/pango-layout.h>
51 #include <pango/pango.h>
52 #include <pango/pangocairo.h>
61 #define _(msgid) gettext (msgid)
63 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
67 /* Measurements as we present to the rest of PSPP. */
68 #define XR_POINT PANGO_SCALE
69 #define XR_INCH (XR_POINT * 72)
71 /* Conversions to and from points. */
75 return x / (double) XR_POINT;
95 /* A font for use with Cairo. */
99 PangoFontDescription *desc;
101 PangoFontMetrics *metrics;
104 /* Cairo output driver. */
107 struct output_driver driver;
109 /* User parameters. */
110 bool headers; /* Draw headers at top of page? */
112 struct xr_font fonts[XR_N_FONTS];
113 int font_height; /* In XR units. */
115 int width; /* Page width minus margins. */
116 int length; /* Page length minus margins and header. */
118 int left_margin; /* Left margin in XR units. */
119 int right_margin; /* Right margin in XR units. */
120 int top_margin; /* Top margin in XR units. */
121 int bottom_margin; /* Bottom margin in XR units. */
123 int line_gutter; /* Space around lines. */
124 int line_space; /* Space between lines. */
125 int line_width; /* Width of lines. */
127 enum xr_output_type file_type; /* Type of output file. */
129 /* Internal state. */
130 struct render_params *params;
135 int page_number; /* Current page number. */
139 static const struct output_driver_class cairo_driver_class;
141 static void xr_show_page (struct xr_driver *);
142 static void draw_headers (struct xr_driver *);
144 static bool load_font (struct xr_driver *, struct xr_font *);
145 static void free_font (struct xr_font *);
147 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
148 enum render_line_style styles[TABLE_N_AXES][2]);
149 static void xr_measure_cell_width (void *, const struct table_cell *,
151 static int xr_measure_cell_height (void *, const struct table_cell *,
153 static void xr_draw_cell (void *, const struct table_cell *,
154 int bb[TABLE_N_AXES][2],
155 int clip[TABLE_N_AXES][2]);
157 /* Driver initialization. */
159 static struct xr_driver *
160 xr_driver_cast (struct output_driver *driver)
162 assert (driver->class == &cairo_driver_class);
163 return UP_CAST (driver, struct xr_driver, driver);
166 static struct driver_option *
167 opt (struct output_driver *d, struct string_map *options, const char *key,
168 const char *default_value)
170 return driver_option_get (d, options, key, default_value);
173 static struct xr_driver *
174 xr_allocate (const char *name, int device_type, struct string_map *o)
176 struct output_driver *d;
177 struct xr_driver *xr;
179 xr = xzalloc (sizeof *xr);
181 output_driver_init (d, &cairo_driver_class, name, device_type);
183 xr->font_height = XR_POINT * 10;
184 xr->fonts[XR_FONT_FIXED].string
185 = parse_string (opt (d, o, "fixed-font", "monospace"));
186 xr->fonts[XR_FONT_PROPORTIONAL].string
187 = parse_string (opt (d, o, "prop-font", "serif"));
188 xr->fonts[XR_FONT_EMPHASIS].string
189 = parse_string (opt (d, o, "emph-font", "serif italic"));
190 xr->line_gutter = XR_POINT;
191 xr->line_space = XR_POINT;
192 xr->line_width = XR_POINT / 2;
199 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
205 cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
207 for (i = 0; i < XR_N_FONTS; i++)
208 if (!load_font (xr, &xr->fonts[i]))
211 if (xr->params == NULL)
213 int single_width, double_width;
215 xr->params = xmalloc (sizeof *xr->params);
216 xr->params->draw_line = xr_draw_line;
217 xr->params->measure_cell_width = xr_measure_cell_width;
218 xr->params->measure_cell_height = xr_measure_cell_height;
219 xr->params->draw_cell = xr_draw_cell;
220 xr->params->aux = xr;
221 xr->params->size[H] = xr->width;
222 xr->params->size[V] = xr->length;
223 xr->params->font_size[H] = xr->font_height / 2; /* XXX */
224 xr->params->font_size[V] = xr->font_height;
226 single_width = 2 * xr->line_gutter + xr->line_width;
227 double_width = 2 * xr->line_gutter + xr->line_space + 2 * xr->line_width;
228 for (i = 0; i < TABLE_N_AXES; i++)
230 xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
231 xr->params->line_widths[i][RENDER_LINE_SINGLE] = single_width;
232 xr->params->line_widths[i][RENDER_LINE_DOUBLE] = double_width;
239 static struct output_driver *
240 xr_create (const char *file_name, enum settings_output_devices device_type,
241 struct string_map *o, enum xr_output_type file_type)
243 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
244 struct output_driver *d;
245 struct xr_driver *xr;
246 cairo_surface_t *surface;
247 cairo_status_t status;
248 double width_pt, length_pt;
249 int paper_width, paper_length;
251 xr = xr_allocate (file_name, device_type, o);
254 xr->headers = parse_boolean (opt (d, o, "headers", "true"));
256 xr->file_type = file_type;
258 parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
259 xr->left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
260 xr->right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
261 xr->top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
262 xr->bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
265 xr->top_margin += 3 * xr->font_height;
266 xr->width = paper_width - xr->left_margin - xr->right_margin;
267 xr->length = paper_length - xr->top_margin - xr->bottom_margin;
269 width_pt = paper_width / 1000.0;
270 length_pt = paper_length / 1000.0;
271 if (xr->file_type == XR_PDF)
272 surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
273 else if (xr->file_type == XR_PS)
274 surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
275 else if (xr->file_type == XR_SVG)
276 surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
280 status = cairo_surface_status (surface);
281 if (status != CAIRO_STATUS_SUCCESS)
283 error (0, 0, _("error opening output file \"%s\": %s"),
284 file_name, cairo_status_to_string (status));
285 cairo_surface_destroy (surface);
289 xr->cairo = cairo_create (surface);
290 cairo_surface_destroy (surface);
292 cairo_translate (xr->cairo,
293 xr_to_pt (xr->left_margin),
294 xr_to_pt (xr->top_margin));
296 if (!xr_set_cairo (xr, xr->cairo))
299 if (xr->width / (xr->font_height / 2) < MIN_WIDTH)
301 error (0, 0, _("The defined page is not wide enough to hold at least %d "
302 "characters in the default font. In fact, there's only "
303 "room for %d characters."),
305 xr->width / (xr->font_height / 2));
309 if (xr->length / xr->font_height < MIN_LENGTH)
311 error (0, 0, _("The defined page is not long "
312 "enough to hold margins and headers, plus least %d "
313 "lines of the default fonts. In fact, there's only "
314 "room for %d lines."),
316 xr->length / xr->font_height);
323 output_driver_destroy (&xr->driver);
327 static struct output_driver *
328 xr_pdf_create (const char *file_name, enum settings_output_devices device_type,
329 struct string_map *o)
331 return xr_create (file_name, device_type, o, XR_PDF);
334 static struct output_driver *
335 xr_ps_create (const char *file_name, enum settings_output_devices device_type,
336 struct string_map *o)
338 return xr_create (file_name, device_type, o, XR_PS);
341 static struct output_driver *
342 xr_svg_create (const char *file_name, enum settings_output_devices device_type,
343 struct string_map *o)
345 return xr_create (file_name, device_type, o, XR_SVG);
349 xr_destroy (struct output_driver *driver)
351 struct xr_driver *xr = xr_driver_cast (driver);
354 if (xr->cairo != NULL)
356 cairo_status_t status;
361 cairo_surface_finish (cairo_get_target (xr->cairo));
362 status = cairo_status (xr->cairo);
363 if (status != CAIRO_STATUS_SUCCESS)
364 error (0, 0, _("error drawing output for %s driver: %s"),
365 output_driver_get_name (driver),
366 cairo_status_to_string (status));
367 cairo_destroy (xr->cairo);
370 free (xr->command_name);
371 for (i = 0; i < XR_N_FONTS; i++)
372 free_font (&xr->fonts[i]);
378 xr_flush (struct output_driver *driver)
380 struct xr_driver *xr = xr_driver_cast (driver);
382 cairo_surface_flush (cairo_get_target (xr->cairo));
386 xr_init_caption_cell (const char *caption, struct table_cell *cell)
388 cell->contents = caption;
389 cell->options = TAB_LEFT;
390 cell->destructor = NULL;
393 static struct render_page *
394 xr_render_table_item (struct xr_driver *xr, const struct table_item *item,
395 int *caption_heightp)
397 const char *caption = table_item_get_caption (item);
401 /* XXX doesn't do well with very large captions */
402 struct table_cell cell;
403 xr_init_caption_cell (caption, &cell);
404 *caption_heightp = xr_measure_cell_height (xr, &cell, xr->width);
407 *caption_heightp = 0;
409 return render_page_create (xr->params, table_item_get_table (item));
413 xr_output_table_item (struct xr_driver *xr,
414 const struct table_item *table_item)
416 struct render_break x_break;
417 struct render_page *page;
421 xr->y += xr->font_height;
423 page = xr_render_table_item (xr, table_item, &caption_height);
424 xr->params->size[V] = xr->length - caption_height;
425 for (render_break_init (&x_break, page, H);
426 render_break_has_next (&x_break); )
428 struct render_page *x_slice;
429 struct render_break y_break;
431 x_slice = render_break_next (&x_break, xr->width);
432 for (render_break_init (&y_break, x_slice, V);
433 render_break_has_next (&y_break); )
435 int space = xr->length - xr->y;
436 struct render_page *y_slice;
438 /* XXX doesn't allow for caption or space between segments */
439 if (render_break_next_size (&y_break) > space)
446 y_slice = render_break_next (&y_break, space);
449 struct table_cell cell;
450 int bb[TABLE_N_AXES][2];
452 xr_init_caption_cell (table_item_get_caption (table_item),
455 bb[H][1] = xr->width;
457 bb[V][1] = caption_height;
458 xr_draw_cell (xr, &cell, bb, bb);
459 xr->y += caption_height;
463 render_page_draw (y_slice);
464 xr->y += render_page_get_size (y_slice, V);
465 render_page_unref (y_slice);
467 render_break_destroy (&y_break);
469 render_break_destroy (&x_break);
473 xr_output_text (struct xr_driver *xr, const char *text)
475 struct table_item *table_item;
477 table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
478 xr_output_table_item (xr, table_item);
479 table_item_unref (table_item);
483 xr_submit (struct output_driver *driver, const struct output_item *output_item)
485 struct xr_driver *xr = xr_driver_cast (driver);
486 if (is_table_item (output_item))
487 xr_output_table_item (xr, to_table_item (output_item));
488 else if (is_chart_item (output_item))
492 xr_draw_chart (to_chart_item (output_item), xr->cairo, 0.0, 0.0,
493 xr_to_pt (xr->width), xr_to_pt (xr->length));
496 else if (is_text_item (output_item))
498 const struct text_item *text_item = to_text_item (output_item);
499 enum text_item_type type = text_item_get_type (text_item);
500 const char *text = text_item_get_text (text_item);
504 case TEXT_ITEM_TITLE:
506 xr->title = xstrdup (text);
509 case TEXT_ITEM_SUBTITLE:
511 xr->subtitle = xstrdup (text);
514 case TEXT_ITEM_COMMAND_CLOSE:
517 case TEXT_ITEM_BLANK_LINE:
519 xr->y += xr->font_height;
522 case TEXT_ITEM_EJECT_PAGE:
528 xr_output_text (xr, text);
532 else if (is_message_item (output_item))
534 const struct message_item *message_item = to_message_item (output_item);
535 const struct msg *msg = message_item_get_msg (message_item);
536 char *s = msg_to_string (msg, xr->command_name);
537 xr_output_text (xr, s);
543 xr_show_page (struct xr_driver *xr)
550 cairo_show_page (xr->cairo);
557 xr_layout_cell (struct xr_driver *, const struct table_cell *,
558 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
559 PangoWrapMode, int *width, int *height);
562 dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
564 cairo_new_path (xr->cairo);
565 cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
566 cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
567 cairo_stroke (xr->cairo);
570 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
571 shortening it to X0...X1 if SHORTEN is true.
572 Draws a horizontal line X1...X3 at Y if RIGHT says so,
573 shortening it to X2...X3 if SHORTEN is true. */
575 horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
576 enum render_line_style left, enum render_line_style right,
579 if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
580 dump_line (xr, x0, y, x3, y);
583 if (left != RENDER_LINE_NONE)
584 dump_line (xr, x0, y, shorten ? x1 : x2, y);
585 if (right != RENDER_LINE_NONE)
586 dump_line (xr, shorten ? x2 : x1, y, x3, y);
590 /* Draws a vertical line Y0...Y2 at X if TOP says so,
591 shortening it to Y0...Y1 if SHORTEN is true.
592 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
593 shortening it to Y2...Y3 if SHORTEN is true. */
595 vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
596 enum render_line_style top, enum render_line_style bottom,
599 if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
600 dump_line (xr, x, y0, x, y3);
603 if (top != RENDER_LINE_NONE)
604 dump_line (xr, x, y0, x, shorten ? y1 : y2);
605 if (bottom != RENDER_LINE_NONE)
606 dump_line (xr, x, shorten ? y2 : y1, x, y3);
611 xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
612 enum render_line_style styles[TABLE_N_AXES][2])
614 const int x0 = bb[H][0];
615 const int y0 = bb[V][0];
616 const int x3 = bb[H][1];
617 const int y3 = bb[V][1];
618 const int top = styles[H][0];
619 const int left = styles[V][0];
620 const int bottom = styles[H][1];
621 const int right = styles[V][1];
623 /* The algorithm here is somewhat subtle, to allow it to handle
624 all the kinds of intersections that we need.
626 Three additional ordinates are assigned along the x axis. The
627 first is xc, midway between x0 and x3. The others are x1 and
628 x2; for a single vertical line these are equal to xc, and for
629 a double vertical line they are the ordinates of the left and
630 right half of the double line.
632 yc, y1, and y2 are assigned similarly along the y axis.
634 The following diagram shows the coordinate system and output
635 for double top and bottom lines, single left line, and no
639 y0 ________________________
645 y1 = y2 = yc |######### # |
650 y3 |________#_____#_______|
652 struct xr_driver *xr = xr_;
654 /* Offset from center of each line in a pair of double lines. */
655 int double_line_ofs = (xr->line_space + xr->line_width) / 2;
657 /* Are the lines along each axis single or double?
658 (It doesn't make sense to have different kinds of line on the
659 same axis, so we don't try to gracefully handle that case.) */
660 bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
661 bool double_horz = left == RENDER_LINE_DOUBLE || right == RENDER_LINE_DOUBLE;
663 /* When horizontal lines are doubled,
664 the left-side line along y1 normally runs from x0 to x2,
665 and the right-side line along y1 from x3 to x1.
666 If the top-side line is also doubled, we shorten the y1 lines,
667 so that the left-side line runs only to x1,
668 and the right-side line only to x2.
669 Otherwise, the horizontal line at y = y1 below would cut off
670 the intersection, which looks ugly:
672 y0 ________________________
677 y1 |######### ########|
680 y2 |######################|
683 y3 |______________________|
684 It is more of a judgment call when the horizontal line is
685 single. We actually choose to cut off the line anyhow, as
686 shown in the first diagram above.
688 bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
689 bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
690 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
691 int horz_line_ofs = double_vert ? double_line_ofs : 0;
692 int xc = (x0 + x3) / 2;
693 int x1 = xc - horz_line_ofs;
694 int x2 = xc + horz_line_ofs;
696 bool shorten_x1_lines = left == RENDER_LINE_DOUBLE;
697 bool shorten_x2_lines = right == RENDER_LINE_DOUBLE;
698 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
699 int vert_line_ofs = double_horz ? double_line_ofs : 0;
700 int yc = (y0 + y3) / 2;
701 int y1 = yc - vert_line_ofs;
702 int y2 = yc + vert_line_ofs;
705 horz_line (xr, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
708 horz_line (xr, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
709 horz_line (xr, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
713 vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
716 vert_line (xr, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
717 vert_line (xr, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
722 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
723 int *min_width, int *max_width)
725 struct xr_driver *xr = xr_;
726 int bb[TABLE_N_AXES][2];
727 int clip[TABLE_N_AXES][2];
734 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
735 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, max_width, &h);
738 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, min_width, &h);
742 xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
744 struct xr_driver *xr = xr_;
745 int bb[TABLE_N_AXES][2];
746 int clip[TABLE_N_AXES][2];
753 clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
754 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
759 xr_draw_cell (void *xr_, const struct table_cell *cell,
760 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
762 struct xr_driver *xr = xr_;
765 xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
768 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
769 and with the given cell OPTIONS for XR. */
771 draw_text (struct xr_driver *xr, const char *string, int x, int y,
772 int max_width, unsigned int options)
774 struct table_cell cell;
775 int bb[TABLE_N_AXES][2];
778 cell.contents = string;
779 cell.options = options;
782 bb[H][1] = x + max_width;
783 bb[V][1] = xr->font_height;
784 xr_layout_cell (xr, &cell, bb, bb, PANGO_WRAP_WORD_CHAR, &w, &h);
788 /* Writes LEFT left-justified and RIGHT right-justified within
789 (X0...X1) at Y. LEFT or RIGHT or both may be null. */
791 draw_header_line (struct xr_driver *xr, const char *left, const char *right,
792 int x0, int x1, int y)
796 right_width = (draw_text (xr, right, x0, y, x1 - x0, TAB_RIGHT)
797 + xr->font_height / 2);
799 draw_text (xr, left, x0, y, x1 - x0 - right_width, TAB_LEFT);
802 /* Draw top of page headers for XR. */
804 draw_headers (struct xr_driver *xr)
810 y = -3 * xr->font_height;
811 x0 = xr->font_height / 2;
812 x1 = xr->width - xr->font_height / 2;
815 cairo_rectangle (xr->cairo, 0, xr_to_pt (y), xr_to_pt (xr->width),
816 xr_to_pt (2 * (xr->font_height
817 + xr->line_width + xr->line_gutter)));
818 cairo_save (xr->cairo);
819 cairo_set_source_rgb (xr->cairo, 0.9, 0.9, 0.9);
820 cairo_fill_preserve (xr->cairo);
821 cairo_restore (xr->cairo);
822 cairo_stroke (xr->cairo);
824 y += xr->line_width + xr->line_gutter;
826 r1 = xasprintf (_("%s - Page %d"), get_start_date (), xr->page_number);
827 r2 = xasprintf ("%s - %s", version, host_system);
829 draw_header_line (xr, xr->title, r1, x0, x1, y);
830 y += xr->font_height;
832 draw_header_line (xr, xr->subtitle, r2, x0, x1, y);
839 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
840 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
841 PangoWrapMode wrap, int *width, int *height)
843 struct xr_font *font;
845 font = (cell->options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
846 : cell->options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
847 : &xr->fonts[XR_FONT_PROPORTIONAL]);
849 pango_layout_set_text (font->layout, cell->contents, -1);
851 pango_layout_set_alignment (
853 ((cell->options & TAB_ALIGNMENT) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
854 : (cell->options & TAB_ALIGNMENT) == TAB_LEFT ? PANGO_ALIGN_LEFT
855 : PANGO_ALIGN_CENTER));
856 pango_layout_set_width (font->layout,
857 bb[H][1] == INT_MAX ? -1 : bb[H][1] - bb[H][0]);
858 pango_layout_set_wrap (font->layout, wrap);
860 if (clip[H][0] != clip[H][1])
862 cairo_save (xr->cairo);
864 if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
866 double x0 = xr_to_pt (clip[H][0]);
867 double y0 = xr_to_pt (clip[V][0] + xr->y);
868 double x1 = xr_to_pt (clip[H][1]);
869 double y1 = xr_to_pt (clip[V][1] + xr->y);
871 cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
872 cairo_clip (xr->cairo);
875 cairo_translate (xr->cairo,
877 xr_to_pt (bb[V][0] + xr->y));
878 pango_cairo_show_layout (xr->cairo, font->layout);
879 cairo_restore (xr->cairo);
882 if (width != NULL || height != NULL)
886 pango_layout_get_size (font->layout, &w, &h);
894 /* Attempts to load FONT, initializing its other members based on
895 its 'string' member and the information in DRIVER. Returns true
896 if successful, otherwise false. */
898 load_font (struct xr_driver *xr, struct xr_font *font)
900 PangoContext *context;
901 PangoLanguage *language;
903 font->desc = pango_font_description_from_string (font->string);
904 if (font->desc == NULL)
906 error (0, 0, _("\"%s\": bad font specification"), font->string);
909 pango_font_description_set_absolute_size (font->desc, xr->font_height);
911 font->layout = pango_cairo_create_layout (xr->cairo);
912 pango_layout_set_font_description (font->layout, font->desc);
914 language = pango_language_get_default ();
915 context = pango_layout_get_context (font->layout);
916 font->metrics = pango_context_get_metrics (context, font->desc, language);
923 free_font (struct xr_font *font)
926 if (font->desc != NULL)
927 pango_font_description_free (font->desc);
928 pango_font_metrics_unref (font->metrics);
929 if (font->layout != NULL)
930 g_object_unref (font->layout);
933 struct output_driver_factory pdf_driver_factory = { "pdf", xr_pdf_create };
934 struct output_driver_factory ps_driver_factory = { "ps", xr_ps_create };
935 struct output_driver_factory svg_driver_factory = { "svg", xr_svg_create };
937 static const struct output_driver_class cairo_driver_class =
945 /* GUI rendering helpers. */
950 struct render_page *page;
951 struct xr_driver *xr;
955 struct chart_item *chart;
958 #define CHART_WIDTH 500
959 #define CHART_HEIGHT 375
962 xr_create_driver (cairo_t *cairo)
964 struct xr_driver *xr;
965 struct string_map map;
967 string_map_init (&map);
968 xr = xr_allocate ("cairo", 0, &map);
969 string_map_destroy (&map);
971 xr->width = INT_MAX / 8;
972 xr->length = INT_MAX / 8;
973 if (!xr_set_cairo (xr, cairo))
975 output_driver_destroy (&xr->driver);
981 static struct xr_rendering *
982 xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr)
984 struct table_item *table_item;
985 struct xr_rendering *r;
987 table_item = table_item_create (table_from_string (0, text), NULL);
988 r = xr_rendering_create (xr, &table_item->output_item, cr);
989 table_item_unref (table_item);
994 struct xr_rendering *
995 xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
998 struct xr_rendering *r = NULL;
1000 if (is_text_item (item))
1001 r = xr_rendering_create_text (xr, text_item_get_text (to_text_item (item)),
1003 else if (is_message_item (item))
1005 const struct message_item *message_item = to_message_item (item);
1006 const struct msg *msg = message_item_get_msg (message_item);
1007 char *s = msg_to_string (msg, NULL);
1008 r = xr_rendering_create_text (xr, s, cr);
1011 else if (is_table_item (item))
1013 r = xzalloc (sizeof *r);
1015 xr_set_cairo (xr, cr);
1016 r->page = xr_render_table_item (xr, to_table_item (item),
1019 else if (is_chart_item (item))
1021 r = xzalloc (sizeof *r);
1022 r->chart = to_chart_item (output_item_ref (item));
1029 xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
1031 if (r->chart == NULL)
1033 *w = render_page_get_size (r->page, H) / 1024;
1034 *h = (render_page_get_size (r->page, V) + r->title_height) / 1024;
1044 xr_rendering_draw (struct xr_rendering *r, cairo_t *cr)
1046 if (r->chart == NULL)
1048 struct xr_driver *xr = r->xr;
1050 xr_set_cairo (xr, cr);
1052 render_page_draw (r->page);
1055 xr_draw_chart (r->chart, cr, 0, 0, CHART_WIDTH, CHART_HEIGHT);
1059 xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
1060 double x, double y, double width, double height)
1062 struct xrchart_geometry geom;
1065 cairo_translate (cr, x, y + height);
1066 cairo_scale (cr, 1.0, -1.0);
1067 xrchart_geometry_init (cr, &geom, width, height);
1068 if (is_boxplot (chart_item))
1069 xrchart_draw_boxplot (chart_item, cr, &geom);
1070 else if (is_histogram_chart (chart_item))
1071 xrchart_draw_histogram (chart_item, cr, &geom);
1072 else if (is_np_plot_chart (chart_item))
1073 xrchart_draw_np_plot (chart_item, cr, &geom);
1074 else if (is_piechart (chart_item))
1075 xrchart_draw_piechart (chart_item, cr, &geom);
1076 else if (is_roc_chart (chart_item))
1077 xrchart_draw_roc (chart_item, cr, &geom);
1078 else if (is_scree (chart_item))
1079 xrchart_draw_scree (chart_item, cr, &geom);
1082 xrchart_geometry_free (cr, &geom);
1088 xr_draw_png_chart (const struct chart_item *item,
1089 const char *file_name_template, int number)
1091 const int width = 640;
1092 const int length = 480;
1094 cairo_surface_t *surface;
1095 cairo_status_t status;
1096 const char *number_pos;
1100 number_pos = strchr (file_name_template, '#');
1101 if (number_pos != NULL)
1102 file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
1103 file_name_template, number, number_pos + 1);
1105 file_name = xstrdup (file_name_template);
1107 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
1108 cr = cairo_create (surface);
1111 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
1112 cairo_rectangle (cr, 0, 0, width, length);
1116 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
1118 xr_draw_chart (item, cr, 0.0, 0.0, width, length);
1120 status = cairo_surface_write_to_png (surface, file_name);
1121 if (status != CAIRO_STATUS_SUCCESS)
1122 error (0, 0, _("error writing output file \"%s\": %s"),
1123 file_name, cairo_status_to_string (status));
1126 cairo_surface_destroy (surface);