#include <config.h>
+#include <output/cairo.h>
+
#include <libpspp/assertion.h>
#include <libpspp/start-date.h>
#include <libpspp/version.h>
#include <output/afm.h>
-#include <output/chart.h>
+#include <output/chart-provider.h>
#include <output/manager.h>
#include <output/output.h>
top-margin=0.5in
bottom-margin=0.5in
- prop-font=Times-Roman
- emph-font=Times-Italic
- fixed-font=Courier
+ prop-font=serif
+ emph-font=serif italic
+ fixed-font=monospace
font-size=10000
line-gutter=1pt
line-width=0.5pt
*/
-/* Measurements. We use the same scale as Pango, for simplicity. */
+/* Measurements as we present to the rest of PSPP. */
#define XR_POINT PANGO_SCALE
#define XR_INCH (XR_POINT * 72)
+/* Conversions to and from points. */
+static double
+xr_to_pt (int x)
+{
+ return x / (double) XR_POINT;
+}
+
+static int
+pt_to_xr (double x)
+{
+ return x * XR_POINT + 0.5;
+}
+
/* Output types. */
enum xr_output_type
{
/* Cairo output driver extension record. */
struct xr_driver_ext
{
- char *file_name; /* Output file name. */
- enum xr_output_type file_type; /* Type of output file. */
cairo_t *cairo;
+ struct xr_font fonts[OUTP_FONT_CNT];
bool draw_headers; /* Draw headers at top of page? */
int page_number; /* Current page number. */
+ int line_gutter; /* Space around lines. */
+ int line_space; /* Space between lines. */
+ int line_width; /* Width of lines. */
+ };
+
+struct xr_driver_options
+ {
+ struct outp_driver *driver;
+
+ char *file_name; /* Output file name. */
+ enum xr_output_type file_type; /* Type of output file. */
+
+
bool portrait; /* Portrait mode? */
+
int paper_width; /* Width of paper before dropping margins. */
int paper_length; /* Length of paper before dropping margins. */
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 line_gutter; /* Space around lines. */
- int line_space; /* Space between lines. */
- int line_width; /* Width of lines. */
-
- struct xr_font fonts[OUTP_FONT_CNT];
};
-static bool handle_option (struct outp_driver *this, const char *key,
+static bool handle_option (void *options, const char *key,
const struct string *val);
static void draw_headers (struct outp_driver *this);
\f
/* Driver initialization. */
-static bool
-xr_open_driver (struct outp_driver *this, struct substring options)
+static struct outp_driver *
+xr_allocate (const char *name, int types)
{
- cairo_surface_t *surface;
- cairo_status_t status;
+ struct outp_driver *this;
struct xr_driver_ext *x;
- double width_pt, length_pt;
size_t i;
+ this = outp_allocate_driver (&cairo_class, name, types);
this->width = this->length = 0;
this->font_height = XR_POINT * 10;
-
- this->ext = x = xmalloc (sizeof *x);
- x->file_name = xstrdup ("pspp.pdf");
- x->file_type = XR_PDF;
- x->draw_headers = true;
- x->page_number = 0;
- x->portrait = true;
- outp_get_paper_size ("", &x->paper_width, &x->paper_length);
- x->left_margin = XR_INCH / 2;
- x->right_margin = XR_INCH / 2;
- x->top_margin = XR_INCH / 2;
- x->bottom_margin = XR_INCH / 2;
- x->line_gutter = XR_POINT;
- x->line_space = XR_POINT;
- x->line_width = XR_POINT / 2;
+ this->ext = x = xzalloc (sizeof *x);
+ x->cairo = NULL;
x->fonts[OUTP_FIXED].string = xstrdup ("monospace");
x->fonts[OUTP_PROPORTIONAL].string = xstrdup ("serif");
x->fonts[OUTP_EMPHASIS].string = xstrdup ("serif italic");
font->metrics = NULL;
font->layout = NULL;
}
+ x->draw_headers = true;
+ x->page_number = 0;
+ x->line_gutter = XR_POINT;
+ x->line_space = XR_POINT;
+ x->line_width = XR_POINT / 2;
- outp_parse_options (options, handle_option, this);
+ return this;
+}
- if (x->portrait)
+static bool
+xr_set_cairo (struct outp_driver *this, cairo_t *cairo)
+{
+ struct xr_driver_ext *x = this->ext;
+ int i;
+
+ x->cairo = cairo;
+
+ cairo_set_line_width (x->cairo, xr_to_pt (x->line_width));
+
+ for (i = 0; i < OUTP_FONT_CNT; i++)
+ if (!load_font (this, &x->fonts[i]))
+ return false;
+
+ this->fixed_width = text_width (this, "0", OUTP_FIXED);
+ this->prop_em_width = text_width (this, "0", OUTP_PROPORTIONAL);
+
+ this->horiz_line_width[OUTP_L_NONE] = 0;
+ this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
+ this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
+ + 2 * x->line_width);
+ memcpy (this->vert_line_width, this->horiz_line_width,
+ sizeof this->vert_line_width);
+
+ return true;
+}
+
+struct outp_driver *
+xr_create_driver (cairo_t *cairo)
+{
+ struct outp_driver *this;
+
+ this = xr_allocate ("cairo", 0);
+ this->width = INT_MAX / 8;
+ this->length = INT_MAX / 8;
+ if (!xr_set_cairo (this, cairo))
{
- this->width = x->paper_width;
- this->length = x->paper_length;
+ this->class->close_driver (this);
+ outp_free_driver (this);
+ return NULL;
+ }
+ return this;
+}
+
+static bool
+xr_open_driver (const char *name, int types, struct substring option_string)
+{
+ struct outp_driver *this;
+ struct xr_driver_ext *x;
+ struct xr_driver_options options;
+ cairo_surface_t *surface;
+ cairo_status_t status;
+ double width_pt, length_pt;
+
+ this = xr_allocate (name, types);
+ x = this->ext;
+
+ options.driver = this;
+ options.file_name = xstrdup ("pspp.pdf");
+ options.file_type = XR_PDF;
+ options.portrait = true;
+ outp_get_paper_size ("", &options.paper_width, &options.paper_length);
+ options.left_margin = XR_INCH / 2;
+ options.right_margin = XR_INCH / 2;
+ options.top_margin = XR_INCH / 2;
+ options.bottom_margin = XR_INCH / 2;
+
+ outp_parse_options (this->name, option_string, handle_option, &options);
+
+ width_pt = options.paper_width / 1000.0;
+ length_pt = options.paper_length / 1000.0;
+ if (options.portrait)
+ {
+ this->width = pt_to_xr (width_pt);
+ this->length = pt_to_xr (length_pt);
}
else
{
- this->width = x->paper_length;
- this->length = x->paper_width;
+ this->width = pt_to_xr (width_pt);
+ this->length = pt_to_xr (length_pt);
}
if (x->draw_headers)
- x->top_margin += 3 * this->font_height;
- this->width -= x->left_margin + x->right_margin;
- this->length -= x->top_margin + x->bottom_margin;
-
- width_pt = x->paper_width / (double) XR_POINT;
- length_pt = x->paper_length / (double) XR_POINT;
- if (x->file_type == XR_PDF)
- surface = cairo_pdf_surface_create (x->file_name, width_pt, length_pt);
- else if (x->file_type == XR_PS)
- surface = cairo_ps_surface_create (x->file_name, width_pt, length_pt);
- else if (x->file_type == XR_SVG)
- surface = cairo_svg_surface_create (x->file_name, width_pt, length_pt);
+ options.top_margin += 3 * this->font_height;
+ this->width -= options.left_margin + options.right_margin;
+ this->length -= options.top_margin + options.bottom_margin;
+
+ if (options.file_type == XR_PDF)
+ surface = cairo_pdf_surface_create (options.file_name,
+ width_pt, length_pt);
+ else if (options.file_type == XR_PS)
+ surface = cairo_ps_surface_create (options.file_name, width_pt, length_pt);
+ else if (options.file_type == XR_SVG)
+ surface = cairo_svg_surface_create (options.file_name,
+ width_pt, length_pt);
else
NOT_REACHED ();
if (status != CAIRO_STATUS_SUCCESS)
{
error (0, 0, _("opening output file \"%s\": %s"),
- x->file_name, cairo_status_to_string (status));
+ options.file_name, cairo_status_to_string (status));
cairo_surface_destroy (surface);
goto error;
}
x->cairo = cairo_create (surface);
cairo_surface_destroy (surface);
- cairo_scale (x->cairo, 1.0 / PANGO_SCALE, 1.0 / PANGO_SCALE);
- cairo_translate (x->cairo, x->left_margin, x->top_margin);
- cairo_set_line_width (x->cairo, x->line_width);
-
- for (i = 0; i < OUTP_FONT_CNT; i++)
- if (!load_font (this, &x->fonts[i]))
- goto error;
+ cairo_translate (x->cairo,
+ xr_to_pt (options.left_margin),
+ xr_to_pt (options.top_margin));
if (this->length / this->font_height < 15)
{
goto error;
}
- this->fixed_width = text_width (this, "0", OUTP_FIXED);
- this->prop_em_width = text_width (this, "0", OUTP_PROPORTIONAL);
-
- this->horiz_line_width[OUTP_L_NONE] = 0;
- this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
- this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
- + 2 * x->line_width);
- memcpy (this->vert_line_width, this->horiz_line_width,
- sizeof this->vert_line_width);
+ if (!xr_set_cairo (this, x->cairo))
+ goto error;
+ outp_register_driver (this);
+ free (options.file_name);
return true;
error:
this->class->close_driver (this);
+ outp_free_driver (this);
+ free (options.file_name);
return false;
}
cairo_surface_finish (cairo_get_target (x->cairo));
status = cairo_status (x->cairo);
if (status != CAIRO_STATUS_SUCCESS)
- error (0, 0, _("writing output file \"%s\": %s"),
- x->file_name, cairo_status_to_string (status));
+ error (0, 0, _("error writing output file for %s driver: %s"),
+ this->name, cairo_status_to_string (status));
cairo_destroy (x->cairo);
}
- free (x->file_name);
for (i = 0; i < OUTP_FONT_CNT; i++)
free_font (&x->fonts[i]);
free (x);
};
static bool
-handle_option (struct outp_driver *this, const char *key,
- const struct string *val)
+handle_option (void *options_, const char *key, const struct string *val)
{
+ struct xr_driver_options *options = options_;
+ struct outp_driver *this = options->driver;
struct xr_driver_ext *x = this->ext;
int subcat;
char *value = ds_cstr (val);
"driver"), key);
break;
case output_file_arg:
- free (x->file_name);
- x->file_name = xstrdup (value);
+ free (options->file_name);
+ options->file_name = xstrdup (value);
break;
case output_type_arg:
if (!strcmp (value, "pdf"))
- x->file_type = XR_PDF;
+ options->file_type = XR_PDF;
else if (!strcmp (value, "ps"))
- x->file_type = XR_PS;
+ options->file_type = XR_PS;
else if (!strcmp (value, "svg"))
- x->file_type = XR_SVG;
+ options->file_type = XR_SVG;
else
{
error (0, 0, _("unknown Cairo output type \"%s\""), value);
}
break;
case paper_size_arg:
- outp_get_paper_size (value, &x->paper_width, &x->paper_length);
+ outp_get_paper_size (value,
+ &options->paper_width, &options->paper_length);
break;
case orientation_arg:
if (!strcmp (value, "portrait"))
- x->portrait = true;
+ options->portrait = true;
else if (!strcmp (value, "landscape"))
- x->portrait = false;
+ options->portrait = false;
else
error (0, 0, _("unknown orientation `%s' (valid orientations are "
"`portrait' and `landscape')"), value);
switch (subcat)
{
case 0:
- x->left_margin = dimension;
+ options->left_margin = dimension;
break;
case 1:
- x->right_margin = dimension;
+ options->right_margin = dimension;
break;
case 2:
- x->top_margin = dimension;
+ options->top_margin = dimension;
break;
case 3:
- x->bottom_margin = dimension;
+ options->bottom_margin = dimension;
break;
case 4:
this->font_height = dimension;
}
static void
-xr_submit (struct outp_driver *this UNUSED, struct som_entity *s)
+xr_output_chart (struct outp_driver *this, const struct chart *chart)
{
- switch (s->type)
- {
- case SOM_CHART:
- break;
- default:
- NOT_REACHED ();
- }
+ struct xr_driver_ext *x = this->ext;
+ struct chart_geometry geom;
+
+ outp_eject_page (this);
+ outp_open_page (this);
+
+ cairo_save (x->cairo);
+ cairo_translate (x->cairo, 0.0, xr_to_pt (this->length));
+ cairo_scale (x->cairo, 1.0, -1.0);
+ chart_geometry_init (x->cairo, &geom,
+ xr_to_pt (this->width), xr_to_pt (this->length));
+ chart_draw (chart, x->cairo, &geom);
+ chart_geometry_free (x->cairo);
+ cairo_restore (x->cairo);
+
+ outp_close_page (this);
}
\f
/* Draws a line from (x0,y0) to (x1,y1). */
{
struct xr_driver_ext *x = this->ext;
cairo_new_path (x->cairo);
- cairo_move_to (x->cairo, x0, y0);
- cairo_line_to (x->cairo, x1, y1);
+ cairo_move_to (x->cairo, xr_to_pt (x0), xr_to_pt (y0));
+ cairo_line_to (x->cairo, xr_to_pt (x1), xr_to_pt (y1));
cairo_stroke (x->cairo);
}
x1 = this->width - this->prop_em_width;
/* Draw box. */
- cairo_rectangle (ext->cairo, 0, y, this->width,
- 2 * (this->font_height
- + ext->line_width + ext->line_gutter));
+ cairo_rectangle (ext->cairo, 0, xr_to_pt (y), xr_to_pt (this->width),
+ xr_to_pt (2 * (this->font_height
+ + ext->line_width + ext->line_gutter)));
cairo_save (ext->cairo);
cairo_set_source_rgb (ext->cairo, 0.9, 0.9, 0.9);
cairo_fill_preserve (ext->cairo);
}
}
cairo_save (ext->cairo);
- cairo_translate (ext->cairo, text->x, text->y);
- cairo_scale (ext->cairo, PANGO_SCALE, PANGO_SCALE);
+ cairo_translate (ext->cairo, xr_to_pt (text->x), xr_to_pt (text->y));
pango_cairo_show_layout (ext->cairo, font->layout);
cairo_restore (ext->cairo);
- pango_cairo_update_layout (ext->cairo, font->layout);
}
if (width != NULL || height != NULL)
static void
xr_text_draw (struct outp_driver *this, const struct outp_text *t)
{
- assert (this->page_open);
text (this, t, true, NULL, NULL);
}
\f
-static void
-xr_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
-{
-#ifdef NO_CHARTS
- ch->lp = NULL;
-#else
- /* XXX libplot doesn't support Cairo yet. */
-#endif
-}
-
-static void
-xr_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
-{
-#ifndef NO_CHARTS
- /* XXX libplot doesn't support Cairo yet. */
-#endif
-}
-\f
/* Attempts to load FONT, initializing its other members based on
its 'string' member and the information in THIS. Returns true
if successful, otherwise false. */
pango_font_description_set_absolute_size (font->desc, this->font_height);
font->layout = pango_cairo_create_layout (x->cairo);
- pango_cairo_update_layout (x->cairo, font->layout);
pango_layout_set_font_description (font->layout, font->desc);
language = pango_language_get_default ();
xr_close_page,
NULL,
- xr_submit,
+ xr_output_chart,
+
+ NULL,
xr_line,
xr_text_metrics,
xr_text_draw,
-
- xr_chart_initialise,
- xr_chart_finalise
};