src/output/cairo-chart.h \
src/output/cairo-fsm.c \
src/output/cairo-fsm.h \
+ src/output/cairo-pager.c \
+ src/output/cairo-pager.h \
src/output/cairo.c \
- src/output/cairo.h \
src/output/charts/boxplot-cairo.c \
src/output/charts/np-plot-cairo.c \
src/output/charts/barchart-cairo.c \
#include "libpspp/assertion.h"
#include "libpspp/message.h"
#include "math/chart-geometry.h"
-#include "output/cairo.h"
#include "output/chart-item.h"
#include "output/charts/barchart.h"
#include "output/charts/boxplot.h"
/* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
#define H TABLE_HORZ
#define V TABLE_VERT
-
+\f
struct xr_fsm_style *
xr_fsm_style_ref (const struct xr_fsm_style *style_)
{
return style;
}
+struct xr_fsm_style *
+xr_fsm_style_unshare (struct xr_fsm_style *old)
+{
+ assert (old->ref_cnt > 0);
+ if (old->ref_cnt == 1)
+ return old;
+
+ xr_fsm_style_unref (old);
+
+ struct xr_fsm_style *new = xmemdup (old, sizeof *old);
+ new->ref_cnt = 1;
+ for (int i = 0; i < XR_N_FONTS; i++)
+ if (old->fonts[i])
+ new->fonts[i] = pango_font_description_copy (old->fonts[i]);
+
+ return new;
+}
+
void
xr_fsm_style_unref (struct xr_fsm_style *style)
{
pango_cairo_context_set_resolution (context, xr->style->font_resolution);
PangoLayout *layout = pango_layout_new (context);
g_object_unref (context);
+
pango_layout_set_font_description (layout, desc);
const char *text = cell->text;
.ops = &xrr_render_ops,
.aux = fsm,
.size = { [H] = style->size[H], [V] = style->size[V] },
- /* XXX font_size */
.line_widths = xr_line_widths,
.min_break = { [H] = style->min_break[H], [V] = style->min_break[V] },
.supports_margins = true,
pango_cairo_context_set_resolution (context, style->font_resolution);
PangoLayout *layout = pango_layout_new (context);
g_object_unref (context);
+
pango_layout_set_font_description (layout, style->fonts[i]);
pango_layout_set_text (layout, "0", 1);
int char_size[TABLE_N_AXES];
pango_layout_get_size (layout, &char_size[H], &char_size[V]);
- for (int j = 0; j < TABLE_N_AXES; j++)
+ for (int a = 0; a < TABLE_N_AXES; a++)
{
- int csj = pango_to_xr (char_size[j]);
- fsm->rp.font_size[j] = MAX (fsm->rp.font_size[j], csj);
+ int csa = pango_to_xr (char_size[a]);
+ fsm->rp.font_size[a] = MAX (fsm->rp.font_size[a], csa);
}
g_object_unref (G_OBJECT (layout));
}
}
-
/* This is primarily meant for use with screen rendering since the result is a
fixed value for charts. */
void
static int
xr_fsm_draw_table (struct xr_fsm *fsm, int space)
{
- int used = 0;
- while (render_pager_has_next (fsm->p))
- {
- int chunk = render_pager_draw_next (fsm->p, space - used);
- if (!chunk)
- return used;
-
- used += chunk;
- cairo_translate (fsm->cairo, 0, chunk);
- }
- return used;
+ return (render_pager_has_next (fsm->p)
+ ? render_pager_draw_next (fsm->p, space)
+ : 0);
}
static int
struct xr_fsm;
struct output_item;
+/* The unit used for internal measurements is inch/(72 * XR_POINT).
+ (Thus, XR_POINT units represent one point.) */
+#define XR_POINT PANGO_SCALE
+
enum xr_font_type
{
XR_FONT_PROPORTIONAL,
{
int ref_cnt;
- int size[TABLE_N_AXES]; /* Page size. */
+ int size[TABLE_N_AXES]; /* Page size. */
int min_break[TABLE_N_AXES]; /* Minimum cell size to allow breaking. */
PangoFontDescription *fonts[XR_N_FONTS];
+ struct cell_color fg;
bool use_system_colors;
bool transparent;
double font_resolution;
};
struct xr_fsm_style *xr_fsm_style_ref (const struct xr_fsm_style *);
+struct xr_fsm_style *xr_fsm_style_unshare (struct xr_fsm_style *);
void xr_fsm_style_unref (struct xr_fsm_style *);
bool xr_fsm_style_equals (const struct xr_fsm_style *,
const struct xr_fsm_style *);
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009, 2010, 2014, 2020 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "output/cairo-pager.h"
+
+#include <math.h>
+#include <pango/pango-layout.h>
+#include <pango/pangocairo.h>
+
+#include "output/driver-provider.h"
+
+#include "gl/xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
+#define H TABLE_HORZ
+#define V TABLE_VERT
+\f
+struct xr_page_style *
+xr_page_style_ref (const struct xr_page_style *ps_)
+{
+ struct xr_page_style *ps = CONST_CAST (struct xr_page_style *, ps_);
+ assert (ps->ref_cnt > 0);
+ ps->ref_cnt++;
+ return ps;
+}
+
+struct xr_page_style *
+xr_page_style_unshare (struct xr_page_style *old)
+{
+ assert (old->ref_cnt > 0);
+ if (old->ref_cnt == 1)
+ return old;
+
+ xr_page_style_unref (old);
+
+ struct xr_page_style *new = xmemdup (old, sizeof *old);
+ new->ref_cnt = 1;
+ for (int i = 0; i < 2; i++)
+ page_heading_copy (&new->headings[i], &old->headings[i]);
+
+ return new;
+}
+
+void
+xr_page_style_unref (struct xr_page_style *ps)
+{
+ if (ps)
+ {
+ assert (ps->ref_cnt > 0);
+ if (!--ps->ref_cnt)
+ {
+ for (int i = 0; i < 2; i++)
+ page_heading_uninit (&ps->headings[i]);
+ free (ps);
+ }
+ }
+}
+
+bool
+xr_page_style_equals (const struct xr_page_style *a,
+ const struct xr_page_style *b)
+{
+ for (int i = 0; i < TABLE_N_AXES; i++)
+ for (int j = 0; j < 2; j++)
+ if (a->margins[i][j] != b->margins[i][j])
+ return false;
+
+ for (int i = 0; i < 2; i++)
+ if (!page_heading_equals (&a->headings[i], &b->headings[i]))
+ return false;
+
+ return (a->initial_page_number == b->initial_page_number
+ && a->object_spacing == b->object_spacing);
+}
+\f
+struct xr_pager
+ {
+ struct xr_page_style *page_style;
+ struct xr_fsm_style *fsm_style;
+ int page_index;
+ int heading_heights[2];
+
+ /* Current output item. */
+ struct xr_fsm *fsm;
+ struct output_item *item;
+
+ /* Current output page. */
+ cairo_t *cr;
+ int y;
+ };
+
+static void xr_pager_run (struct xr_pager *);
+
+/* Conversions to and from points. */
+static double
+xr_to_pt (int x)
+{
+ return x / (double) XR_POINT;
+}
+
+static int
+pango_to_xr (int pango)
+{
+ return (XR_POINT != PANGO_SCALE
+ ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
+ : pango);
+}
+
+static int
+xr_to_pango (int xr)
+{
+ return (XR_POINT != PANGO_SCALE
+ ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
+ : xr);
+}
+
+static int
+get_layout_height (PangoLayout *layout)
+{
+ int w, h;
+ pango_layout_get_size (layout, &w, &h);
+ return h;
+}
+
+static int
+xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
+ const struct page_heading *ph, int page_number,
+ int width, int base_y, double font_resolution)
+{
+ PangoContext *context = pango_cairo_create_context (cairo);
+ pango_cairo_context_set_resolution (context, font_resolution);
+ PangoLayout *layout = pango_layout_new (context);
+ g_object_unref (context);
+
+ pango_layout_set_font_description (layout, font);
+
+ int y = 0;
+ for (size_t i = 0; i < ph->n; i++)
+ {
+ const struct page_paragraph *pp = &ph->paragraphs[i];
+
+ char *markup = output_driver_substitute_heading_vars (pp->markup,
+ page_number);
+ pango_layout_set_markup (layout, markup, -1);
+ free (markup);
+
+ pango_layout_set_alignment (
+ layout,
+ (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
+ : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER
+ : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
+ : PANGO_ALIGN_RIGHT));
+ pango_layout_set_width (layout, xr_to_pango (width));
+
+ cairo_save (cairo);
+ cairo_translate (cairo, 0, xr_to_pt (y + base_y));
+ pango_cairo_show_layout (cairo, layout);
+ cairo_restore (cairo);
+
+ y += pango_to_xr (get_layout_height (layout));
+ }
+
+ g_object_unref (G_OBJECT (layout));
+
+ return y;
+}
+
+static void
+xr_measure_headings (const struct xr_page_style *ps,
+ const struct xr_fsm_style *fs,
+ int heading_heights[2])
+{
+ cairo_surface_t *surface = cairo_recording_surface_create (
+ CAIRO_CONTENT_COLOR, NULL);
+ cairo_t *cairo = cairo_create (surface);
+ for (int i = 0; i < 2; i++)
+ {
+ int *h = &heading_heights[i];
+ *h = xr_render_page_heading (cairo, fs->fonts[XR_FONT_PROPORTIONAL],
+ &ps->headings[i], -1, fs->size[H], 0,
+ fs->font_resolution);
+ if (*h)
+ *h += ps->object_spacing;
+ }
+ cairo_destroy (cairo);
+ cairo_surface_destroy (surface);
+}
+
+struct xr_pager *
+xr_pager_create (const struct xr_page_style *ps_,
+ const struct xr_fsm_style *fs_)
+{
+ struct xr_page_style *ps = xr_page_style_ref (ps_);
+ struct xr_fsm_style *fs = xr_fsm_style_ref (fs_);
+
+ int heading_heights[2];
+ xr_measure_headings (ps, fs, heading_heights);
+ int total = heading_heights[0] + heading_heights[1];
+ if (total > 0 && total < fs->size[V])
+ {
+ fs = xr_fsm_style_unshare (fs);
+ ps = xr_page_style_unshare (ps);
+
+ for (int i = 0; i < 2; i++)
+ ps->margins[V][i] += heading_heights[i];
+ fs->size[V] -= total;
+ }
+
+ struct xr_pager *p = xmalloc (sizeof *p);
+ *p = (struct xr_pager) { .page_style = ps, .fsm_style = fs };
+ return p;
+}
+
+void
+xr_pager_destroy (struct xr_pager *p)
+{
+ if (p)
+ {
+ xr_page_style_unref (p->page_style);
+ xr_fsm_style_unref (p->fsm_style);
+
+ xr_fsm_destroy (p->fsm);
+ output_item_unref (p->item);
+
+ if (p->cr)
+ {
+ cairo_restore (p->cr);
+ cairo_destroy (p->cr);
+ }
+ free (p);
+ }
+}
+
+bool
+xr_pager_has_item (const struct xr_pager *p)
+{
+ return p->item != NULL;
+}
+
+void
+xr_pager_add_item (struct xr_pager *p, const struct output_item *item)
+{
+ assert (!p->item);
+ p->item = output_item_ref (item);
+ xr_pager_run (p);
+}
+
+bool
+xr_pager_has_page (const struct xr_pager *p)
+{
+ return p->cr;
+}
+
+void
+xr_pager_add_page (struct xr_pager *p, cairo_t *cr)
+{
+ assert (!p->cr);
+ cairo_save (cr);
+ p->cr = cr;
+ p->y = 0;
+
+ const struct xr_fsm_style *fs = p->fsm_style;
+ const struct xr_page_style *ps = p->page_style;
+ const struct cell_color *bg = &ps->bg;
+ if (bg->alpha)
+ {
+ cairo_save (cr);
+ cairo_set_source_rgba (cr, bg->r / 255.0, bg->g / 255.0,
+ bg->b / 255.0, bg->alpha / 255.0);
+ cairo_rectangle (cr, 0, 0, fs->size[H], fs->size[V]);
+ cairo_fill (cr);
+ cairo_restore (cr);
+ }
+ cairo_translate (cr,
+ xr_to_pt (ps->margins[H][0]),
+ xr_to_pt (ps->margins[V][0]));
+
+ const PangoFontDescription *font = fs->fonts[XR_FONT_PROPORTIONAL];
+ int page_number = p->page_index++ + ps->initial_page_number;
+ if (p->heading_heights[0])
+ xr_render_page_heading (cr, font, &ps->headings[0], page_number,
+ fs->size[H], -p->heading_heights[0],
+ fs->font_resolution);
+
+ if (p->heading_heights[1])
+ xr_render_page_heading (cr, font, &ps->headings[1], page_number,
+ fs->size[H], fs->size[V] + ps->object_spacing,
+ fs->font_resolution);
+
+ xr_pager_run (p);
+}
+
+bool
+xr_pager_needs_new_page (struct xr_pager *p)
+{
+ if (p->item && (!p->cr || p->y >= p->fsm_style->size[V]))
+ {
+ if (p->cr)
+ {
+ cairo_restore (p->cr);
+ cairo_destroy (p->cr);
+ p->cr = NULL;
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+static void
+xr_pager_run (struct xr_pager *p)
+{
+ if (p->item && p->cr && p->y < p->fsm_style->size[V])
+ {
+ if (!p->fsm)
+ {
+ p->fsm = xr_fsm_create (p->item, p->fsm_style, p->cr);
+ if (!p->fsm)
+ {
+ output_item_unref (p->item);
+ p->item = NULL;
+
+ return;
+ }
+ }
+
+ for (;;)
+ {
+ int spacing = p->page_style->object_spacing;
+ int chunk = xr_fsm_draw_slice (p->fsm, p->cr,
+ p->fsm_style->size[V] - p->y);
+ p->y += chunk + spacing;
+ cairo_translate (p->cr, 0, xr_to_pt (chunk + spacing));
+
+ if (xr_fsm_is_empty (p->fsm))
+ {
+ xr_fsm_destroy (p->fsm);
+ p->fsm = NULL;
+ output_item_unref (p->item);
+ p->item = NULL;
+ return;
+ }
+ else if (!chunk)
+ {
+ assert (p->y > 0);
+ p->y = INT_MAX;
+ return;
+ }
+ }
+ }
+}
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009, 2010, 2014, 2020 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CAIRO_PAGER_H
+#define OUTPUT_CAIRO_PAGER_H 1
+
+#include <stdbool.h>
+
+#ifdef HAVE_CAIRO
+
+/* Cairo output driver paginater. */
+
+#include <cairo/cairo.h>
+#include <pango/pango-font.h>
+#include "output/cairo-fsm.h"
+#include "output/page-setup-item.h"
+#include "output/table.h"
+
+struct xr_page_style
+ {
+ int ref_cnt;
+
+ int margins[TABLE_N_AXES][2]; /* Margins. */
+
+ struct page_heading headings[2]; /* Top and bottom headings. */
+
+ struct cell_color bg; /* Background color. */
+ int initial_page_number;
+ int object_spacing;
+ };
+struct xr_page_style *xr_page_style_ref (const struct xr_page_style *);
+struct xr_page_style *xr_page_style_unshare (struct xr_page_style *);
+void xr_page_style_unref (struct xr_page_style *);
+bool xr_page_style_equals (const struct xr_page_style *,
+ const struct xr_page_style *);
+struct xr_page_style *xr_page_style_default (void);
+
+static inline int
+xr_page_style_paper_size (const struct xr_page_style *ps,
+ const struct xr_fsm_style *fs, enum table_axis a)
+{
+ return fs->size[a] + ps->margins[a][0] + ps->margins[a][1];
+}
+
+struct xr_pager *xr_pager_create (const struct xr_page_style *,
+ const struct xr_fsm_style *);
+void xr_pager_destroy (struct xr_pager *);
+
+bool xr_pager_has_item (const struct xr_pager *);
+void xr_pager_add_item (struct xr_pager *, const struct output_item *);
+
+bool xr_pager_has_page (const struct xr_pager *);
+void xr_pager_add_page (struct xr_pager *, cairo_t *);
+bool xr_pager_needs_new_page (struct xr_pager *);
+
+#endif /* HAVE_CAIRO */
+
+#endif /* output/cairo-pager.h */
#include <config.h>
-#include "output/cairo.h"
-
#include "libpspp/assertion.h"
#include "libpspp/cast.h"
-#include "libpspp/hash-functions.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/cairo-fsm.h"
-#include "output/chart-item-provider.h"
+#include "output/cairo-pager.h"
#include "output/driver-provider.h"
-#include "output/group-item.h"
-#include "output/message-item.h"
#include "output/options.h"
-#include "output/page-eject-item.h"
-#include "output/page-setup-item.h"
-#include "output/render.h"
-#include "output/table-item.h"
#include "output/table.h"
-#include "output/text-item.h"
#include <cairo/cairo-pdf.h>
#include <cairo/cairo-ps.h>
#include <inttypes.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/c-ctype.h"
#include "gl/c-strcase.h"
-#include "gl/intprops.h"
-#include "gl/minmax.h"
#include "gl/xalloc.h"
#include "gettext.h"
return x / (double) XR_POINT;
}
-/* Dimensions for drawing lines in tables. */
-#define XR_LINE_WIDTH (XR_POINT / 2) /* Width of an ordinary line. */
-#define XR_LINE_SPACE XR_POINT /* Space between double lines. */
-
/* Output types. */
enum xr_output_type
{
{
struct output_driver driver;
- /* User parameters. */
- PangoFontDescription *fonts[XR_N_FONTS];
-
- /* Measurements all in inch/(72 * XR_POINT). */
- int size[TABLE_N_AXES]; /* Page size with margins subtracted. */
- int margins[TABLE_N_AXES][2]; /* Margins. */
- int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
- int object_spacing; /* Space between output objects. */
-
- struct cell_color bg; /* Background color */
- struct cell_color fg; /* Foreground color */
- bool transparent; /* true -> do not render background */
- bool systemcolors; /* true -> do not change colors */
-
- int initial_page_number;
-
- struct page_heading headings[2]; /* Top and bottom headings. */
- int headings_height[2];
-
- /* Internal state. */
- struct xr_fsm_style *style;
- int char_width, char_height;
- cairo_t *cairo;
+ struct xr_fsm_style *fsm_style;
+ struct xr_page_style *page_style;
+ struct xr_pager *pager;
cairo_surface_t *surface;
- int page_number; /* Current page number. */
- int y;
- struct xr_fsm *fsm;
};
static const struct output_driver_class cairo_driver_class;
-static void xr_driver_destroy_fsm (struct xr_driver *);
-static void xr_driver_run_fsm (struct xr_driver *);
\f
/* Output driver basics. */
return desc;
}
-static void
-apply_options (struct xr_driver *xr, struct string_map *o)
+static struct xr_driver *
+xr_allocate (const char *name, int device_type, struct string_map *o)
{
+ struct xr_driver *xr = xzalloc (sizeof *xr);
struct output_driver *d = &xr->driver;
- /* In inch/72000 units used by parse_paper_size() and parse_dimension(). */
+ output_driver_init (d, &cairo_driver_class, name, device_type);
/* Scale factor from inch/72000 to inch/(72 * XR_POINT). */
const double scale = XR_POINT / 1000.;
- for (int i = 0; i < XR_N_FONTS; i++)
- if (xr->fonts[i] != NULL)
- pango_font_description_free (xr->fonts[i]);
-
- int font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
- xr->fonts[XR_FONT_FIXED] = parse_font_option
- (d, o, "fixed-font", "monospace", font_size, false, false);
- xr->fonts[XR_FONT_PROPORTIONAL] = parse_font_option (
- d, o, "prop-font", "sans serif", font_size, false, false);
-
- xr->fg = parse_color (opt (d, o, "foreground-color", "#000000000000"));
- xr->bg = parse_color (opt (d, o, "background-color", "#FFFFFFFFFFFF"));
-
- xr->transparent = parse_boolean (opt (d, o, "transparent", "false"));
- xr->systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
-
- /* Get dimensions. */
int paper[TABLE_N_AXES];
parse_paper_size (opt (d, o, "paper-size", ""), &paper[H], &paper[V]);
+ for (int a = 0; a < TABLE_N_AXES; a++)
+ paper[a] *= scale;
int margins[TABLE_N_AXES][2];
- margins[H][0] = parse_dimension (opt (d, o, "left-margin", ".5in"));
- margins[H][1] = parse_dimension (opt (d, o, "right-margin", ".5in"));
- margins[V][0] = parse_dimension (opt (d, o, "top-margin", ".5in"));
- margins[V][1] = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
+ margins[H][0] = parse_dimension (opt (d, o, "left-margin", ".5in")) * scale;
+ margins[H][1] = parse_dimension (opt (d, o, "right-margin", ".5in")) * scale;
+ margins[V][0] = parse_dimension (opt (d, o, "top-margin", ".5in")) * scale;
+ margins[V][1] = parse_dimension (opt (d, o, "bottom-margin", ".5in")) * scale;
+
+ int size[TABLE_N_AXES];
+ for (int a = 0; a < TABLE_N_AXES; a++)
+ size[a] = paper[a] - margins[a][0] - margins[a][1];
int min_break[TABLE_N_AXES];
min_break[H] = parse_dimension (opt (d, o, "min-hbreak", NULL)) * scale;
min_break[V] = parse_dimension (opt (d, o, "min-vbreak", NULL)) * scale;
-
- int object_spacing = (parse_dimension (opt (d, o, "object-spacing", NULL))
- * scale);
-
- /* Convert to inch/(XR_POINT * 72). */
for (int a = 0; a < TABLE_N_AXES; a++)
- {
- for (int i = 0; i < 2; i++)
- xr->margins[a][i] = margins[a][i] * scale;
- xr->size[a] = (paper[a] - margins[a][0] - margins[a][1]) * scale;
- xr->min_break[a] = min_break[a] >= 0 ? min_break[a] : xr->size[a] / 2;
- }
- xr->object_spacing = object_spacing >= 0 ? object_spacing : XR_POINT * 12;
-
- /* There are no headings so headings_height can stay 0. */
-}
-
-static struct xr_driver *
-xr_allocate (const char *name, int device_type, struct string_map *o)
-{
- struct xr_driver *xr = xzalloc (sizeof *xr);
- struct output_driver *d = &xr->driver;
-
- output_driver_init (d, &cairo_driver_class, name, device_type);
-
- apply_options (xr, o);
-
- return xr;
-}
-
-static int
-pango_to_xr (int pango)
-{
- return (XR_POINT != PANGO_SCALE
- ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
- : pango);
-}
-
-static int
-xr_to_pango (int xr)
-{
- return (XR_POINT != PANGO_SCALE
- ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
- : xr);
-}
-
-static void
-xr_measure_fonts (cairo_t *cairo, PangoFontDescription *fonts[XR_N_FONTS],
- int *char_width, int *char_height)
-{
- *char_width = 0;
- *char_height = 0;
- for (int i = 0; i < XR_N_FONTS; i++)
- {
- PangoContext *context = pango_cairo_create_context (cairo);
- pango_cairo_context_set_resolution (context, 72.0);
- PangoLayout *layout = pango_layout_new (context);
- g_object_unref (context);
- pango_layout_set_font_description (layout, fonts[i]);
-
- pango_layout_set_text (layout, "0", 1);
-
- int cw, ch;
- pango_layout_get_size (layout, &cw, &ch);
- *char_width = MAX (*char_width, pango_to_xr (cw));
- *char_height = MAX (*char_height, pango_to_xr (ch));
-
- g_object_unref (G_OBJECT (layout));
- }
-}
-
-static int
-get_layout_height (PangoLayout *layout)
-{
- int w, h;
- pango_layout_get_size (layout, &w, &h);
- return h;
-}
+ if (min_break[a] <= 0)
+ min_break[a] = size[a] / 2;
-static int
-xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
- const struct page_heading *ph, int page_number,
- int width, bool draw, int base_y)
-{
- PangoContext *context = pango_cairo_create_context (cairo);
- pango_cairo_context_set_resolution (context, 72.0);
- PangoLayout *layout = pango_layout_new (context);
- g_object_unref (context);
-
- pango_layout_set_font_description (layout, font);
+ int font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000);
+ PangoFontDescription *fixed_font = parse_font_option
+ (d, o, "fixed-font", "monospace", font_size, false, false);
+ PangoFontDescription *proportional_font = parse_font_option (
+ d, o, "prop-font", "sans serif", font_size, false, false);
- int y = 0;
- for (size_t i = 0; i < ph->n; i++)
- {
- const struct page_paragraph *pp = &ph->paragraphs[i];
-
- char *markup = output_driver_substitute_heading_vars (pp->markup,
- page_number);
- pango_layout_set_markup (layout, markup, -1);
- free (markup);
-
- pango_layout_set_alignment (
- layout,
- (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
- : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER
- : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
- : PANGO_ALIGN_RIGHT));
- pango_layout_set_width (layout, xr_to_pango (width));
- if (draw)
- {
- cairo_save (cairo);
- cairo_translate (cairo, 0, xr_to_pt (y + base_y));
- pango_cairo_show_layout (cairo, layout);
- cairo_restore (cairo);
- }
-
- y += pango_to_xr (get_layout_height (layout));
- }
+ struct cell_color fg = parse_color (opt (d, o, "foreground-color", "black"));
- g_object_unref (G_OBJECT (layout));
+ bool transparent = parse_boolean (opt (d, o, "transparent", "false"));
+ struct cell_color bg = (transparent
+ ? (struct cell_color) { .alpha = 0 }
+ : parse_color (opt (d, o, "background-color",
+ "white")));
- return y;
-}
+ bool systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
-static int
-xr_measure_headings (cairo_surface_t *surface,
- const PangoFontDescription *font,
- const struct page_heading headings[2],
- int width, int object_spacing, int height[2])
-{
- cairo_t *cairo = cairo_create (surface);
- int total = 0;
- for (int i = 0; i < 2; i++)
- {
- int h = xr_render_page_heading (cairo, font, &headings[i], -1,
- width, false, 0);
+ int object_spacing
+ = parse_dimension (opt (d, o, "object-spacing", NULL)) * scale;
+ if (object_spacing <= 0)
+ object_spacing = XR_POINT * 12;
- /* If the top heading is nonempty, add some space below it. */
- if (h && i == 0)
- h += object_spacing;
+ xr->page_style = xmalloc (sizeof *xr->page_style);
+ *xr->page_style = (struct xr_page_style) {
+ .ref_cnt = 1,
- if (height)
- height[i] = h;
- total += h;
- }
- cairo_destroy (cairo);
- return total;
-}
+ .margins = {
+ [H] = { margins[H][0], margins[H][1], },
+ [V] = { margins[V][0], margins[V][1], },
+ },
-static bool
-xr_check_fonts (cairo_surface_t *surface,
- PangoFontDescription *fonts[XR_N_FONTS],
- int usable_width, int usable_length)
-{
- cairo_t *cairo = cairo_create (surface);
- int char_width, char_height;
- xr_measure_fonts (cairo, fonts, &char_width, &char_height);
- cairo_destroy (cairo);
-
- bool ok = true;
- enum { MIN_WIDTH = 3, MIN_LENGTH = 3 };
- if (usable_width / char_width < MIN_WIDTH)
- {
- 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, usable_width / char_width);
- ok = false;
- }
- if (usable_length / char_height < MIN_LENGTH)
- {
- 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, usable_length / char_height);
- ok = false;
- }
- return ok;
-}
-
-static void
-xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
-{
- xr->cairo = cairo;
-
- cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
-
- xr_measure_fonts (xr->cairo, xr->fonts, &xr->char_width, &xr->char_height);
+ .bg = bg,
+ .initial_page_number = 1,
+ .object_spacing = object_spacing,
+ };
- if (xr->style == NULL)
- {
- xr->style = xmalloc (sizeof *xr->style);
- *xr->style = (struct xr_fsm_style) {
- .ref_cnt = 1,
- .size = { [H] = xr->size[H], [V] = xr->size[V] },
- .min_break = { [H] = xr->min_break[H], [V] = xr->min_break[V] },
- .use_system_colors = xr->systemcolors,
- .transparent = xr->transparent,
- .font_resolution = 72.0,
- };
-
- for (size_t i = 0; i < XR_N_FONTS; i++)
- xr->style->fonts[i] = pango_font_description_copy (xr->fonts[i]);
- }
+ xr->fsm_style = xmalloc (sizeof *xr->fsm_style);
+ *xr->fsm_style = (struct xr_fsm_style) {
+ .ref_cnt = 1,
+ .size = { [H] = size[H], [V] = size[V] },
+ .min_break = { [H] = min_break[H], [V] = min_break[V] },
+ .fonts = {
+ [XR_FONT_PROPORTIONAL] = proportional_font,
+ [XR_FONT_FIXED] = fixed_font,
+ },
+ .fg = fg,
+ .use_system_colors = systemcolors,
+ .transparent = transparent,
+ .font_resolution = 72.0,
+ };
- if (!xr->systemcolors)
- cairo_set_source_rgb (xr->cairo,
- xr->fg.r / 255.0, xr->fg.g / 255.0, xr->fg.b / 255.0);
+ return xr;
}
static struct output_driver *
const char *file_name = fh_get_file_name (fh);
struct xr_driver *xr = xr_allocate (file_name, device_type, o);
- double paper_pt[TABLE_N_AXES];
+ double paper[TABLE_N_AXES];
for (int a = 0; a < TABLE_N_AXES; a++)
- paper_pt[a] = xr_to_pt (xr->size[a]
- + xr->margins[a][0] + xr->margins[a][1]);
+ paper[a] = xr_to_pt (xr_page_style_paper_size (xr->page_style,
+ xr->fsm_style, a));
if (file_type == XR_PDF)
- xr->surface = cairo_pdf_surface_create (file_name,
- paper_pt[H], paper_pt[V]);
+ xr->surface = cairo_pdf_surface_create (file_name, paper[H], paper[V]);
else if (file_type == XR_PS)
- xr->surface = cairo_ps_surface_create (file_name, paper_pt[H], paper_pt[V]);
+ xr->surface = cairo_ps_surface_create (file_name, paper[H], paper[V]);
else if (file_type == XR_SVG)
- xr->surface = cairo_svg_surface_create (file_name,
- paper_pt[H], paper_pt[V]);
+ xr->surface = cairo_svg_surface_create (file_name, paper[H], paper[V]);
else
NOT_REACHED ();
goto error;
}
- if (!xr_check_fonts (xr->surface, xr->fonts, xr->size[H], xr->size[V]))
- goto error;
-
fh_unref (fh);
return &xr->driver;
xr_destroy (struct output_driver *driver)
{
struct xr_driver *xr = xr_driver_cast (driver);
- size_t i;
- xr_driver_destroy_fsm (xr);
-
- if (xr->cairo != NULL)
+ if (xr->surface)
{
cairo_surface_finish (xr->surface);
- cairo_status_t status = cairo_status (xr->cairo);
+ cairo_status_t status = cairo_surface_status (xr->surface);
if (status != CAIRO_STATUS_SUCCESS)
fprintf (stderr, _("error drawing output for %s driver: %s"),
output_driver_get_name (driver),
cairo_status_to_string (status));
cairo_surface_destroy (xr->surface);
-
- cairo_destroy (xr->cairo);
}
- for (i = 0; i < XR_N_FONTS; i++)
- if (xr->fonts[i] != NULL)
- pango_font_description_free (xr->fonts[i]);
-
- xr_fsm_style_unref (xr->style);
+ xr_pager_destroy (xr->pager);
+ xr_page_style_unref (xr->page_style);
+ xr_fsm_style_unref (xr->fsm_style);
free (xr);
}
{
struct xr_driver *xr = xr_driver_cast (driver);
- cairo_surface_flush (cairo_get_target (xr->cairo));
+ if (xr->surface)
+ cairo_surface_flush (xr->surface);
}
static void
{
struct xr_driver *xr = xr_driver_cast (driver);
- xr->initial_page_number = ps->initial_page_number;
- xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
-
- if (xr->cairo)
- return;
-
- int size[TABLE_N_AXES];
- for (int a = 0; a < TABLE_N_AXES; a++)
- {
- double total_margin = ps->margins[a][0] + ps->margins[a][1];
- size[a] = (ps->paper[a] - total_margin) * 72 * XR_POINT;
- }
-
- int headings_height[2];
- size[V] -= xr_measure_headings (
- xr->surface, xr->fonts[XR_FONT_PROPORTIONAL], ps->headings,
- size[H], xr->object_spacing, headings_height);
+ const double scale = 72 * XR_POINT;
int swap = ps->orientation == PAGE_LANDSCAPE;
enum table_axis h = H ^ swap;
enum table_axis v = V ^ swap;
- if (!xr_check_fonts (xr->surface, xr->fonts, size[h], size[v]))
- return;
- for (int i = 0; i < 2; i++)
- {
- page_heading_uninit (&xr->headings[i]);
- page_heading_copy (&xr->headings[i], &ps->headings[i]);
- xr->headings_height[i] = headings_height[i];
- }
+ xr_page_style_unref (xr->page_style);
+ xr->page_style = xmalloc (sizeof *xr->page_style);
+ *xr->page_style = (struct xr_page_style) {
+ .ref_cnt = 1,
- for (int a = 0; a < TABLE_N_AXES; a++)
- {
- xr->size[a] = size[a ^ swap];
- for (int i = 0; i < 2; i++)
- xr->margins[a][i] = ps->margins[a ^ swap][i] * 72 * XR_POINT;
- }
- cairo_pdf_surface_set_size (xr->surface,
- ps->paper[h] * 72.0, ps->paper[v] * 72.0);
+ .margins = {
+ [H] = { ps->margins[h][0] * scale, ps->margins[h][1] * scale },
+ [V] = { ps->margins[v][0] * scale, ps->margins[v][1] * scale },
+ },
+
+ .bg = xr->page_style->bg,
+ .initial_page_number = ps->initial_page_number,
+ .object_spacing = ps->object_spacing * 72 * XR_POINT,
+ };
+ for (size_t i = 0; i < 2; i++)
+ page_heading_copy (&xr->page_style->headings[i], &ps->headings[i]);
+
+ struct xr_fsm_style *old_fs = xr->fsm_style;
+ xr->fsm_style = xmalloc (sizeof *xr->fsm_style);
+ *xr->fsm_style = (struct xr_fsm_style) {
+ .ref_cnt = 1,
+ .size = { [H] = ps->paper[H] * scale, [V] = ps->paper[V] * scale },
+ .min_break = {
+ [H] = ps->paper[H] * scale / 2,
+ [V] = ps->paper[V] * scale / 2,
+ },
+ .fg = old_fs->fg,
+ .use_system_colors = old_fs->use_system_colors,
+ .transparent = old_fs->transparent,
+ .font_resolution = 72.0,
+ };
+ for (size_t i = 0; i < XR_N_FONTS; i++)
+ xr->fsm_style->fonts[i] = pango_font_description_copy (old_fs->fonts[i]);
+ xr_fsm_style_unref (old_fs);
+
+ cairo_pdf_surface_set_size (xr->surface, ps->paper[H] * 72.0,
+ ps->paper[V] * 72.0);
}
static void
if (is_page_setup_item (output_item))
{
- xr_update_page_setup (driver,
- to_page_setup_item (output_item)->page_setup);
+ if (!xr->pager)
+ xr_update_page_setup (driver,
+ to_page_setup_item (output_item)->page_setup);
return;
}
- if (!xr->cairo)
+ if (!xr->pager)
{
- xr->page_number = xr->initial_page_number - 1;
- xr_set_cairo (xr, cairo_create (xr->surface));
- cairo_save (xr->cairo);
- xr_driver_next_page (xr, xr->cairo);
+ xr->pager = xr_pager_create (xr->page_style, xr->fsm_style);
+ xr_pager_add_page (xr->pager, cairo_create (xr->surface));
}
- xr_driver_output_item (xr, output_item);
- while (xr_driver_need_new_page (xr))
+ xr_pager_add_item (xr->pager, output_item);
+ while (xr_pager_needs_new_page (xr->pager))
{
- cairo_restore (xr->cairo);
- cairo_show_page (xr->cairo);
- cairo_save (xr->cairo);
- xr_driver_next_page (xr, xr->cairo);
- }
-}
-\f
-/* Functions for rendering a series of output items to a series of Cairo
- contexts, with pagination.
-
- Used by PSPPIRE for printing, and by the basic Cairo output driver above as
- its underlying implementation.
-
- See the big comment in cairo.h for intended usage. */
-
-/* Gives new page CAIRO to XR for output. */
-void
-xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
-{
- if (!xr->transparent)
- {
- cairo_save (cairo);
- cairo_set_source_rgb (cairo,
- xr->bg.r / 255.0, xr->bg.g / 255.0, xr->bg.b / 255.0);
- cairo_rectangle (cairo, 0, 0, xr->size[H], xr->size[V]);
- cairo_fill (cairo);
- cairo_restore (cairo);
- }
- cairo_translate (cairo,
- xr_to_pt (xr->margins[H][0]),
- xr_to_pt (xr->margins[V][0] + xr->headings_height[0]));
-
- xr->page_number++;
- xr->cairo = cairo;
- xr->y = 0;
-
- xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL],
- &xr->headings[0], xr->page_number, xr->size[H], true,
- -xr->headings_height[0]);
- xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL],
- &xr->headings[1], xr->page_number, xr->size[H], true,
- xr->size[V]);
-
- xr_driver_run_fsm (xr);
-}
-
-/* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
- rendering a previous output item, that is, only if xr_driver_need_new_page()
- returns false. */
-void
-xr_driver_output_item (struct xr_driver *xr,
- const struct output_item *output_item)
-{
- assert (xr->fsm == NULL);
- xr->fsm = xr_fsm_create (output_item, xr->style, xr->cairo);
- xr_driver_run_fsm (xr);
-}
-
-/* Returns true if XR is in the middle of rendering an output item and needs a
- new page to be appended using xr_driver_next_page() to make progress,
- otherwise false. */
-bool
-xr_driver_need_new_page (const struct xr_driver *xr)
-{
- return xr->fsm != NULL;
-}
-
-/* Returns true if the current page doesn't have any content yet. */
-bool
-xr_driver_is_page_blank (const struct xr_driver *xr)
-{
- return xr->y == 0;
-}
-
-static void
-xr_driver_destroy_fsm (struct xr_driver *xr)
-{
- xr_fsm_destroy (xr->fsm);
- xr->fsm = NULL;
-}
-
-static void
-xr_driver_run_fsm (struct xr_driver *xr)
-{
- if (xr->fsm != NULL)
- {
- cairo_save (xr->cairo);
- cairo_translate (xr->cairo, 0, xr_to_pt (xr->y));
- int used = xr_fsm_draw_slice (xr->fsm, xr->cairo, xr->size[V] - xr->y);
- xr->y += used;
- cairo_restore (xr->cairo);
-
- if (xr_fsm_is_empty (xr->fsm))
- xr_driver_destroy_fsm (xr);
+ cairo_surface_show_page (xr->surface);
+ xr_pager_add_page (xr->pager, cairo_create (xr->surface));
}
}
\f
xr_submit,
xr_flush,
};
-\f
-struct xr_driver *
-xr_driver_create (cairo_t *cairo, struct string_map *options)
-{
- struct xr_driver *xr = xr_allocate ("cairo", 0, options);
- xr_set_cairo (xr, cairo);
- return xr;
-}
-
-/* Destroy XR, which should have been created with xr_driver_create(). Any
- cairo_t added to XR is not destroyed, because it is owned by the client. */
-void
-xr_driver_destroy (struct xr_driver *xr)
-{
- if (xr != NULL)
- {
- xr->cairo = NULL;
- output_driver_destroy (&xr->driver);
- }
-}
+++ /dev/null
-/* PSPP - a program for statistical analysis.
- Copyright (C) 2009, 2010, 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
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef OUTPUT_CAIRO_H
-#define OUTPUT_CAIRO_H 1
-
-#include <stdbool.h>
-
-#ifdef HAVE_CAIRO
-
-#include <cairo/cairo.h>
-
-struct cell_color;
-struct chart_item;
-struct output_driver;
-struct output_item;
-struct string_map;
-
-/* Creating and destroying Cairo output drivers. */
-struct xr_driver *xr_driver_create (cairo_t *, struct string_map *options);
-void xr_driver_destroy (struct xr_driver *);
-
-/* Functions for rendering a series of output items to a series of Cairo
- contexts, with pagination, possibly including headers.
-
- The intended usage pattern is this:
-
- * Create an xr_driver with xr_driver_create(). The cairo_t passed in must
- accurately reflect the properties of the output (e.g. for the purpose of
- page size and font selection) but need not be used for rendering.
-
- * Call xr_driver_next_page() to set up the first real output page's
- cairo_t. (You can skip this step if the cairo_t passed to
- xr_driver_create() can be used.)
-
- * Then, for each output_item:
-
- - Pass the output item to xr_driver_output_item(). As much output as
- fits will be rendered on the current page.
-
- - Then, as long as xr_driver_need_new_page() returns true, obtain a new
- page for rendering and pass it to xr_driver_next_page(). As much
- output as fits on the new page will be rendered on it.
-
- * When you're done, destroy the output driver with xr_driver_destroy().
-
- These functions may also be used for counting pages without actually
- rendering output. Follow the same steps, except pass NULL as the cairo_t to
- xr_driver_next_page(). (But xr_driver_create() still needs a valid cairo_t
- for page setup.)
-
- (If the cairo_t that you pass to xr_driver_create() won't remain valid, be
- sure to clear it out one way or another before calling xr_driver_destroy(),
- so that xr_driver_destroy() won't destroy it itself.)
-*/
-void xr_driver_next_page (struct xr_driver *, cairo_t *);
-void xr_driver_output_item (struct xr_driver *, const struct output_item *);
-bool xr_driver_need_new_page (const struct xr_driver *);
-bool xr_driver_is_page_blank (const struct xr_driver *);
-
-/* Render charts with Cairo. */
-
-#endif /* HAVE_CAIRO */
-
-#endif /* output/cairo.h */
#include "gl/xalloc.h"
+bool
+page_paragraph_equals (const struct page_paragraph *a,
+ const struct page_paragraph *b)
+{
+ return (!a || !b ? a == b
+ : !a->markup || !b->markup ? a->markup == b->markup
+ : !strcmp (a->markup, b->markup) && a->halign == b->halign);
+}
+
void
page_heading_copy (struct page_heading *dst, const struct page_heading *src)
{
free (ph->paragraphs);
}
+bool
+page_heading_equals (const struct page_heading *a,
+ const struct page_heading *b)
+{
+ if (!a || !b)
+ return a == b;
+
+ if (a->n != b->n)
+ return false;
+
+ for (size_t i = 0; i < a->n; i++)
+ if (!page_paragraph_equals (&a->paragraphs[i], &b->paragraphs[i]))
+ return false;
+
+ return true;
+}
+
struct page_setup *
page_setup_clone (const struct page_setup *old)
{
enum table_halign halign;
};
+bool page_paragraph_equals (const struct page_paragraph *,
+ const struct page_paragraph *);
+
struct page_heading
{
struct page_paragraph *paragraphs;
void page_heading_copy (struct page_heading *, const struct page_heading *);
void page_heading_uninit (struct page_heading *);
+bool page_heading_equals (const struct page_heading *,
+ const struct page_heading *);
struct page_setup
{
#include "libpspp/assertion.h"
#include "libpspp/string-map.h"
#include "output/cairo-fsm.h"
-#include "output/cairo.h"
+#include "output/cairo-pager.h"
#include "output/driver-provider.h"
#include "output/driver.h"
#include "output/chart-item.h"
/* Variables pertaining to printing */
GtkPrintSettings *print_settings;
- struct xr_driver *print_xrd;
+
+ struct xr_fsm_style *fsm_style;
+ struct xr_page_style *page_style;
+ struct xr_pager *pager;
int print_item;
int print_n_pages;
gboolean paginated;
GtkTreeModel *model;
view = xmalloc (sizeof *view);
- view->style = NULL;
- view->object_spacing = 10;
- view->output = output;
- view->render_width = 0;
- view->max_width = 0;
- view->y = 0;
- view->overview = overview;
- view->cur_group = NULL;
- view->toplevel = gtk_widget_get_toplevel (GTK_WIDGET (output));
- view->buttontime = 0;
- view->items = NULL;
- view->n_items = view->allocated_items = 0;
- view->selected_item = NULL;
- view->print_settings = NULL;
- view->print_xrd = NULL;
- view->print_item = 0;
- view->print_n_pages = 0;
- view->paginated = FALSE;
+ *view = (struct psppire_output_view) {
+ .object_spacing = 10,
+ .output = output,
+ .overview = overview,
+ .toplevel = gtk_widget_get_toplevel (GTK_WIDGET (output)),
+ };
g_signal_connect (output, "draw", G_CALLBACK (layout_draw_callback), NULL);
get_cairo_context_from_print_context (GtkPrintContext *context)
{
cairo_t *cr = gtk_print_context_get_cairo_context (context);
-
- /*
- For all platforms except windows, gtk_print_context_get_dpi_[xy] returns 72.
- Windows returns 600.
- */
- double xres = gtk_print_context_get_dpi_x (context);
- double yres = gtk_print_context_get_dpi_y (context);
-
- /* This means that the cairo context now has its dimensions in Points */
- cairo_scale (cr, xres / 72.0, yres / 72.0);
-
- return cr;
+ return cairo_reference (cr);
}
-
static void
create_xr_print_driver (GtkPrintContext *context, struct psppire_output_view *view)
{
- struct string_map options;
- GtkPageSetup *page_setup;
- double width, height;
- double left_margin;
- double right_margin;
- double top_margin;
- double bottom_margin;
-
- page_setup = gtk_print_context_get_page_setup (context);
- width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_MM);
- height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_MM);
- left_margin = gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_MM);
- right_margin = gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_MM);
- top_margin = gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_MM);
- bottom_margin = gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_MM);
+ GtkPageSetup *ps = gtk_print_context_get_page_setup (context);
- string_map_init (&options);
- string_map_insert_nocopy (&options, xstrdup ("paper-size"),
- c_xasprintf("%.2fx%.2fmm", width, height));
- string_map_insert_nocopy (&options, xstrdup ("left-margin"),
- c_xasprintf ("%.2fmm", left_margin));
- string_map_insert_nocopy (&options, xstrdup ("right-margin"),
- c_xasprintf ("%.2fmm", right_margin));
- string_map_insert_nocopy (&options, xstrdup ("top-margin"),
- c_xasprintf ("%.2fmm", top_margin));
- string_map_insert_nocopy (&options, xstrdup ("bottom-margin"),
- c_xasprintf ("%.2fmm", bottom_margin));
-
- view->print_xrd = xr_driver_create (get_cairo_context_from_print_context (context), &options);
-
- string_map_destroy (&options);
+ enum { H = TABLE_HORZ, V = TABLE_VERT };
+ int paper[TABLE_N_AXES] = {
+ [H] = gtk_page_setup_get_paper_width (ps, GTK_UNIT_POINTS) * XR_POINT,
+ [V] = gtk_page_setup_get_paper_height (ps, GTK_UNIT_POINTS) * XR_POINT,
+ };
+
+ /* These are all 1/2 inch. The "margins" that GTK+ gives us are useless:
+ they are the printer's imagable area. */
+ int margins[TABLE_N_AXES][2] = {
+ [H][0] = XR_POINT * 36,
+ [H][1] = XR_POINT * 36,
+ [V][0] = XR_POINT * 36,
+ [V][1] = XR_POINT * 36,
+ };
+
+ double size[TABLE_N_AXES];
+ for (int a = 0; a < TABLE_N_AXES; a++)
+ size[a] = paper[a] - margins[a][0] - margins[a][1];
+
+ PangoFontDescription *proportional_font
+ = pango_font_description_from_string ("Sans Serif 10");
+ PangoFontDescription *fixed_font
+ = pango_font_description_from_string ("Monospace 10");
+
+ view->page_style = xmalloc (sizeof *view->page_style);
+ *view->page_style = (struct xr_page_style) {
+ .ref_cnt = 1,
+
+ .margins = {
+ [H] = { margins[H][0], margins[H][1] },
+ [V] = { margins[V][0], margins[V][1] },
+ },
+ .bg = { .alpha = 0 },
+ .initial_page_number = 1,
+ .object_spacing = 12 * XR_POINT,
+ };
+
+ view->fsm_style = xmalloc (sizeof *view->fsm_style);
+ *view->fsm_style = (struct xr_fsm_style) {
+ .ref_cnt = 1,
+
+ .size = { [H] = size[H], [V] = size[V] },
+ .min_break = { [H] = size[H] / 2, [V] = size[V] / 2 },
+ .fonts = {
+ [XR_FONT_PROPORTIONAL] = proportional_font,
+ [XR_FONT_FIXED] = fixed_font,
+ },
+ .fg = CELL_COLOR_BLACK,
+ .use_system_colors = false,
+ .transparent = false,
+ .font_resolution = 72.0
+ };
+
+ view->pager = xr_pager_create (view->page_style, view->fsm_style);
}
static gboolean
}
else if (view->print_item < view->n_items)
{
- xr_driver_output_item (view->print_xrd,
- view->items[view->print_item++].item);
- while (xr_driver_need_new_page (view->print_xrd))
+ xr_pager_add_item (view->pager, view->items[view->print_item++].item);
+ while (xr_pager_needs_new_page (view->pager))
{
- xr_driver_next_page (view->print_xrd, get_cairo_context_from_print_context (context));
+ xr_pager_add_page (view->pager,
+ get_cairo_context_from_print_context (context));
view->print_n_pages ++;
}
return FALSE;
}
else
{
- gtk_print_operation_set_n_pages (operation, view->print_n_pages);
+ gtk_print_operation_set_n_pages (operation, MAX (1, view->print_n_pages));
/* Re-create the driver to do the real printing. */
- xr_driver_destroy (view->print_xrd);
- create_xr_print_driver (context, view);
+ xr_pager_destroy (view->pager);
+ view->pager = xr_pager_create (view->page_style, view->fsm_style);
view->print_item = 0;
view->paginated = TRUE;
create_xr_print_driver (context, view);
view->print_item = 0;
- view->print_n_pages = 1;
+ view->print_n_pages = 0;
view->paginated = FALSE;
}
GtkPrintContext *context,
struct psppire_output_view *view)
{
- xr_driver_destroy (view->print_xrd);
+ xr_pager_destroy (view->pager);
+ view->pager = NULL;
}
gint page_number,
struct psppire_output_view *view)
{
- xr_driver_next_page (view->print_xrd, get_cairo_context_from_print_context (context));
- while (!xr_driver_need_new_page (view->print_xrd)
+ xr_pager_add_page (view->pager,
+ get_cairo_context_from_print_context (context));
+ while (!xr_pager_needs_new_page (view->pager)
&& view->print_item < view->n_items)
- xr_driver_output_item (view->print_xrd, view->items [view->print_item++].item);
+ xr_pager_add_item (view->pager, view->items [view->print_item++].item);
}
if (view->print_settings != NULL)
gtk_print_operation_set_print_settings (print, view->print_settings);
+ gtk_print_operation_set_use_full_page (print, TRUE);
+ gtk_print_operation_set_unit (print, GTK_UNIT_POINTS);
+
g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), view);
g_signal_connect (print, "end_print", G_CALLBACK (end_print), view);
g_signal_connect (print, "paginate", G_CALLBACK (paginate), view);