1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "output/cairo.h"
21 #include "libpspp/assertion.h"
22 #include "libpspp/cast.h"
23 #include "libpspp/hash-functions.h"
24 #include "libpspp/message.h"
25 #include "libpspp/pool.h"
26 #include "libpspp/start-date.h"
27 #include "libpspp/str.h"
28 #include "libpspp/string-map.h"
29 #include "libpspp/version.h"
30 #include "data/file-handle-def.h"
31 #include "output/cairo-chart.h"
32 #include "output/cairo-fsm.h"
33 #include "output/chart-item-provider.h"
34 #include "output/driver-provider.h"
35 #include "output/group-item.h"
36 #include "output/message-item.h"
37 #include "output/options.h"
38 #include "output/page-eject-item.h"
39 #include "output/page-setup-item.h"
40 #include "output/render.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>
49 #include <cairo/cairo.h>
52 #include <pango/pango-font.h>
53 #include <pango/pango-layout.h>
54 #include <pango/pango.h>
55 #include <pango/pangocairo.h>
58 #include "gl/c-ctype.h"
59 #include "gl/c-strcase.h"
60 #include "gl/intprops.h"
61 #include "gl/minmax.h"
62 #include "gl/xalloc.h"
65 #define _(msgid) gettext (msgid)
67 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
71 /* The unit used for internal measurements is inch/(72 * XR_POINT).
72 (Thus, XR_POINT units represent one point.) */
73 #define XR_POINT PANGO_SCALE
75 /* Conversions to and from points. */
79 return x / (double) XR_POINT;
82 /* Dimensions for drawing lines in tables. */
83 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
84 #define XR_LINE_SPACE XR_POINT /* Space between double lines. */
94 /* Cairo output driver. */
97 struct output_driver driver;
99 /* User parameters. */
100 PangoFontDescription *fonts[XR_N_FONTS];
102 /* Measurements all in inch/(72 * XR_POINT). */
103 int size[TABLE_N_AXES]; /* Page size with margins subtracted. */
104 int margins[TABLE_N_AXES][2]; /* Margins. */
105 int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
106 int object_spacing; /* Space between output objects. */
108 struct cell_color bg; /* Background color */
109 struct cell_color fg; /* Foreground color */
110 bool transparent; /* true -> do not render background */
111 bool systemcolors; /* true -> do not change colors */
113 int initial_page_number;
115 struct page_heading headings[2]; /* Top and bottom headings. */
116 int headings_height[2];
118 /* Internal state. */
119 struct xr_fsm_style *style;
121 int char_width, char_height;
123 cairo_surface_t *surface;
124 int page_number; /* Current page number. */
129 static const struct output_driver_class cairo_driver_class;
131 static void xr_driver_destroy_fsm (struct xr_driver *);
132 static void xr_driver_run_fsm (struct xr_driver *);
134 /* Output driver basics. */
136 static struct xr_driver *
137 xr_driver_cast (struct output_driver *driver)
139 assert (driver->class == &cairo_driver_class);
140 return UP_CAST (driver, struct xr_driver, driver);
143 static struct driver_option *
144 opt (struct output_driver *d, struct string_map *options, const char *key,
145 const char *default_value)
147 return driver_option_get (d, options, key, default_value);
150 static PangoFontDescription *
151 parse_font (const char *font, int default_size, bool bold, bool italic)
153 if (!c_strcasecmp (font, "Monospaced"))
156 PangoFontDescription *desc = pango_font_description_from_string (font);
160 /* If the font description didn't include an explicit font size, then set it
161 to DEFAULT_SIZE, which is in inch/72000 units. */
162 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
163 pango_font_description_set_size (desc,
164 (default_size / 1000.0) * PANGO_SCALE);
166 pango_font_description_set_weight (desc, (bold
168 : PANGO_WEIGHT_NORMAL));
169 pango_font_description_set_style (desc, (italic
171 : PANGO_STYLE_NORMAL));
176 static PangoFontDescription *
177 parse_font_option (struct output_driver *d, struct string_map *options,
178 const char *key, const char *default_value,
179 int default_size, bool bold, bool italic)
181 char *string = parse_string (opt (d, options, key, default_value));
182 PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
185 msg (MW, _("`%s': bad font specification"), string);
187 /* Fall back to DEFAULT_VALUE, which had better be a valid font
189 desc = parse_font (default_value, default_size, bold, italic);
190 assert (desc != NULL);
198 apply_options (struct xr_driver *xr, struct string_map *o)
200 struct output_driver *d = &xr->driver;
202 /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
204 /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
205 const double scale = XR_POINT / 1000.;
207 for (int i = 0; i < XR_N_FONTS; i++)
208 if (xr->fonts[i] != NULL)
209 pango_font_description_free (xr->fonts[i]);
211 int font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
212 xr->fonts[XR_FONT_FIXED] = parse_font_option
213 (d, o, "fixed-font", "monospace", font_size, false, false);
214 xr->fonts[XR_FONT_PROPORTIONAL] = parse_font_option (
215 d, o, "prop-font", "sans serif", font_size, false, false);
217 xr->fg = parse_color (opt (d, o, "foreground-color", "#000000000000"));
218 xr->bg = parse_color (opt (d, o, "background-color", "#FFFFFFFFFFFF"));
220 xr->transparent = parse_boolean (opt (d, o, "transparent", "false"));
221 xr->systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
223 /* Get dimensions. */
224 int paper[TABLE_N_AXES];
225 parse_paper_size (opt (d, o, "paper-size", ""), &paper[H], &paper[V]);
227 int margins[TABLE_N_AXES][2];
228 margins[H][0] = parse_dimension (opt (d, o, "left-margin", ".5in"));
229 margins[H][1] = parse_dimension (opt (d, o, "right-margin", ".5in"));
230 margins[V][0] = parse_dimension (opt (d, o, "top-margin", ".5in"));
231 margins[V][1] = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
233 int min_break[TABLE_N_AXES];
234 min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
235 min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
237 int object_spacing = (parse_dimension (opt (d, o, "object-spacing", NULL))
240 /* Convert to inch/(XR_POINT * 72). */
241 for (int a = 0; a < TABLE_N_AXES; a++)
243 for (int i = 0; i < 2; i++)
244 xr->margins[a][i] = margins[a][i] * scale;
245 xr->size[a] = (paper[a] - margins[a][0] - margins[a][1]) * scale;
246 xr->min_break[a] = min_break[a] >= 0 ? min_break[a] : xr->size[a] / 2;
248 xr->object_spacing = object_spacing >= 0 ? object_spacing : XR_POINT * 12;
250 /* There are no headings so headings_height can stay 0. */
253 static struct xr_driver *
254 xr_allocate (const char *name, int device_type, struct string_map *o,
257 struct xr_driver *xr = xzalloc (sizeof *xr);
258 struct output_driver *d = &xr->driver;
260 output_driver_init (d, &cairo_driver_class, name, device_type);
262 /* This is a nasty kluge for an issue that does not make sense. On any
263 surface other than a screen (e.g. for output to PDF or PS or SVG), the
264 fonts are way too big by default. A "9-point" font seems to appear about
265 16 points tall. We use a scale factor for these surfaces to help, but the
266 underlying issue is a mystery. */
267 xr->font_scale = font_scale;
269 apply_options (xr, o);
275 pango_to_xr (int pango)
277 return (XR_POINT != PANGO_SCALE
278 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
285 return (XR_POINT != PANGO_SCALE
286 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
291 xr_measure_fonts (cairo_t *cairo, PangoFontDescription *fonts[XR_N_FONTS],
292 int *char_width, int *char_height)
296 for (int i = 0; i < XR_N_FONTS; i++)
298 PangoLayout *layout = pango_cairo_create_layout (cairo);
299 pango_layout_set_font_description (layout, fonts[i]);
301 pango_layout_set_text (layout, "0", 1);
304 pango_layout_get_size (layout, &cw, &ch);
305 *char_width = MAX (*char_width, pango_to_xr (cw));
306 *char_height = MAX (*char_height, pango_to_xr (ch));
308 g_object_unref (G_OBJECT (layout));
313 get_layout_height (PangoLayout *layout)
316 pango_layout_get_size (layout, &w, &h);
321 xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
322 const struct page_heading *ph, int page_number,
323 int width, bool draw, int base_y)
325 PangoLayout *layout = pango_cairo_create_layout (cairo);
326 pango_layout_set_font_description (layout, font);
329 for (size_t i = 0; i < ph->n; i++)
331 const struct page_paragraph *pp = &ph->paragraphs[i];
333 char *markup = output_driver_substitute_heading_vars (pp->markup,
335 pango_layout_set_markup (layout, markup, -1);
338 pango_layout_set_alignment (
340 (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
341 : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER
342 : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
343 : PANGO_ALIGN_RIGHT));
344 pango_layout_set_width (layout, xr_to_pango (width));
348 cairo_translate (cairo, 0, xr_to_pt (y + base_y));
349 pango_cairo_show_layout (cairo, layout);
350 cairo_restore (cairo);
353 y += pango_to_xr (get_layout_height (layout));
356 g_object_unref (G_OBJECT (layout));
362 xr_measure_headings (cairo_surface_t *surface,
363 const PangoFontDescription *font,
364 const struct page_heading headings[2],
365 int width, int object_spacing, int height[2])
367 cairo_t *cairo = cairo_create (surface);
369 for (int i = 0; i < 2; i++)
371 int h = xr_render_page_heading (cairo, font, &headings[i], -1,
374 /* If the top heading is nonempty, add some space below it. */
382 cairo_destroy (cairo);
387 xr_check_fonts (cairo_surface_t *surface,
388 PangoFontDescription *fonts[XR_N_FONTS],
389 int usable_width, int usable_length)
391 cairo_t *cairo = cairo_create (surface);
392 int char_width, char_height;
393 xr_measure_fonts (cairo, fonts, &char_width, &char_height);
394 cairo_destroy (cairo);
397 enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
398 if (usable_width / char_width < MIN_WIDTH)
400 msg (ME, _("The defined page is not wide enough to hold at least %d "
401 "characters in the default font. In fact, there's only "
402 "room for %d characters."),
403 MIN_WIDTH, usable_width / char_width);
406 if (usable_length / char_height < MIN_LENGTH)
408 msg (ME, _("The defined page is not long enough to hold at least %d "
409 "lines in the default font. In fact, there's only "
410 "room for %d lines."),
411 MIN_LENGTH, usable_length / char_height);
418 xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
422 cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
424 xr_measure_fonts (xr->cairo, xr->fonts, &xr->char_width, &xr->char_height);
426 if (xr->style == NULL)
428 xr->style = xmalloc (sizeof *xr->style);
429 *xr->style = (struct xr_fsm_style) {
431 .size = { [H] = xr->size[H], [V] = xr->size[V] },
432 .min_break = { [H] = xr->min_break[H], [V] = xr->min_break[V] },
433 .use_system_colors = xr->systemcolors,
434 .transparent = xr->transparent,
435 .font_scale = xr->font_scale,
438 for (size_t i = 0; i < XR_N_FONTS; i++)
439 xr->style->fonts[i] = pango_font_description_copy (xr->fonts[i]);
442 if (!xr->systemcolors)
443 cairo_set_source_rgb (xr->cairo,
444 xr->fg.r / 255.0, xr->fg.g / 255.0, xr->fg.b / 255.0);
447 static struct output_driver *
448 xr_create (struct file_handle *fh, enum settings_output_devices device_type,
449 struct string_map *o, enum xr_output_type file_type)
451 const char *file_name = fh_get_file_name (fh);
452 struct xr_driver *xr = xr_allocate (file_name, device_type, o, 72.0 / 128.0);
454 double paper_pt[TABLE_N_AXES];
455 for (int a = 0; a < TABLE_N_AXES; a++)
456 paper_pt[a] = xr_to_pt (xr->size[a]
457 + xr->margins[a][0] + xr->margins[a][1]);
458 if (file_type == XR_PDF)
459 xr->surface = cairo_pdf_surface_create (file_name,
460 paper_pt[H], paper_pt[V]);
461 else if (file_type == XR_PS)
462 xr->surface = cairo_ps_surface_create (file_name, paper_pt[H], paper_pt[V]);
463 else if (file_type == XR_SVG)
464 xr->surface = cairo_svg_surface_create (file_name,
465 paper_pt[H], paper_pt[V]);
469 cairo_status_t status = cairo_surface_status (xr->surface);
470 if (status != CAIRO_STATUS_SUCCESS)
472 msg (ME, _("error opening output file `%s': %s"),
473 file_name, cairo_status_to_string (status));
477 if (!xr_check_fonts (xr->surface, xr->fonts, xr->size[H], xr->size[V]))
485 output_driver_destroy (&xr->driver);
489 static struct output_driver *
490 xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
491 struct string_map *o)
493 return xr_create (fh, device_type, o, XR_PDF);
496 static struct output_driver *
497 xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
498 struct string_map *o)
500 return xr_create (fh, device_type, o, XR_PS);
503 static struct output_driver *
504 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
505 struct string_map *o)
507 return xr_create (fh, device_type, o, XR_SVG);
511 xr_destroy (struct output_driver *driver)
513 struct xr_driver *xr = xr_driver_cast (driver);
516 xr_driver_destroy_fsm (xr);
518 if (xr->cairo != NULL)
520 cairo_surface_finish (xr->surface);
521 cairo_status_t status = cairo_status (xr->cairo);
522 if (status != CAIRO_STATUS_SUCCESS)
523 fprintf (stderr, _("error drawing output for %s driver: %s"),
524 output_driver_get_name (driver),
525 cairo_status_to_string (status));
526 cairo_surface_destroy (xr->surface);
528 cairo_destroy (xr->cairo);
531 for (i = 0; i < XR_N_FONTS; i++)
532 if (xr->fonts[i] != NULL)
533 pango_font_description_free (xr->fonts[i]);
535 xr_fsm_style_unref (xr->style);
540 xr_flush (struct output_driver *driver)
542 struct xr_driver *xr = xr_driver_cast (driver);
544 cairo_surface_flush (cairo_get_target (xr->cairo));
548 xr_update_page_setup (struct output_driver *driver,
549 const struct page_setup *ps)
551 struct xr_driver *xr = xr_driver_cast (driver);
553 xr->initial_page_number = ps->initial_page_number;
554 xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
559 int size[TABLE_N_AXES];
560 for (int a = 0; a < TABLE_N_AXES; a++)
562 double total_margin = ps->margins[a][0] + ps->margins[a][1];
563 size[a] = (ps->paper[a] - total_margin) * 72 * XR_POINT;
566 int headings_height[2];
567 size[V] -= xr_measure_headings (
568 xr->surface, xr->fonts[XR_FONT_PROPORTIONAL], ps->headings,
569 size[H], xr->object_spacing, headings_height);
571 int swap = ps->orientation == PAGE_LANDSCAPE;
572 enum table_axis h = H ^ swap;
573 enum table_axis v = V ^ swap;
574 if (!xr_check_fonts (xr->surface, xr->fonts, size[h], size[v]))
577 for (int i = 0; i < 2; i++)
579 page_heading_uninit (&xr->headings[i]);
580 page_heading_copy (&xr->headings[i], &ps->headings[i]);
581 xr->headings_height[i] = headings_height[i];
584 for (int a = 0; a < TABLE_N_AXES; a++)
586 xr->size[a] = size[a ^ swap];
587 for (int i = 0; i < 2; i++)
588 xr->margins[a][i] = ps->margins[a ^ swap][i] * 72 * XR_POINT;
590 cairo_pdf_surface_set_size (xr->surface,
591 ps->paper[h] * 72.0, ps->paper[v] * 72.0);
595 xr_submit (struct output_driver *driver, const struct output_item *output_item)
597 struct xr_driver *xr = xr_driver_cast (driver);
599 if (is_page_setup_item (output_item))
601 xr_update_page_setup (driver,
602 to_page_setup_item (output_item)->page_setup);
608 xr->page_number = xr->initial_page_number - 1;
609 xr_set_cairo (xr, cairo_create (xr->surface));
610 cairo_save (xr->cairo);
611 xr_driver_next_page (xr, xr->cairo);
614 xr_driver_output_item (xr, output_item);
615 while (xr_driver_need_new_page (xr))
617 cairo_restore (xr->cairo);
618 cairo_show_page (xr->cairo);
619 cairo_save (xr->cairo);
620 xr_driver_next_page (xr, xr->cairo);
624 /* Functions for rendering a series of output items to a series of Cairo
625 contexts, with pagination.
627 Used by PSPPIRE for printing, and by the basic Cairo output driver above as
628 its underlying implementation.
630 See the big comment in cairo.h for intended usage. */
632 /* Gives new page CAIRO to XR for output. */
634 xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
636 if (!xr->transparent)
639 cairo_set_source_rgb (cairo,
640 xr->bg.r / 255.0, xr->bg.g / 255.0, xr->bg.b / 255.0);
641 cairo_rectangle (cairo, 0, 0, xr->size[H], xr->size[V]);
643 cairo_restore (cairo);
645 cairo_translate (cairo,
646 xr_to_pt (xr->margins[H][0]),
647 xr_to_pt (xr->margins[V][0] + xr->headings_height[0]));
653 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL],
654 &xr->headings[0], xr->page_number, xr->size[H], true,
655 -xr->headings_height[0]);
656 xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL],
657 &xr->headings[1], xr->page_number, xr->size[H], true,
660 xr_driver_run_fsm (xr);
663 /* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
664 rendering a previous output item, that is, only if xr_driver_need_new_page()
667 xr_driver_output_item (struct xr_driver *xr,
668 const struct output_item *output_item)
670 assert (xr->fsm == NULL);
671 xr->fsm = xr_fsm_create (output_item, xr->style, xr->cairo);
672 xr_driver_run_fsm (xr);
675 /* Returns true if XR is in the middle of rendering an output item and needs a
676 new page to be appended using xr_driver_next_page() to make progress,
679 xr_driver_need_new_page (const struct xr_driver *xr)
681 return xr->fsm != NULL;
684 /* Returns true if the current page doesn't have any content yet. */
686 xr_driver_is_page_blank (const struct xr_driver *xr)
692 xr_driver_destroy_fsm (struct xr_driver *xr)
694 xr_fsm_destroy (xr->fsm);
699 xr_driver_run_fsm (struct xr_driver *xr)
703 cairo_save (xr->cairo);
704 cairo_translate (xr->cairo, 0, xr_to_pt (xr->y));
705 int used = xr_fsm_draw_slice (xr->fsm, xr->cairo, xr->size[V] - xr->y);
707 cairo_restore (xr->cairo);
709 if (xr_fsm_is_empty (xr->fsm))
710 xr_driver_destroy_fsm (xr);
714 struct output_driver_factory pdf_driver_factory =
715 { "pdf", "pspp.pdf", xr_pdf_create };
716 struct output_driver_factory ps_driver_factory =
717 { "ps", "pspp.ps", xr_ps_create };
718 struct output_driver_factory svg_driver_factory =
719 { "svg", "pspp.svg", xr_svg_create };
721 static const struct output_driver_class cairo_driver_class =
730 xr_driver_create (cairo_t *cairo, struct string_map *options)
732 struct xr_driver *xr = xr_allocate ("cairo", 0, options, 1.0);
733 xr_set_cairo (xr, cairo);
737 /* Destroy XR, which should have been created with xr_driver_create(). Any
738 cairo_t added to XR is not destroyed, because it is owned by the client. */
740 xr_driver_destroy (struct xr_driver *xr)
745 output_driver_destroy (&xr->driver);