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/cairo-pager.h"
34 #include "output/chart-item-provider.h"
35 #include "output/driver-provider.h"
36 #include "output/group-item.h"
37 #include "output/message-item.h"
38 #include "output/options.h"
39 #include "output/page-eject-item.h"
40 #include "output/page-setup-item.h"
41 #include "output/render.h"
42 #include "output/table-item.h"
43 #include "output/table.h"
44 #include "output/text-item.h"
46 #include <cairo/cairo-pdf.h>
47 #include <cairo/cairo-ps.h>
48 #include <cairo/cairo-svg.h>
50 #include <cairo/cairo.h>
53 #include <pango/pango-font.h>
54 #include <pango/pango-layout.h>
55 #include <pango/pango.h>
56 #include <pango/pangocairo.h>
59 #include "gl/c-ctype.h"
60 #include "gl/c-strcase.h"
61 #include "gl/intprops.h"
62 #include "gl/minmax.h"
63 #include "gl/xalloc.h"
66 #define _(msgid) gettext (msgid)
68 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
72 /* The unit used for internal measurements is inch/(72 * XR_POINT).
73 (Thus, XR_POINT units represent one point.) */
74 #define XR_POINT PANGO_SCALE
76 /* Conversions to and from points. */
80 return x / (double) XR_POINT;
83 /* Dimensions for drawing lines in tables. */
84 #define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
85 #define XR_LINE_SPACE XR_POINT /* Space between double lines. */
95 /* Cairo output driver. */
98 struct output_driver driver;
100 struct xr_fsm_style *fsm_style;
101 struct xr_page_style *page_style;
102 struct xr_pager *pager;
104 /* Internal state. */
105 cairo_surface_t *surface;
106 int page_number; /* Current page number. */
111 static const struct output_driver_class cairo_driver_class;
114 /* Output driver basics. */
116 static struct xr_driver *
117 xr_driver_cast (struct output_driver *driver)
119 assert (driver->class == &cairo_driver_class);
120 return UP_CAST (driver, struct xr_driver, driver);
123 static struct driver_option *
124 opt (struct output_driver *d, struct string_map *options, const char *key,
125 const char *default_value)
127 return driver_option_get (d, options, key, default_value);
130 static PangoFontDescription *
131 parse_font (const char *font, int default_size, bool bold, bool italic)
133 if (!c_strcasecmp (font, "Monospaced"))
136 PangoFontDescription *desc = pango_font_description_from_string (font);
140 /* If the font description didn't include an explicit font size, then set it
141 to DEFAULT_SIZE, which is in inch/72000 units. */
142 if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
143 pango_font_description_set_size (desc,
144 (default_size / 1000.0) * PANGO_SCALE);
146 pango_font_description_set_weight (desc, (bold
148 : PANGO_WEIGHT_NORMAL));
149 pango_font_description_set_style (desc, (italic
151 : PANGO_STYLE_NORMAL));
156 static PangoFontDescription *
157 parse_font_option (struct output_driver *d, struct string_map *options,
158 const char *key, const char *default_value,
159 int default_size, bool bold, bool italic)
161 char *string = parse_string (opt (d, options, key, default_value));
162 PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
165 msg (MW, _("`%s': bad font specification"), string);
167 /* Fall back to DEFAULT_VALUE, which had better be a valid font
169 desc = parse_font (default_value, default_size, bold, italic);
170 assert (desc != NULL);
177 static struct xr_driver *
178 xr_allocate (const char *name, int device_type, struct string_map *o)
180 struct xr_driver *xr = xzalloc (sizeof *xr);
181 struct output_driver *d = &xr->driver;
183 output_driver_init (d, &cairo_driver_class, name, device_type);
185 /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
186 const double scale = XR_POINT / 1000.;
188 int paper[TABLE_N_AXES];
189 parse_paper_size (opt (d, o, "paper-size", ""), &paper[H], &paper[V]);
190 for (int a = 0; a < TABLE_N_AXES; a++)
193 int margins[TABLE_N_AXES][2];
194 margins[H][0] = parse_dimension (opt (d, o, "left-margin", ".5in")) * scale;
195 margins[H][1] = parse_dimension (opt (d, o, "right-margin", ".5in")) * scale;
196 margins[V][0] = parse_dimension (opt (d, o, "top-margin", ".5in")) * scale;
197 margins[V][1] = parse_dimension (opt (d, o, "bottom-margin", ".5in")) * scale;
199 int size[TABLE_N_AXES];
200 for (int a = 0; a < TABLE_N_AXES; a++)
201 size[a] = paper[a] - margins[a][0] - margins[a][1];
203 int min_break[TABLE_N_AXES];
204 min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
205 min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
206 for (int a = 0; a < TABLE_N_AXES; a++)
207 if (min_break[a] <= 0)
208 min_break[a] = size[a] / 2;
210 int font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
211 PangoFontDescription *fixed_font = parse_font_option
212 (d, o, "fixed-font", "monospace", font_size, false, false);
213 PangoFontDescription *proportional_font = parse_font_option (
214 d, o, "prop-font", "sans serif", font_size, false, false);
216 struct cell_color fg = parse_color (opt (d, o, "foreground-color", "black"));
218 bool transparent = parse_boolean (opt (d, o, "transparent", "false"));
219 struct cell_color bg = (transparent
220 ? (struct cell_color) { .alpha = 0 }
221 : parse_color (opt (d, o, "background-color",
224 bool systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
227 = parse_dimension (opt (d, o, "object-spacing", NULL)) * scale;
228 if (object_spacing <= 0)
229 object_spacing = XR_POINT * 12;
231 xr->page_style = xmalloc (sizeof *xr->page_style);
232 *xr->page_style = (struct xr_page_style) {
236 [H] = { margins[H][0], margins[H][1], },
237 [V] = { margins[V][0], margins[V][1], },
241 .initial_page_number = 1,
242 .object_spacing = object_spacing,
245 xr->fsm_style = xmalloc (sizeof *xr->fsm_style);
246 *xr->fsm_style = (struct xr_fsm_style) {
248 .size = { [H] = size[H], [V] = size[V] },
249 .min_break = { [H] = min_break[H], [V] = min_break[V] },
251 [XR_FONT_PROPORTIONAL] = proportional_font,
252 [XR_FONT_FIXED] = fixed_font,
255 .use_system_colors = systemcolors,
256 .transparent = transparent,
257 .font_resolution = 72.0,
263 static struct output_driver *
264 xr_create (struct file_handle *fh, enum settings_output_devices device_type,
265 struct string_map *o, enum xr_output_type file_type)
267 const char *file_name = fh_get_file_name (fh);
268 struct xr_driver *xr = xr_allocate (file_name, device_type, o);
270 double paper[TABLE_N_AXES];
271 for (int a = 0; a < TABLE_N_AXES; a++)
272 paper[a] = xr_to_pt (xr_page_style_paper_size (xr->page_style,
274 if (file_type == XR_PDF)
275 xr->surface = cairo_pdf_surface_create (file_name, paper[H], paper[V]);
276 else if (file_type == XR_PS)
277 xr->surface = cairo_ps_surface_create (file_name, paper[H], paper[V]);
278 else if (file_type == XR_SVG)
279 xr->surface = cairo_svg_surface_create (file_name, paper[H], paper[V]);
283 cairo_status_t status = cairo_surface_status (xr->surface);
284 if (status != CAIRO_STATUS_SUCCESS)
286 msg (ME, _("error opening output file `%s': %s"),
287 file_name, cairo_status_to_string (status));
296 output_driver_destroy (&xr->driver);
300 static struct output_driver *
301 xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
302 struct string_map *o)
304 return xr_create (fh, device_type, o, XR_PDF);
307 static struct output_driver *
308 xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
309 struct string_map *o)
311 return xr_create (fh, device_type, o, XR_PS);
314 static struct output_driver *
315 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
316 struct string_map *o)
318 return xr_create (fh, device_type, o, XR_SVG);
322 xr_destroy (struct output_driver *driver)
324 struct xr_driver *xr = xr_driver_cast (driver);
328 cairo_surface_finish (xr->surface);
329 cairo_status_t status = cairo_surface_status (xr->surface);
330 if (status != CAIRO_STATUS_SUCCESS)
331 fprintf (stderr, _("error drawing output for %s driver: %s"),
332 output_driver_get_name (driver),
333 cairo_status_to_string (status));
334 cairo_surface_destroy (xr->surface);
337 xr_pager_destroy (xr->pager);
338 xr_page_style_unref (xr->page_style);
339 xr_fsm_style_unref (xr->fsm_style);
344 xr_flush (struct output_driver *driver)
346 struct xr_driver *xr = xr_driver_cast (driver);
349 cairo_surface_flush (xr->surface);
353 xr_update_page_setup (struct output_driver *driver,
354 const struct page_setup *ps)
356 struct xr_driver *xr = xr_driver_cast (driver);
358 const double scale = 72 * XR_POINT;
360 int swap = ps->orientation == PAGE_LANDSCAPE;
361 enum table_axis h = H ^ swap;
362 enum table_axis v = V ^ swap;
364 struct xr_page_style *page_style = xmalloc (sizeof *page_style);
365 *page_style = (struct xr_page_style) {
369 [H] = { ps->margins[h][0] * scale, ps->margins[h][1] * scale },
370 [V] = { ps->margins[v][0] * scale, ps->margins[v][1] * scale },
373 .bg = xr->page_style->bg,
374 .initial_page_number = ps->initial_page_number,
375 .object_spacing = ps->object_spacing * 72 * XR_POINT,
378 for (size_t i = 0; i < 2; i++)
379 page_heading_copy (&page_style->headings[i], &ps->headings[i]);
381 xr_page_style_unref (xr->page_style);
382 xr->page_style = page_style;
386 xr_submit (struct output_driver *driver, const struct output_item *output_item)
388 struct xr_driver *xr = xr_driver_cast (driver);
390 if (is_page_setup_item (output_item))
392 xr_update_page_setup (driver,
393 to_page_setup_item (output_item)->page_setup);
399 xr->pager = xr_pager_create (xr->page_style, xr->fsm_style);
400 xr_pager_add_page (xr->pager, cairo_create (xr->surface));
403 xr_pager_add_item (xr->pager, output_item);
404 while (xr_pager_needs_new_page (xr->pager))
406 cairo_surface_show_page (xr->surface);
407 xr_pager_add_page (xr->pager, cairo_create (xr->surface));
411 struct output_driver_factory pdf_driver_factory =
412 { "pdf", "pspp.pdf", xr_pdf_create };
413 struct output_driver_factory ps_driver_factory =
414 { "ps", "pspp.ps", xr_ps_create };
415 struct output_driver_factory svg_driver_factory =
416 { "svg", "pspp.svg", xr_svg_create };
418 static const struct output_driver_class cairo_driver_class =