/* PSPP - a program for statistical analysis.
- Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 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 "libpspp/assertion.h"
#include "libpspp/cast.h"
#include "libpspp/message.h"
+#include "libpspp/pool.h"
#include "libpspp/start-date.h"
#include "libpspp/str.h"
#include "libpspp/string-map.h"
#include "libpspp/version.h"
+#include "data/file-handle-def.h"
#include "output/cairo-chart.h"
#include "output/chart-item-provider.h"
#include "output/charts/boxplot.h"
#include "output/charts/np-plot.h"
#include "output/charts/piechart.h"
+#include "output/charts/barchart.h"
#include "output/charts/plot-hist.h"
#include "output/charts/roc-chart.h"
#include "output/charts/spreadlevel-plot.h"
#include "output/charts/scree.h"
+#include "output/charts/scatterplot.h"
#include "output/driver-provider.h"
#include "output/message-item.h"
#include "output/options.h"
#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/c-strcase.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
return x / (double) XR_POINT;
}
+/* Conversion from 1/96" units ("pixels") to Cairo/Pango units. */
+static int
+px_to_xr (int x)
+{
+ return x * (PANGO_SCALE * 72 / 96);
+}
+
/* Output types. */
enum xr_output_type
{
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 */
+ int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
+
+ struct xr_color bg; /* Background color */
+ struct xr_color fg; /* Foreground color */
/* Internal state. */
struct render_params *params;
char *subtitle;
cairo_t *cairo;
int page_number; /* Current page number. */
- int y;
+ int x, y;
struct xr_render_fsm *fsm;
+ int nest;
};
static const struct output_driver_class cairo_driver_class;
static void xr_driver_run_fsm (struct xr_driver *);
static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
- enum render_line_style styles[TABLE_N_AXES][2]);
+ enum render_line_style styles[TABLE_N_AXES][2],
+ struct cell_color colors[TABLE_N_AXES][2]);
static void xr_measure_cell_width (void *, const struct table_cell *,
int *min, int *max);
static int xr_measure_cell_height (void *, const struct table_cell *,
int width);
static void xr_draw_cell (void *, const struct table_cell *,
int bb[TABLE_N_AXES][2],
+ int spill[TABLE_N_AXES][2],
int clip[TABLE_N_AXES][2]);
+static int xr_adjust_break (void *, const struct table_cell *,
+ int width, int height);
static struct xr_render_fsm *xr_render_output_item (
struct xr_driver *, const struct output_item *);
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)
+parse_font (const char *font, int default_size, bool bold, bool italic)
{
- PangoFontDescription *desc;
- char *string;
+ if (!c_strcasecmp (font, "Monospaced"))
+ font = "Monospace";
- /* Parse KEY as a font description. */
- string = parse_string (opt (d, options, key, default_value));
- desc = pango_font_description_from_string (string);
+ PangoFontDescription *desc = pango_font_description_from_string (font);
if (desc == NULL)
+ return NULL;
+
+ /* If the font description didn't include an explicit font size, then set it
+ 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_size / 1000.0) * PANGO_SCALE);
+
+ pango_font_description_set_weight (desc, (bold
+ ? PANGO_WEIGHT_BOLD
+ : PANGO_WEIGHT_NORMAL));
+ pango_font_description_set_style (desc, (italic
+ ? PANGO_STYLE_ITALIC
+ : PANGO_STYLE_NORMAL));
+
+ return desc;
+}
+
+static PangoFontDescription *
+parse_font_option (struct output_driver *d, struct string_map *options,
+ const char *key, const char *default_value,
+ int default_size, bool bold, bool italic)
+{
+ char *string = parse_string (opt (d, options, key, default_value));
+ PangoFontDescription *desc = parse_font (string, default_size, bold, italic);
+ if (!desc)
{
- 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. */
- desc = pango_font_description_from_string (default_value);
+ desc = parse_font (default_value, default_size, bold, italic);
assert (desc != NULL);
}
free (string);
- /* If the font description didn't include an explicit font size, then set it
- to DEFAULT_POINTS. */
- if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE))
- pango_font_description_set_size (desc,
- default_points / 1000.0 * PANGO_SCALE);
-
return desc;
}
-
static void
apply_options (struct xr_driver *xr, struct string_map *o)
{
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;
+ int min_break[TABLE_N_AXES];
- int font_points = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
+ /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
+ const double scale = XR_POINT / 1000.;
+
+ int i;
for (i = 0; i < XR_N_FONTS; i++)
{
pango_font_description_free (font->desc);
}
- xr->fonts[XR_FONT_FIXED].desc = parse_font (d, o, "fixed-font", "monospace",
- font_points);
- xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font (d, o, "prop-font",
- "serif", font_points);
- xr->fonts[XR_FONT_EMPHASIS].desc = parse_font (d, o, "emph-font",
- "serif italic", font_points);
+ font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
+ xr->fonts[XR_FONT_FIXED].desc = parse_font_option
+ (d, o, "fixed-font", "monospace", font_size, false, false);
+ xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font_option (
+ d, o, "prop-font", "sans serif", font_size, false, false);
+ xr->fonts[XR_FONT_EMPHASIS].desc = parse_font_option (
+ d, o, "emph-font", "sans serif", font_size, false, true);
- xr->line_gutter = XR_POINT;
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"));
+
+ min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
+ min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
+
+ /* 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;
+ xr->min_break[H] = min_break[H] >= 0 ? min_break[H] : xr->width / 2;
+ xr->min_break[V] = min_break[V] >= 0 ? min_break[V] : xr->length / 2;
}
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_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (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;
-
xr->params = xmalloc (sizeof *xr->params);
xr->params->draw_line = xr_draw_line;
xr->params->measure_cell_width = xr_measure_cell_width;
xr->params->measure_cell_height = xr_measure_cell_height;
+ xr->params->adjust_break = xr_adjust_break;
xr->params->draw_cell = xr_draw_cell;
xr->params->aux = xr;
xr->params->size[H] = xr->width;
xr->params->font_size[H] = xr->char_width;
xr->params->font_size[V] = xr->char_height;
- single_width = 2 * xr->line_gutter + xr->line_width;
- double_width = 2 * xr->line_gutter + xr->line_space + 2 * xr->line_width;
+ int lw = xr->line_width;
+ int ls = xr->line_space;
for (i = 0; i < TABLE_N_AXES; i++)
{
xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
- xr->params->line_widths[i][RENDER_LINE_SINGLE] = single_width;
- xr->params->line_widths[i][RENDER_LINE_DOUBLE] = double_width;
+ xr->params->line_widths[i][RENDER_LINE_SINGLE] = lw;
+ xr->params->line_widths[i][RENDER_LINE_DASHED] = lw;
+ xr->params->line_widths[i][RENDER_LINE_THICK] = lw * 2;
+ xr->params->line_widths[i][RENDER_LINE_THIN] = lw / 2;
+ xr->params->line_widths[i][RENDER_LINE_DOUBLE] = 2 * lw + ls;
}
+
+ for (i = 0; i < TABLE_N_AXES; i++)
+ xr->params->min_break[i] = xr->min_break[i];
+ xr->params->supports_margins = true;
}
- 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;
}
struct string_map *o, enum xr_output_type file_type)
{
enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
- struct output_driver *d;
struct xr_driver *xr;
cairo_surface_t *surface;
cairo_status_t status;
double width_pt, length_pt;
xr = xr_allocate (file_name, device_type, o);
- d = &xr->driver;
- 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,
}
static struct output_driver *
-xr_pdf_create (const char *file_name, enum settings_output_devices device_type,
+xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type,
struct string_map *o)
{
- return xr_create (file_name, device_type, o, XR_PDF);
+ struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_PDF);
+ fh_unref (fh);
+ return od ;
}
static struct output_driver *
-xr_ps_create (const char *file_name, enum settings_output_devices device_type,
+xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type,
struct string_map *o)
{
- return xr_create (file_name, device_type, o, XR_PS);
+ struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_PS);
+ fh_unref (fh);
+ return od ;
}
static struct output_driver *
-xr_svg_create (const char *file_name, enum settings_output_devices device_type,
+xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type,
struct string_map *o)
{
- return xr_create (file_name, device_type, o, XR_SVG);
+ struct output_driver *od = xr_create (fh_get_file_name (fh), device_type, o, XR_SVG);
+ fh_unref (fh);
+ return od ;
}
static void
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);
}
- free (xr->command_name);
for (i = 0; i < XR_N_FONTS; i++)
{
struct xr_font *font = &xr->fonts[i];
cairo_surface_flush (cairo_get_target (xr->cairo));
}
-static void
-xr_init_caption_cell (const char *caption, struct table_cell *cell)
-{
- cell->contents = caption;
- cell->options = TAB_LEFT;
- cell->destructor = NULL;
-}
-
-static struct render_page *
-xr_render_table_item (struct xr_driver *xr, const struct table_item *item,
- int *caption_widthp, int *caption_heightp)
-{
- const char *caption = table_item_get_caption (item);
-
- if (caption != NULL)
- {
- /* XXX doesn't do well with very large captions */
- int min_width, max_width;
- struct table_cell cell;
-
- xr_init_caption_cell (caption, &cell);
-
- xr_measure_cell_width (xr, &cell, &min_width, &max_width);
- *caption_widthp = MIN (max_width, xr->width);
- *caption_heightp = xr_measure_cell_height (xr, &cell, *caption_widthp);
- }
- else
- *caption_heightp = 0;
-
- return render_page_create (xr->params, table_item_get_table (item));
-}
-
static void
xr_submit (struct output_driver *driver, const struct output_item *output_item)
{
See the big comment in cairo.h for intended usage. */
-/* Gives new page CAIRO to XR for output. CAIRO may be null to skip actually
- rendering the page (which might be useful to find out how many pages an
- output document has without actually rendering it). */
+/* Gives new page CAIRO to XR for output. */
void
xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
{
- if (cairo != NULL)
- {
- cairo_save (cairo);
- 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);
-
- cairo_translate (cairo,
- xr_to_pt (xr->left_margin),
- xr_to_pt (xr->top_margin));
- }
+ cairo_save (cairo);
+ 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);
+
+ cairo_translate (cairo,
+ xr_to_pt (xr->left_margin),
+ xr_to_pt (xr->top_margin));
xr->page_number++;
xr->cairo = cairo;
- xr->y = 0;
+ xr->x = xr->y = 0;
xr_driver_run_fsm (xr);
}
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, int *brk);
static void
-dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
+dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
+ const struct cell_color *color)
+{
+ cairo_new_path (xr->cairo);
+ cairo_set_source_rgb (xr->cairo,
+ color->r / 255.0, color->g / 255.0, color->b / 255.0);
+ cairo_set_line_width (
+ xr->cairo,
+ xr_to_pt (style == RENDER_LINE_THICK ? xr->line_width * 2
+ : style == RENDER_LINE_THIN ? xr->line_width / 2
+ : xr->line_width));
+ cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
+ cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
+ cairo_stroke (xr->cairo);
+}
+
+static void UNUSED
+dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
{
cairo_new_path (xr->cairo);
- cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
- cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
+ cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
+ cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
+ cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y0 + xr->y));
+ cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
+ cairo_line_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y1 + xr->y));
+ cairo_close_path (xr->cairo);
cairo_stroke (xr->cairo);
}
+static void
+fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
+{
+ cairo_new_path (xr->cairo);
+ cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
+ cairo_rectangle (xr->cairo,
+ xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y),
+ xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
+ cairo_fill (xr->cairo);
+}
+
/* Draws a horizontal line X0...X2 at Y if LEFT says so,
shortening it to X0...X1 if SHORTEN is true.
Draws a horizontal line X1...X3 at Y if RIGHT says so,
static void
horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
enum render_line_style left, enum render_line_style right,
+ const struct cell_color *left_color,
+ const struct cell_color *right_color,
bool shorten)
{
- if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
- dump_line (xr, x0, y, x3, y);
+ if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten
+ && cell_color_equal (left_color, right_color))
+ dump_line (xr, x0, y, x3, y, left, left_color);
else
{
if (left != RENDER_LINE_NONE)
- dump_line (xr, x0, y, shorten ? x1 : x2, y);
+ dump_line (xr, x0, y, shorten ? x1 : x2, y, left, left_color);
if (right != RENDER_LINE_NONE)
- dump_line (xr, shorten ? x2 : x1, y, x3, y);
+ dump_line (xr, shorten ? x2 : x1, y, x3, y, right, right_color);
}
}
static void
vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
enum render_line_style top, enum render_line_style bottom,
+ const struct cell_color *top_color,
+ const struct cell_color *bottom_color,
bool shorten)
{
- if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
- dump_line (xr, x, y0, x, y3);
+ if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten
+ && cell_color_equal (top_color, bottom_color))
+ dump_line (xr, x, y0, x, y3, top, top_color);
else
{
if (top != RENDER_LINE_NONE)
- dump_line (xr, x, y0, x, shorten ? y1 : y2);
+ dump_line (xr, x, y0, x, shorten ? y1 : y2, top, top_color);
if (bottom != RENDER_LINE_NONE)
- dump_line (xr, x, shorten ? y2 : y1, x, y3);
+ dump_line (xr, x, shorten ? y2 : y1, x, y3, bottom, bottom_color);
}
}
static void
xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
- enum render_line_style styles[TABLE_N_AXES][2])
+ enum render_line_style styles[TABLE_N_AXES][2],
+ struct cell_color colors[TABLE_N_AXES][2])
{
const int x0 = bb[H][0];
const int y0 = bb[V][0];
const int x3 = bb[H][1];
const int y3 = bb[V][1];
const int top = styles[H][0];
- const int left = styles[V][0];
const int bottom = styles[H][1];
- const int right = styles[V][1];
+
+ int start_side = render_direction_rtl();
+ int end_side = !start_side;
+ const int start_of_line = styles[V][start_side];
+ const int end_of_line = styles[V][end_side];
+ const struct cell_color *top_color = &colors[H][0];
+ const struct cell_color *bottom_color = &colors[H][1];
+ const struct cell_color *start_color = &colors[V][start_side];
+ const struct cell_color *end_color = &colors[V][end_side];
/* The algorithm here is somewhat subtle, to allow it to handle
all the kinds of intersections that we need.
(It doesn't make sense to have different kinds of line on the
same axis, so we don't try to gracefully handle that case.) */
bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
- bool double_horz = left == RENDER_LINE_DOUBLE || right == RENDER_LINE_DOUBLE;
+ bool double_horz = start_of_line == RENDER_LINE_DOUBLE || end_of_line == RENDER_LINE_DOUBLE;
/* When horizontal lines are doubled,
the left-side line along y1 normally runs from x0 to x2,
int x1 = xc - horz_line_ofs;
int x2 = xc + horz_line_ofs;
- bool shorten_x1_lines = left == RENDER_LINE_DOUBLE;
- bool shorten_x2_lines = right == RENDER_LINE_DOUBLE;
+ bool shorten_x1_lines = start_of_line == RENDER_LINE_DOUBLE;
+ bool shorten_x2_lines = end_of_line == RENDER_LINE_DOUBLE;
bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
int vert_line_ofs = double_horz ? double_line_ofs : 0;
int yc = (y0 + y3) / 2;
int y2 = yc + vert_line_ofs;
if (!double_horz)
- horz_line (xr, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
+ horz_line (xr, x0, x1, x2, x3, yc, start_of_line, end_of_line,
+ start_color, end_color, shorten_yc_line);
else
{
- horz_line (xr, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
- horz_line (xr, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
+ horz_line (xr, x0, x1, x2, x3, y1, start_of_line, end_of_line,
+ start_color, end_color, shorten_y1_lines);
+ horz_line (xr, x0, x1, x2, x3, y2, start_of_line, end_of_line,
+ start_color, end_color, shorten_y2_lines);
}
if (!double_vert)
- vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
+ vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
+ shorten_xc_line);
else
{
- vert_line (xr, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
- vert_line (xr, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
+ vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
+ shorten_x1_lines);
+ vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
+ shorten_x2_lines);
}
}
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, NULL);
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, NULL);
+
+ if (*min_width > 0)
+ *min_width += px_to_xr (cell->style->margin[H][0]
+ + cell->style->margin[H][1]);
+ if (*max_width > 0)
+ *max_width += px_to_xr (cell->style->margin[H][0]
+ + cell->style->margin[H][1]);
}
static int
int w, h;
bb[H][0] = 0;
- bb[H][1] = width;
+ bb[H][1] = width - px_to_xr (cell->style->margin[H][0]
+ + cell->style->margin[H][1]);
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, NULL);
+ h += px_to_xr (cell->style->margin[V][0] + cell->style->margin[V][1]);
return h;
}
+static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
+
static void
xr_draw_cell (void *xr_, const struct table_cell *cell,
- int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
+ int bb[TABLE_N_AXES][2],
+ int spill[TABLE_N_AXES][2],
+ int clip[TABLE_N_AXES][2])
{
struct xr_driver *xr = xr_;
- int w, h;
+ int w, h, brk;
- xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
+ cairo_save (xr->cairo);
+ int bg_clip[TABLE_N_AXES][2];
+ for (int axis = 0; axis < TABLE_N_AXES; axis++)
+ {
+ bg_clip[axis][0] = clip[axis][0];
+ if (bb[axis][0] == clip[axis][0])
+ bg_clip[axis][0] -= spill[axis][0];
+
+ bg_clip[axis][1] = clip[axis][1];
+ if (bb[axis][1] == clip[axis][1])
+ bg_clip[axis][1] += spill[axis][1];
+ }
+ xr_clip (xr, bg_clip);
+ cairo_set_source_rgb (xr->cairo,
+ cell->style->bg.r / 255.,
+ cell->style->bg.g / 255.,
+ cell->style->bg.b / 255.);
+ fill_rectangle (xr,
+ bb[H][0] - spill[H][0],
+ bb[V][0] - spill[V][0],
+ bb[H][1] + spill[H][1],
+ bb[V][1] + spill[V][1]);
+ cairo_restore (xr->cairo);
+
+ cairo_save (xr->cairo);
+ cairo_set_source_rgb (xr->cairo,
+ cell->style->fg.r / 255.,
+ cell->style->fg.g / 255.,
+ cell->style->fg.b / 255.);
+
+ for (int axis = 0; axis < TABLE_N_AXES; axis++)
+ {
+ bb[axis][0] += px_to_xr (cell->style->margin[axis][0]);
+ bb[axis][1] -= px_to_xr (cell->style->margin[axis][1]);
+ }
+ if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
+ xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
+ cairo_restore (xr->cairo);
+}
+
+static int
+xr_adjust_break (void *xr_, const struct table_cell *cell,
+ int width, int height)
+{
+ struct xr_driver *xr = xr_;
+ int bb[TABLE_N_AXES][2];
+ int clip[TABLE_N_AXES][2];
+ int w, h, brk;
+
+ if (xr_measure_cell_height (xr_, cell, width) < height)
+ return -1;
+
+ bb[H][0] = 0;
+ bb[H][1] = width - px_to_xr (cell->style->margin[H][0]
+ + cell->style->margin[H][1]);
+ if (bb[H][1] <= 0)
+ return 0;
+ bb[V][0] = 0;
+ bb[V][1] = height - px_to_xr (cell->style->margin[V][0]
+ + cell->style->margin[V][1]);
+ clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
+ xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
+ return brk;
}
\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)
+xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
+{
+ if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
+ {
+ double x0 = xr_to_pt (clip[H][0] + xr->x);
+ double y0 = xr_to_pt (clip[V][0] + xr->y);
+ double x1 = xr_to_pt (clip[H][1] + xr->x);
+ double y1 = xr_to_pt (clip[V][1] + xr->y);
+
+ cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
+ cairo_clip (xr->cairo);
+ }
+}
+
+static void
+add_attr_with_start (PangoAttrList *list, PangoAttribute *attr, guint start_index)
{
- struct xr_font *font;
+ attr->start_index = start_index;
+ pango_attr_list_insert (list, attr);
+}
- font = (cell->options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
- : cell->options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
- : &xr->fonts[XR_FONT_PROPORTIONAL]);
+static int
+xr_layout_cell_text (struct xr_driver *xr,
+ const struct cell_contents *contents,
+ const struct cell_style *style,
+ int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
+ int *widthp, int *brk)
+{
+ unsigned int options = contents->options;
+ size_t length;
+ int w, h;
- pango_layout_set_text (font->layout, cell->contents, -1);
+ struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
+ : options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
+ : &xr->fonts[XR_FONT_PROPORTIONAL]);
+ struct xr_font local_font;
+ if (style->font)
+ {
+ PangoFontDescription *desc = parse_font (
+ style->font,
+ style->font_size ? style->font_size * 1000 * 72 / 128 : 10000,
+ style->bold, style->italic);
+ if (desc)
+ {
+ PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
+ pango_layout_set_font_description (layout, desc);
+
+ local_font.desc = desc;
+ local_font.layout = layout;
+ font = &local_font;
+ }
+ }
+
+ int footnote_adjustment;
+ if (contents->n_footnotes == 0)
+ footnote_adjustment = 0;
+ else if (contents->n_footnotes == 1 && (options & TAB_HALIGN) == TAB_RIGHT)
+ {
+ PangoAttrList *attrs;
+
+ const char *marker = contents->footnotes[0]->marker;
+ pango_layout_set_text (font->layout, marker, strlen (marker));
+
+ attrs = pango_attr_list_new ();
+ pango_attr_list_insert (attrs, pango_attr_rise_new (7000));
+ pango_layout_set_attributes (font->layout, attrs);
+ pango_attr_list_unref (attrs);
+
+ pango_layout_get_size (font->layout, &w, &h);
+ footnote_adjustment = MIN (w, px_to_xr (style->margin[H][1]));
+ }
+ else
+ footnote_adjustment = px_to_xr (style->margin[H][1]);
+
+ length = strlen (contents->text);
+ if (footnote_adjustment)
+ {
+ PangoAttrList *attrs;
+ struct string s;
+
+ bb[H][1] += footnote_adjustment;
+
+ ds_init_empty (&s);
+ ds_extend (&s, length + contents->n_footnotes * 10);
+ ds_put_cstr (&s, contents->text);
+ cell_contents_format_footnote_markers (contents, &s);
+ pango_layout_set_text (font->layout, ds_cstr (&s), ds_length (&s));
+ ds_destroy (&s);
+
+ attrs = pango_attr_list_new ();
+ if (style->underline)
+ pango_attr_list_insert (attrs, pango_attr_underline_new (
+ PANGO_UNDERLINE_SINGLE));
+ add_attr_with_start (attrs, pango_attr_rise_new (7000), length);
+ add_attr_with_start (
+ attrs, pango_attr_font_desc_new (font->desc), length);
+ pango_layout_set_attributes (font->layout, attrs);
+ pango_attr_list_unref (attrs);
+ }
+ else
+ {
+ pango_layout_set_text (font->layout, contents->text, -1);
+
+ if (style->underline)
+ {
+ PangoAttrList *attrs = pango_attr_list_new ();
+ pango_attr_list_insert (attrs, pango_attr_underline_new (
+ PANGO_UNDERLINE_SINGLE));
+ pango_layout_set_attributes (font->layout, attrs);
+ pango_attr_list_unref (attrs);
+ }
+ }
pango_layout_set_alignment (
font->layout,
- ((cell->options & TAB_ALIGNMENT) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
- : (cell->options & TAB_ALIGNMENT) == TAB_LEFT ? PANGO_ALIGN_LEFT
+ ((options & TAB_HALIGN) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
+ : (options & TAB_HALIGN) == 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_save (xr->cairo);
+ xr_clip (xr, clip);
+ cairo_translate (xr->cairo,
+ xr_to_pt (bb[H][0] + xr->x),
+ xr_to_pt (bb[V][0] + xr->y));
+ pango_cairo_show_layout (xr->cairo, font->layout);
- if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
+ /* If enabled, this draws a blue rectangle around the extents of each
+ line of text, which can be rather useful for debugging layout
+ issues. */
+ if (0)
{
- double x0 = xr_to_pt (clip[H][0]);
- double y0 = xr_to_pt (clip[V][0] + xr->y);
- double x1 = xr_to_pt (clip[H][1]);
- double y1 = xr_to_pt (clip[V][1] + xr->y);
-
- cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
- cairo_clip (xr->cairo);
+ PangoLayoutIter *iter;
+ iter = pango_layout_get_iter (font->layout);
+ do
+ {
+ PangoRectangle extents;
+
+ pango_layout_iter_get_line_extents (iter, &extents, NULL);
+ cairo_save (xr->cairo);
+ cairo_set_source_rgb (xr->cairo, 1, 0, 0);
+ dump_rectangle (xr,
+ pango_to_xr (extents.x) - xr->x,
+ pango_to_xr (extents.y) - xr->y,
+ pango_to_xr (extents.x + extents.width) - xr->x,
+ pango_to_xr (extents.y + extents.height) - xr->y);
+ cairo_restore (xr->cairo);
+ }
+ while (pango_layout_iter_next_line (iter));
+ pango_layout_iter_free (iter);
}
- cairo_translate (xr->cairo,
- xr_to_pt (bb[H][0]),
- xr_to_pt (bb[V][0] + xr->y));
- pango_cairo_show_layout (xr->cairo, font->layout);
cairo_restore (xr->cairo);
}
- if (width != NULL || height != NULL)
+ pango_layout_get_size (font->layout, &w, &h);
+ w = pango_to_xr (w);
+ h = pango_to_xr (h);
+ if (w > *widthp)
+ *widthp = w;
+ if (bb[V][0] + h >= bb[V][1])
{
- int w, h;
+ PangoLayoutIter *iter;
+ int best UNUSED = 0;
- pango_layout_get_size (font->layout, &w, &h);
- if (width != NULL)
- *width = w;
- if (height != NULL)
- *height = h;
+ /* Choose a breakpoint between lines instead of in the middle of one. */
+ iter = pango_layout_get_iter (font->layout);
+ do
+ {
+ PangoRectangle extents;
+ int y0, y1;
+ int bottom;
+
+ pango_layout_iter_get_line_extents (iter, NULL, &extents);
+ pango_layout_iter_get_line_yrange (iter, &y0, &y1);
+ extents.x = pango_to_xr (extents.x);
+ extents.y = pango_to_xr (y0);
+ extents.width = pango_to_xr (extents.width);
+ extents.height = pango_to_xr (y1 - y0);
+ bottom = bb[V][0] + extents.y + extents.height;
+ if (bottom < bb[V][1])
+ {
+ if (brk && clip[H][0] != clip[H][1])
+ best = bottom;
+ if (brk)
+ *brk = bottom;
+ }
+ else
+ break;
+ }
+ while (pango_layout_iter_next_line (iter));
+ pango_layout_iter_free (iter);
+
+ /* If enabled, draws a green line across the chosen breakpoint, which can
+ be useful for debugging issues with breaking. */
+ if (0)
+ {
+ if (best && !xr->nest)
+ dump_line (xr, -xr->left_margin, best,
+ xr->width + xr->right_margin, best,
+ RENDER_LINE_SINGLE, &CELL_COLOR (0, 255, 0));
+ }
}
+
+ pango_layout_set_attributes (font->layout, NULL);
+
+ if (font == &local_font)
+ {
+ g_object_unref (G_OBJECT (font->layout));
+ pango_font_description_free (font->desc);
+ }
+
+ return bb[V][0] + h;
}
static void
-xr_draw_title (struct xr_driver *xr, const char *title,
- int title_width, int title_height)
+xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
+ int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
+ int *width, int *height, int *brk)
{
- struct table_cell cell;
int bb[TABLE_N_AXES][2];
+ size_t i;
- xr_init_caption_cell (title, &cell);
- bb[H][0] = 0;
- bb[H][1] = title_width;
- bb[V][0] = 0;
- bb[V][1] = title_height;
- xr_draw_cell (xr, &cell, bb, bb);
+ *width = 0;
+ *height = 0;
+ if (brk)
+ *brk = 0;
+
+ memcpy (bb, bb_, sizeof bb);
+
+ /* If enabled, draws a blue rectangle around the cell extents, which can be
+ useful for debugging layout. */
+ if (0)
+ {
+ if (clip[H][0] != clip[H][1])
+ {
+ int offset = (xr->nest) * XR_POINT;
+
+ cairo_save (xr->cairo);
+ cairo_set_source_rgb (xr->cairo, 0, 0, 1);
+ dump_rectangle (xr,
+ bb[H][0] + offset, bb[V][0] + offset,
+ bb[H][1] - offset, bb[V][1] - offset);
+ cairo_restore (xr->cairo);
+ }
+ }
+
+ for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++)
+ {
+ const struct cell_contents *contents = &cell->contents[i];
+
+ if (brk)
+ *brk = bb[V][0];
+ if (i > 0)
+ {
+ bb[V][0] += xr->char_height / 2;
+ if (bb[V][0] >= bb[V][1])
+ break;
+ if (brk)
+ *brk = bb[V][0];
+ }
+
+ bb[V][0] = xr_layout_cell_text (xr, contents, cell->style, bb, clip,
+ width, brk);
+ }
+ *height = bb[V][0] - bb_[V][0];
}
\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 =
{
struct output_item *item;
/* Table items. */
- struct render_page *page;
+ struct render_pager *p;
struct xr_driver *xr;
- int title_width;
- int title_height;
};
#define CHART_WIDTH 500
struct table_item *table_item;
struct xr_rendering *r;
- table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
+ table_item = table_item_create (table_from_string (TAB_LEFT, text),
+ NULL, NULL);
r = xr_rendering_create (xr, &table_item->output_item, cr);
table_item_unref (table_item);
return r;
}
-void
+void
xr_rendering_apply_options (struct xr_rendering *xr, struct string_map *o)
{
if (is_table_item (xr->item))
r->item = output_item_ref (item);
r->xr = xr;
xr_set_cairo (xr, cr);
- r->page = xr_render_table_item (xr, to_table_item (item),
- &r->title_width, &r->title_height);
+ r->p = render_pager_create (xr->params, to_table_item (item));
}
else if (is_chart_item (item))
{
return r;
}
+void
+xr_rendering_destroy (struct xr_rendering *r)
+{
+ if (r)
+ {
+ output_item_unref (r->item);
+ render_pager_destroy (r->p);
+ free (r);
+ }
+}
+
void
xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
{
if (is_table_item (r->item))
{
- 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 = render_pager_get_size (r->p, H) / XR_POINT;
+ *h = render_pager_get_size (r->p, V) / XR_POINT;
}
else
{
static void xr_draw_chart (const struct chart_item *, cairo_t *,
double x, double y, double width, double height);
-/* Draws onto CR at least the region of R that is enclosed in (X,Y)-(X+W,Y+H),
- and possibly some additional parts. */
+/* Draws onto CR */
void
-xr_rendering_draw (struct xr_rendering *r, cairo_t *cr,
- int x, int y, int w, int h)
+xr_rendering_draw_all (struct xr_rendering *r, cairo_t *cr)
{
if (is_table_item (r->item))
{
xr_set_cairo (xr, cr);
- if (r->title_height > 0)
- {
- xr->y = 0;
- xr_draw_title (xr, table_item_get_caption (to_table_item (r->item)),
- r->title_width, r->title_height);
- }
+ render_pager_draw (r->p);
- xr->y = r->title_height;
- render_page_draw_region (r->page, x * 1024, (y * 1024) - r->title_height,
- w * 1024, h * 1024);
}
else
xr_draw_chart (to_chart_item (r->item), cr,
xrchart_draw_np_plot (chart_item, cr, &geom);
else if (is_piechart (chart_item))
xrchart_draw_piechart (chart_item, cr, &geom);
+ else if (is_barchart (chart_item))
+ xrchart_draw_barchart (chart_item, cr, &geom);
else if (is_roc_chart (chart_item))
xrchart_draw_roc (chart_item, cr, &geom);
else if (is_scree (chart_item))
xrchart_draw_scree (chart_item, cr, &geom);
else if (is_spreadlevel_plot_chart (chart_item))
xrchart_draw_spreadlevel (chart_item, cr, &geom);
+ else if (is_scatterplot_chart (chart_item))
+ xrchart_draw_scatterplot (chart_item, cr, &geom);
else
NOT_REACHED ();
xrchart_geometry_free (cr, &geom);
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);
{
struct xr_render_fsm fsm;
struct table_item *table_item;
- struct render_break x_break;
- struct render_break y_break;
- int caption_height;
+ struct render_pager *p;
};
static bool
{
struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
- for (;;)
+ while (render_pager_has_next (ts->p))
{
- struct render_page *y_slice;
- int space;
-
- while (!render_break_has_next (&ts->y_break))
- {
- struct render_page *x_slice;
-
- render_break_destroy (&ts->y_break);
- if (!render_break_has_next (&ts->x_break))
- return false;
-
- x_slice = render_break_next (&ts->x_break, xr->width);
- render_break_init (&ts->y_break, x_slice, V);
- }
+ int used;
- space = xr->length - xr->y;
- if (render_break_next_size (&ts->y_break) > space)
+ used = render_pager_draw_next (ts->p, xr->length - xr->y);
+ if (!used)
{
assert (xr->y > 0);
return true;
}
-
- y_slice = render_break_next (&ts->y_break, space);
- if (ts->caption_height)
- {
- if (xr->cairo)
- xr_draw_title (xr, table_item_get_caption (ts->table_item),
- xr->width, ts->caption_height);
-
- xr->y += ts->caption_height;
- ts->caption_height = 0;
- }
-
- if (xr->cairo)
- render_page_draw (y_slice);
- xr->y += render_page_get_size (y_slice, V);
- render_page_unref (y_slice);
+ else
+ xr->y += used;
}
+ return false;
}
static void
struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
table_item_unref (ts->table_item);
- render_break_destroy (&ts->x_break);
- render_break_destroy (&ts->y_break);
+ render_pager_destroy (ts->p);
free (ts);
}
xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
{
struct xr_table_state *ts;
- struct render_page *page;
- int caption_width;
ts = xmalloc (sizeof *ts);
ts->fsm.render = xr_table_render;
if (xr->y > 0)
xr->y += xr->char_height;
- page = xr_render_table_item (xr, table_item,
- &caption_width, &ts->caption_height);
- xr->params->size[V] = xr->length - ts->caption_height;
-
- render_break_init (&ts->x_break, page, H);
- render_break_init_empty (&ts->y_break);
+ ts->p = render_pager_create (xr->params, table_item);
return &ts->fsm;
}
{
struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
- if (xr->y > 0)
+ const int chart_height = 0.8 * (xr->length < xr->width ? xr->length : xr->width);
+
+ if (xr->y > xr->length - chart_height)
return true;
if (xr->cairo != NULL)
- xr_draw_chart (cs->chart_item, xr->cairo, 0.0, 0.0,
- xr_to_pt (xr->width), xr_to_pt (xr->length));
- xr->y = xr->length;
+ {
+ xr_draw_chart (cs->chart_item, xr->cairo,
+ 0.0,
+ xr_to_pt (xr->y),
+ xr_to_pt (xr->width),
+ xr_to_pt (chart_height));
+ }
+ xr->y += chart_height;
return false;
}
}
\f
static struct xr_render_fsm *
-xr_create_text_renderer (struct xr_driver *xr, const char *text)
+xr_create_text_renderer (struct xr_driver *xr, const struct text_item *item)
{
- struct table_item *table_item;
- struct xr_render_fsm *fsm;
-
- table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
- fsm = xr_render_table (xr, table_item);
+ struct tab_table *tab = tab_create (1, 1);
+
+ struct cell_style *style = pool_alloc (tab->container, sizeof *style);
+ *style = (struct cell_style) CELL_STYLE_INITIALIZER;
+ if (item->font)
+ style->font = pool_strdup (tab->container, item->font);
+ style->font_size = item->font_size;
+ style->bold = item->bold;
+ style->italic = item->italic;
+ style->underline = item->underline;
+ tab->styles[0] = style;
+
+ tab_text (tab, 0, 0, TAB_LEFT, text_item_get_text (item));
+ struct table_item *table_item = table_item_create (&tab->table, NULL, NULL);
+ struct xr_render_fsm *fsm = xr_render_table (xr, table_item);
table_item_unref (table_item);
return fsm;
break;
default:
- return xr_create_text_renderer (xr, text);
+ return xr_create_text_renderer (xr, text_item);
}
return NULL;
const struct message_item *message_item)
{
const struct msg *msg = message_item_get_msg (message_item);
- struct xr_render_fsm *fsm;
- char *s;
-
- s = msg_to_string (msg, xr->command_name);
- fsm = xr_create_text_renderer (xr, s);
+ char *s = msg_to_string (msg, message_item->command_name);
+ struct text_item *item = text_item_create (TEXT_ITEM_PARAGRAPH, s);
free (s);
+ struct xr_render_fsm *fsm = xr_create_text_renderer (xr, item);
+ text_item_unref (item);
return fsm;
}