/* PSPP - a program for statistical analysis.
- Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <cairo/cairo-ps.h>
#include <cairo/cairo-svg.h>
#include <cairo/cairo.h>
+#include <math.h>
#include <pango/pango-font.h>
#include <pango/pango-layout.h>
#include <pango/pango.h>
#include <pango/pangocairo.h>
#include <stdlib.h>
-#include "gl/error.h"
#include "gl/intprops.h"
#include "gl/minmax.h"
#include "gl/xalloc.h"
#define H TABLE_HORZ
#define V TABLE_VERT
-/* Measurements as we present to the rest of PSPP. */
+/* The unit used for internal measurements is inch/(72 * XR_POINT). */
#define XR_POINT PANGO_SCALE
-#define XR_INCH (XR_POINT * 72)
/* Conversions to and from points. */
static double
int width; /* Page width minus margins. */
int length; /* Page length minus margins and header. */
- int left_margin; /* Left margin in XR units. */
- int right_margin; /* Right margin in XR units. */
- int top_margin; /* Top margin in XR units. */
- int bottom_margin; /* Bottom margin in XR units. */
+ int left_margin; /* Left margin in inch/(72 * XR_POINT). */
+ int right_margin; /* Right margin in inch/(72 * XR_POINT). */
+ int top_margin; /* Top margin in inch/(72 * XR_POINT). */
+ int bottom_margin; /* Bottom margin in inch/(72 * XR_POINT). */
int line_gutter; /* Space around lines. */
int line_space; /* Space between lines. */
int line_width; /* Width of lines. */
- double bg_red, bg_green, bg_blue; /* Background color */
- double fg_red, fg_green, fg_blue; /* Foreground color */
+ struct xr_color bg; /* Background color */
+ struct xr_color fg; /* Foreground color */
/* Internal state. */
struct render_params *params;
Future implementations might allow things like "yellow" and
"sky-blue-ultra-brown"
*/
-static void
+void
parse_color (struct output_driver *d, struct string_map *options,
- const char *key, const char *default_value,
- double *dred, double *dgreen, double *dblue)
+ const char *key, const char *default_value,
+ struct xr_color *color)
{
int red, green, blue;
char *string = parse_string (opt (d, options, key, default_value));
free (string);
/* Convert 16 bit ints to float */
- *dred = red / (double) 0xFFFF;
- *dgreen = green / (double) 0xFFFF;
- *dblue = blue / (double) 0xFFFF;
+ color->red = red / (double) 0xFFFF;
+ color->green = green / (double) 0xFFFF;
+ color->blue = blue / (double) 0xFFFF;
}
static PangoFontDescription *
parse_font (struct output_driver *d, struct string_map *options,
const char *key, const char *default_value,
- int default_points)
+ int default_size)
{
PangoFontDescription *desc;
char *string;
desc = pango_font_description_from_string (string);
if (desc == NULL)
{
- error (0, 0, _("`%s': bad font specification"), string);
+ msg (MW, _("`%s': bad font specification"), string);
/* Fall back to DEFAULT_VALUE, which had better be a valid font
description. */
free (string);
/* If the font description didn't include an explicit font size, then set it
- to DEFAULT_POINTS. */
+ to DEFAULT_SIZE, which is in inch/72000 units. */
if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
pango_font_description_set_size (desc,
- default_points / 1000.0 * PANGO_SCALE);
+ (default_size / 1000.0) * PANGO_SCALE);
return desc;
}
{
struct output_driver *d = &xr->driver;
- int paper_width, paper_length, i;
+ /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
+ int left_margin, right_margin;
+ int top_margin, bottom_margin;
+ int paper_width, paper_length;
+ int font_size;
+
+ /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
+ const double scale = XR_POINT / 1000.;
- int font_points = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
+ int i;
for (i = 0; i < XR_N_FONTS; i++)
{
pango_font_description_free (font->desc);
}
+ font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
xr->fonts[XR_FONT_FIXED].desc = parse_font (d, o, "fixed-font", "monospace",
- font_points);
+ font_size);
xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font (d, o, "prop-font",
- "serif", font_points);
+ "serif", font_size);
xr->fonts[XR_FONT_EMPHASIS].desc = parse_font (d, o, "emph-font",
- "serif italic", font_points);
+ "serif italic", font_size);
- xr->line_gutter = XR_POINT;
+ xr->line_gutter = parse_dimension (opt (d, o, "gutter", "3pt")) * scale;
xr->line_space = XR_POINT;
xr->line_width = XR_POINT / 2;
xr->page_number = 0;
- parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg_red, &xr->bg_green, &xr->bg_blue);
- parse_color (d, o, "foreground-color", "#000000000000", &xr->fg_red, &xr->fg_green, &xr->fg_blue);
+ parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &xr->bg);
+ parse_color (d, o, "foreground-color", "#000000000000", &xr->fg);
+ /* Get dimensions. */
parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
- xr->left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
- xr->right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
- xr->top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
- xr->bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
-
- xr->width = paper_width - xr->left_margin - xr->right_margin;
- xr->length = paper_length - xr->top_margin - xr->bottom_margin;
+ left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
+ right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
+ top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
+ bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
+
+ /* Convert to inch/(XR_POINT * 72). */
+ xr->left_margin = left_margin * scale;
+ xr->right_margin = right_margin * scale;
+ xr->top_margin = top_margin * scale;
+ xr->bottom_margin = bottom_margin * scale;
+ xr->width = (paper_width - left_margin - right_margin) * scale;
+ xr->length = (paper_length - top_margin - bottom_margin) * scale;
}
static struct xr_driver *
return xr;
}
-static bool
-xr_is_72dpi (cairo_t *cr)
+static int
+pango_to_xr (int pango)
{
- cairo_surface_type_t type;
- cairo_surface_t *surface;
+ return (XR_POINT != PANGO_SCALE
+ ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
+ : pango);
+}
- surface = cairo_get_target (cr);
- type = cairo_surface_get_type (surface);
- return type == CAIRO_SURFACE_TYPE_PDF || type == CAIRO_SURFACE_TYPE_PS;
+static int
+xr_to_pango (int xr)
+{
+ return (XR_POINT != PANGO_SCALE
+ ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
+ : xr);
}
static bool
xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
{
- PangoContext *context;
- PangoFontMap *map;
int i;
xr->cairo = cairo;
cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
- map = pango_cairo_font_map_get_default ();
- context = pango_font_map_create_context (map);
- if (xr_is_72dpi (cairo))
- {
- /* Pango seems to always scale fonts according to the DPI specified
- in the font map, even if the surface has a real DPI. The default
- DPI is 96, so on a 72 DPI device fonts end up being 96/72 = 133%
- of their desired size. We deal with this by fixing the resolution
- here. Presumably there is a better solution, but what? */
- pango_cairo_context_set_resolution (context, 72.0);
- }
-
xr->char_width = 0;
xr->char_height = 0;
for (i = 0; i < XR_N_FONTS; i++)
struct xr_font *font = &xr->fonts[i];
int char_width, char_height;
- font->layout = pango_layout_new (context);
+ font->layout = pango_cairo_create_layout (cairo);
pango_layout_set_font_description (font->layout, font->desc);
pango_layout_set_text (font->layout, "0", 1);
pango_layout_get_size (font->layout, &char_width, &char_height);
- xr->char_width = MAX (xr->char_width, char_width);
- xr->char_height = MAX (xr->char_height, char_height);
+ xr->char_width = MAX (xr->char_width, pango_to_xr (char_width));
+ xr->char_height = MAX (xr->char_height, pango_to_xr (char_height));
}
- g_object_unref (G_OBJECT (context));
-
if (xr->params == NULL)
{
int single_width, double_width;
}
}
- cairo_set_source_rgb (xr->cairo, xr->fg_red, xr->fg_green, xr->fg_blue);
+ cairo_set_source_rgb (xr->cairo, xr->fg.red, xr->fg.green, xr->fg.blue);
return true;
}
xr = xr_allocate (file_name, device_type, o);
- width_pt = (xr->width + xr->left_margin + xr->right_margin) / 1000.0;
- length_pt = (xr->length + xr->top_margin + xr->bottom_margin) / 1000.0;
+ width_pt = xr_to_pt (xr->width + xr->left_margin + xr->right_margin);
+ length_pt = xr_to_pt (xr->length + xr->top_margin + xr->bottom_margin);
if (file_type == XR_PDF)
surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
else if (file_type == XR_PS)
status = cairo_surface_status (surface);
if (status != CAIRO_STATUS_SUCCESS)
{
- error (0, 0, _("error opening output file `%s': %s"),
+ msg (ME, _("error opening output file `%s': %s"),
file_name, cairo_status_to_string (status));
cairo_surface_destroy (surface);
goto error;
if (xr->width / xr->char_width < MIN_WIDTH)
{
- error (0, 0, _("The defined page is not wide enough to hold at least %d "
+ msg (ME, _("The defined page is not wide enough to hold at least %d "
"characters in the default font. In fact, there's only "
"room for %d characters."),
MIN_WIDTH,
if (xr->length / xr->char_height < MIN_LENGTH)
{
- error (0, 0, _("The defined page is not long enough to hold at least %d "
+ msg (ME, _("The defined page is not long enough to hold at least %d "
"lines in the default font. In fact, there's only "
"room for %d lines."),
MIN_LENGTH,
cairo_surface_finish (cairo_get_target (xr->cairo));
status = cairo_status (xr->cairo);
if (status != CAIRO_STATUS_SUCCESS)
- error (0, 0, _("error drawing output for %s driver: %s"),
+ msg (ME, _("error drawing output for %s driver: %s"),
output_driver_get_name (driver),
cairo_status_to_string (status));
cairo_destroy (xr->cairo);
{
struct xr_driver *xr = xr_driver_cast (driver);
+ output_driver_track_current_command (output_item, &xr->command_name);
+
xr_driver_output_item (xr, output_item);
while (xr_driver_need_new_page (xr))
{
if (cairo != NULL)
{
cairo_save (cairo);
- cairo_set_source_rgb (cairo, xr->bg_red, xr->bg_green, xr->bg_blue);
+ cairo_set_source_rgb (cairo, xr->bg.red, xr->bg.green, xr->bg.blue);
cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
cairo_fill (cairo);
cairo_restore (cairo);
static void
xr_layout_cell (struct xr_driver *, const struct table_cell *,
int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
- PangoWrapMode, int *width, int *height);
+ int *width, int *height);
static void
dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
bb[V][0] = 0;
bb[V][1] = INT_MAX;
clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
- xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, max_width, &h);
+ xr_layout_cell (xr, cell, bb, clip, max_width, &h);
bb[H][1] = 1;
- xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, min_width, &h);
+ xr_layout_cell (xr, cell, bb, clip, min_width, &h);
}
static int
bb[V][0] = 0;
bb[V][1] = INT_MAX;
clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
- xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
+ xr_layout_cell (xr, cell, bb, clip, &w, &h);
return h;
}
struct xr_driver *xr = xr_;
int w, h;
- xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
+ xr_layout_cell (xr, cell, bb, clip, &w, &h);
}
\f
static void
xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
- PangoWrapMode wrap, int *width, int *height)
+ int *width, int *height)
{
struct xr_font *font;
+ int w, h;
font = (cell->options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
: cell->options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
((cell->options & TAB_ALIGNMENT) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
: (cell->options & TAB_ALIGNMENT) == TAB_LEFT ? PANGO_ALIGN_LEFT
: PANGO_ALIGN_CENTER));
- pango_layout_set_width (font->layout,
- bb[H][1] == INT_MAX ? -1 : bb[H][1] - bb[H][0]);
- pango_layout_set_wrap (font->layout, wrap);
+ pango_layout_set_width (
+ font->layout,
+ bb[H][1] == INT_MAX ? -1 : xr_to_pango (bb[H][1] - bb[H][0]));
+ pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
if (clip[H][0] != clip[H][1])
{
cairo_restore (xr->cairo);
}
- if (width != NULL || height != NULL)
- {
- int w, h;
-
- pango_layout_get_size (font->layout, &w, &h);
- if (width != NULL)
- *width = w;
- if (height != NULL)
- *height = h;
- }
+ pango_layout_get_size (font->layout, &w, &h);
+ *width = pango_to_xr (w);
+ *height = pango_to_xr (h);
}
static void
xr_draw_cell (xr, &cell, bb, bb);
}
\f
-struct output_driver_factory pdf_driver_factory = { "pdf", xr_pdf_create };
-struct output_driver_factory ps_driver_factory = { "ps", xr_ps_create };
-struct output_driver_factory svg_driver_factory = { "svg", xr_svg_create };
+struct output_driver_factory pdf_driver_factory =
+ { "pdf", "pspp.pdf", xr_pdf_create };
+struct output_driver_factory ps_driver_factory =
+ { "ps", "pspp.ps", xr_ps_create };
+struct output_driver_factory svg_driver_factory =
+ { "svg", "pspp.svg", xr_svg_create };
static const struct output_driver_class cairo_driver_class =
{
{
int w0 = render_page_get_size (r->page, H);
int w1 = r->title_width;
- *w = MAX (w0, w1) / 1024;
- *h = (render_page_get_size (r->page, V) + r->title_height) / 1024;
+ *w = MAX (w0, w1) / XR_POINT;
+ *h = (render_page_get_size (r->page, V) + r->title_height) / XR_POINT;
}
else
{
}
xr->y = r->title_height;
- render_page_draw_region (r->page, x * 1024, (y * 1024) - r->title_height,
- w * 1024, h * 1024);
+ render_page_draw_region (r->page,
+ x * XR_POINT, (y * XR_POINT) - r->title_height,
+ w * XR_POINT, h * XR_POINT);
}
else
xr_draw_chart (to_chart_item (r->item), cr,
char *
xr_draw_png_chart (const struct chart_item *item,
- const char *file_name_template, int number)
+ const char *file_name_template, int number,
+ const struct xr_color *fg,
+ const struct xr_color *bg
+ )
{
const int width = 640;
const int length = 480;
surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
cr = cairo_create (surface);
- cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ cairo_set_source_rgb (cr, bg->red, bg->green, bg->blue);
+ cairo_paint (cr);
+
+ cairo_set_source_rgb (cr, fg->red, fg->green, fg->blue);
xr_draw_chart (item, cr, 0.0, 0.0, width, length);
status = cairo_surface_write_to_png (surface, file_name);
if (status != CAIRO_STATUS_SUCCESS)
- error (0, 0, _("error writing output file `%s': %s"),
+ msg (ME, _("error writing output file `%s': %s"),
file_name, cairo_status_to_string (status));
cairo_destroy (cr);