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 /* FONT_SCALE is a nasty kluge for an issue that does not make sense. On any
178 surface other than a screen (e.g. for output to PDF or PS or SVG), the fonts
179 are way too big by default. A "9-point" font seems to appear about 16
180 points tall. We use a scale factor for these surfaces to help, but the
181 underlying issue is a mystery. */
182 static struct xr_driver *
183 xr_allocate (const char *name, int device_type, struct string_map *o,
186 struct xr_driver *xr = xzalloc (sizeof *xr);
187 struct output_driver *d = &xr->driver;
189 output_driver_init (d, &cairo_driver_class, name, device_type);
191 /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
192 const double scale = XR_POINT / 1000.;
194 int paper[TABLE_N_AXES];
195 parse_paper_size (opt (d, o, "paper-size", ""), &paper[H], &paper[V]);
196 for (int a = 0; a < TABLE_N_AXES; a++)
199 int margins[TABLE_N_AXES][2];
200 margins[H][0] = parse_dimension (opt (d, o, "left-margin", ".5in")) * scale;
201 margins[H][1] = parse_dimension (opt (d, o, "right-margin", ".5in")) * scale;
202 margins[V][0] = parse_dimension (opt (d, o, "top-margin", ".5in")) * scale;
203 margins[V][1] = parse_dimension (opt (d, o, "bottom-margin", ".5in")) * scale;
205 int size[TABLE_N_AXES];
206 for (int a = 0; a < TABLE_N_AXES; a++)
207 size[a] = paper[a] - margins[a][0] - margins[a][1];
209 int min_break[TABLE_N_AXES];
210 min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
211 min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
212 for (int a = 0; a < TABLE_N_AXES; a++)
213 if (min_break[a] <= 0)
214 min_break[a] = size[a] / 2;
216 int font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
217 PangoFontDescription *fixed_font = parse_font_option
218 (d, o, "fixed-font", "monospace", font_size, false, false);
219 PangoFontDescription *proportional_font = parse_font_option (
220 d, o, "prop-font", "sans serif", font_size, false, false);
222 struct cell_color fg = parse_color (opt (d, o, "foreground-color", "black"));
224 bool transparent = parse_boolean (opt (d, o, "transparent", "false"));
225 struct cell_color bg = (transparent
226 ? (struct cell_color) { .alpha = 0 }
227 : parse_color (opt (d, o, "background-color",
230 bool systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
233 = parse_dimension (opt (d, o, "object-spacing", NULL)) * scale;
234 if (object_spacing <= 0)
235 object_spacing = XR_POINT * 12;
237 xr->page_style = xmalloc (sizeof *xr->page_style);
238 *xr->page_style = (struct xr_page_style) {
241 .size = { [H] = size[H], [V] = size[V] },
243 [H] = { margins[H][0], margins[H][1], },
244 [V] = { margins[V][0], margins[V][1], },
247 .font = pango_font_description_copy (proportional_font),
250 .font_scale = font_scale,
251 .initial_page_number = 1,
252 .object_spacing = object_spacing,
255 xr->fsm_style = xmalloc (sizeof *xr->fsm_style);
256 *xr->fsm_style = (struct xr_fsm_style) {
258 .size = { [H] = size[H], [V] = size[V] },
259 .min_break = { [H] = min_break[H], [V] = min_break[V] },
261 [XR_FONT_PROPORTIONAL] = proportional_font,
262 [XR_FONT_FIXED] = fixed_font,
265 .use_system_colors = systemcolors,
266 .transparent = transparent,
267 .font_scale = font_scale,
273 static struct output_driver *
274 xr_create (struct file_handle *fh, enum settings_output_devices device_type,
275 struct string_map *o, enum xr_output_type file_type)
277 const char *file_name = fh_get_file_name (fh);
278 struct xr_driver *xr = xr_allocate (file_name, device_type, o, 72.0 / 128.0);
280 double paper[TABLE_N_AXES];
281 for (int a = 0; a < TABLE_N_AXES; a++)
282 paper[a] = xr_to_pt (xr_page_style_paper_size (xr->page_style, a));
283 if (file_type == XR_PDF)
284 xr->surface = cairo_pdf_surface_create (file_name, paper[H], paper[V]);
285 else if (file_type == XR_PS)
286 xr->surface = cairo_ps_surface_create (file_name, paper[H], paper[V]);
287 else if (file_type == XR_SVG)
288 xr->surface = cairo_svg_surface_create (file_name, paper[H], paper[V]);
292 cairo_status_t status = cairo_surface_status (xr->surface);
293 if (status != CAIRO_STATUS_SUCCESS)
295 msg (ME, _("error opening output file `%s': %s"),
296 file_name, cairo_status_to_string (status));
305 output_driver_destroy (&xr->driver);
309 static struct output_driver *
310 xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
311 struct string_map *o)
313 return xr_create (fh, device_type, o, XR_PDF);
316 static struct output_driver *
317 xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
318 struct string_map *o)
320 return xr_create (fh, device_type, o, XR_PS);
323 static struct output_driver *
324 xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
325 struct string_map *o)
327 return xr_create (fh, device_type, o, XR_SVG);
331 xr_destroy (struct output_driver *driver)
333 struct xr_driver *xr = xr_driver_cast (driver);
337 cairo_surface_finish (xr->surface);
338 cairo_status_t status = cairo_surface_status (xr->surface);
339 if (status != CAIRO_STATUS_SUCCESS)
340 fprintf (stderr, _("error drawing output for %s driver: %s"),
341 output_driver_get_name (driver),
342 cairo_status_to_string (status));
343 cairo_surface_destroy (xr->surface);
346 xr_pager_destroy (xr->pager);
347 xr_page_style_unref (xr->page_style);
348 xr_fsm_style_unref (xr->fsm_style);
353 xr_flush (struct output_driver *driver)
355 struct xr_driver *xr = xr_driver_cast (driver);
358 cairo_surface_flush (xr->surface);
362 xr_update_page_setup (struct output_driver *driver,
363 const struct page_setup *ps)
365 struct xr_driver *xr = xr_driver_cast (driver);
367 const double scale = 72 * XR_POINT;
369 int swap = ps->orientation == PAGE_LANDSCAPE;
370 enum table_axis h = H ^ swap;
371 enum table_axis v = V ^ swap;
373 struct xr_page_style *page_style = xmalloc (sizeof *page_style);
374 *page_style = (struct xr_page_style) {
378 [H] = (ps->paper[h] - ps->margins[h][0] - ps->margins[h][1]) * scale,
379 [V] = (ps->paper[v] - ps->margins[v][0] - ps->margins[v][1]) * scale,
382 [H] = { ps->margins[h][0] * scale, ps->margins[h][1] * scale },
383 [V] = { ps->margins[v][0] * scale, ps->margins[v][1] * scale },
386 .font = pango_font_description_copy (xr->page_style->font),
388 .bg = xr->page_style->bg,
389 .font_scale = xr->page_style->font_scale,
390 .initial_page_number = ps->initial_page_number,
391 .object_spacing = ps->object_spacing * 72 * XR_POINT,
394 for (size_t i = 0; i < 2; i++)
395 page_heading_copy (&page_style->headings[i], &ps->headings[i]);
397 xr_page_style_unref (xr->page_style);
398 xr->page_style = page_style;
402 xr_submit (struct output_driver *driver, const struct output_item *output_item)
404 struct xr_driver *xr = xr_driver_cast (driver);
406 if (is_page_setup_item (output_item))
408 xr_update_page_setup (driver,
409 to_page_setup_item (output_item)->page_setup);
415 xr->pager = xr_pager_create (xr->page_style);
416 xr_pager_add_page (xr->pager, cairo_create (xr->surface));
419 xr_pager_add_item (xr->pager, xr->fsm_style, output_item);
420 while (xr_pager_needs_new_page (xr->pager))
422 cairo_surface_show_page (xr->surface);
423 xr_pager_add_page (xr->pager, cairo_create (xr->surface));
427 struct output_driver_factory pdf_driver_factory =
428 { "pdf", "pspp.pdf", xr_pdf_create };
429 struct output_driver_factory ps_driver_factory =
430 { "ps", "pspp.ps", xr_ps_create };
431 struct output_driver_factory svg_driver_factory =
432 { "svg", "pspp.svg", xr_svg_create };
434 static const struct output_driver_class cairo_driver_class =