From e6b6e7d67f173867d731ebca6b8fbad5a2f82560 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Fri, 11 Dec 2020 23:20:56 -0800 Subject: [PATCH] cairo-pager: New. --- src/output/automake.mk | 3 +- src/output/cairo-chart.c | 1 - src/output/cairo-fsm.c | 44 ++- src/output/cairo-fsm.h | 8 +- src/output/cairo-pager.c | 369 ++++++++++++++++++++ src/output/cairo-pager.h | 71 ++++ src/output/cairo.c | 576 +++++++------------------------ src/output/cairo.h | 78 ----- src/output/page-setup-item.c | 26 ++ src/output/page-setup-item.h | 5 + src/ui/gui/psppire-output-view.c | 159 +++++---- 11 files changed, 713 insertions(+), 627 deletions(-) create mode 100644 src/output/cairo-pager.c create mode 100644 src/output/cairo-pager.h delete mode 100644 src/output/cairo.h diff --git a/src/output/automake.mk b/src/output/automake.mk index d524bc8f60..e267089c7d 100644 --- a/src/output/automake.mk +++ b/src/output/automake.mk @@ -95,8 +95,9 @@ src_output_liboutput_la_SOURCES += \ 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 \ diff --git a/src/output/cairo-chart.c b/src/output/cairo-chart.c index 809da37c2a..62459d6f57 100644 --- a/src/output/cairo-chart.c +++ b/src/output/cairo-chart.c @@ -34,7 +34,6 @@ #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" diff --git a/src/output/cairo-fsm.c b/src/output/cairo-fsm.c index a9e4d4c68d..ec7e727880 100644 --- a/src/output/cairo-fsm.c +++ b/src/output/cairo-fsm.c @@ -52,7 +52,7 @@ /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */ #define H TABLE_HORZ #define V TABLE_VERT - + struct xr_fsm_style * xr_fsm_style_ref (const struct xr_fsm_style *style_) { @@ -63,6 +63,24 @@ 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) { @@ -618,6 +636,7 @@ xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell, 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; @@ -1097,7 +1116,6 @@ xr_fsm_create (const struct output_item *item_, .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, @@ -1118,16 +1136,17 @@ xr_fsm_create (const struct output_item *item_, 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)); @@ -1149,7 +1168,6 @@ xr_fsm_destroy (struct xr_fsm *fsm) } } - /* This is primarily meant for use with screen rendering since the result is a fixed value for charts. */ void @@ -1181,17 +1199,9 @@ xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp) 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 diff --git a/src/output/cairo-fsm.h b/src/output/cairo-fsm.h index 74b43860c8..37f4b8b40b 100644 --- a/src/output/cairo-fsm.h +++ b/src/output/cairo-fsm.h @@ -28,6 +28,10 @@ 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, @@ -39,9 +43,10 @@ struct xr_fsm_style { 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; @@ -53,6 +58,7 @@ struct xr_fsm_style 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 *); diff --git a/src/output/cairo-pager.c b/src/output/cairo-pager.c new file mode 100644 index 0000000000..36823bd945 --- /dev/null +++ b/src/output/cairo-pager.c @@ -0,0 +1,369 @@ +/* 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 . */ + +#include + +#include "output/cairo-pager.h" + +#include +#include +#include + +#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 + +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); +} + +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; + } + } + } +} diff --git a/src/output/cairo-pager.h b/src/output/cairo-pager.h new file mode 100644 index 0000000000..ab843ee30e --- /dev/null +++ b/src/output/cairo-pager.h @@ -0,0 +1,71 @@ +/* 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 . */ + +#ifndef OUTPUT_CAIRO_PAGER_H +#define OUTPUT_CAIRO_PAGER_H 1 + +#include + +#ifdef HAVE_CAIRO + +/* Cairo output driver paginater. */ + +#include +#include +#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 */ diff --git a/src/output/cairo.c b/src/output/cairo.c index c3cef5c62a..4ee386d25f 100644 --- a/src/output/cairo.c +++ b/src/output/cairo.c @@ -16,31 +16,17 @@ #include -#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 #include @@ -50,15 +36,9 @@ #include #include #include -#include -#include -#include #include -#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" @@ -79,10 +59,6 @@ xr_to_pt (int x) 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 { @@ -96,39 +72,14 @@ struct xr_driver { 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 *); /* Output driver basics. */ @@ -193,253 +144,90 @@ parse_font_option (struct output_driver *d, struct string_map *options, 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 * @@ -449,18 +237,16 @@ xr_create (struct file_handle *fh, enum settings_output_devices device_type, 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 (); @@ -472,9 +258,6 @@ xr_create (struct file_handle *fh, enum settings_output_devices device_type, goto error; } - if (!xr_check_fonts (xr->surface, xr->fonts, xr->size[H], xr->size[V])) - goto error; - fh_unref (fh); return &xr->driver; @@ -509,28 +292,21 @@ static void 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); } @@ -539,7 +315,8 @@ xr_flush (struct output_driver *driver) { 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 @@ -548,45 +325,49 @@ xr_update_page_setup (struct output_driver *driver, { 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 @@ -596,116 +377,23 @@ xr_submit (struct output_driver *driver, const struct output_item *output_item) 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); - } -} - -/* 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)); } } @@ -723,23 +411,3 @@ static const struct output_driver_class cairo_driver_class = xr_submit, xr_flush, }; - -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); - } -} diff --git a/src/output/cairo.h b/src/output/cairo.h deleted file mode 100644 index 50bbc372fe..0000000000 --- a/src/output/cairo.h +++ /dev/null @@ -1,78 +0,0 @@ -/* 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 . */ - -#ifndef OUTPUT_CAIRO_H -#define OUTPUT_CAIRO_H 1 - -#include - -#ifdef HAVE_CAIRO - -#include - -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 */ diff --git a/src/output/page-setup-item.c b/src/output/page-setup-item.c index b4f8e4c4ca..45cf8c977f 100644 --- a/src/output/page-setup-item.c +++ b/src/output/page-setup-item.c @@ -25,6 +25,15 @@ #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) { @@ -48,6 +57,23 @@ page_heading_uninit (struct page_heading *ph) 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) { diff --git a/src/output/page-setup-item.h b/src/output/page-setup-item.h index 6fbe514797..f311ad2d5a 100644 --- a/src/output/page-setup-item.h +++ b/src/output/page-setup-item.h @@ -46,6 +46,9 @@ struct page_paragraph enum table_halign halign; }; +bool page_paragraph_equals (const struct page_paragraph *, + const struct page_paragraph *); + struct page_heading { struct page_paragraph *paragraphs; @@ -54,6 +57,8 @@ struct page_heading 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 { diff --git a/src/ui/gui/psppire-output-view.c b/src/ui/gui/psppire-output-view.c index 626d52e07e..f48b1ade4f 100644 --- a/src/ui/gui/psppire-output-view.c +++ b/src/ui/gui/psppire-output-view.c @@ -28,7 +28,7 @@ #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" @@ -76,7 +76,10 @@ struct psppire_output_view /* 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; @@ -860,24 +863,12 @@ psppire_output_view_new (GtkLayout *output, GtkTreeView *overview) 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); @@ -989,55 +980,68 @@ static cairo_t * 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 @@ -1053,22 +1057,22 @@ paginate (GtkPrintOperation *operation, } 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; @@ -1084,7 +1088,7 @@ begin_print (GtkPrintOperation *operation, create_xr_print_driver (context, view); view->print_item = 0; - view->print_n_pages = 1; + view->print_n_pages = 0; view->paginated = FALSE; } @@ -1093,7 +1097,8 @@ end_print (GtkPrintOperation *operation, GtkPrintContext *context, struct psppire_output_view *view) { - xr_driver_destroy (view->print_xrd); + xr_pager_destroy (view->pager); + view->pager = NULL; } @@ -1103,10 +1108,11 @@ draw_page (GtkPrintOperation *operation, 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); } @@ -1121,6 +1127,9 @@ psppire_output_view_print (struct psppire_output_view *view, 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); -- 2.30.2