cairo output works again
authorBen Pfaff <blp@cs.stanford.edu>
Sat, 12 Dec 2020 07:20:56 +0000 (23:20 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 13 Dec 2020 19:25:12 +0000 (11:25 -0800)
src/output/automake.mk
src/output/cairo-fsm.c
src/output/cairo-fsm.h
src/output/cairo-pager.c [new file with mode: 0644]
src/output/cairo-pager.h [new file with mode: 0644]
src/output/cairo.c
src/output/page-setup-item.c
src/output/page-setup-item.h
src/ui/gui/psppire-output-view.c
tests/output/render-test.c

index d524bc8f60e083a66615f0d9f6c9f3ecf0e4861c..61d804372fef133182cbbe965db0110f1bc9df16 100644 (file)
@@ -95,6 +95,8 @@ 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 \
index 73231dba4e0e73addf1650194812cd5ed8e961a2..5d1b63e4f1937780333bbcae6e9e1a538d2e115e 100644 (file)
@@ -1118,10 +1118,10 @@ xr_fsm_create (const struct output_item *item_,
 
       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));
@@ -1143,7 +1143,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
@@ -1175,17 +1174,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
index 60a6efb489166241c1d52a4966166667395f8d6d..f89acbe5372a9e65557970c5bfa6da158a47efd7 100644 (file)
 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,
@@ -42,6 +46,7 @@ struct xr_fsm_style
     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_scale;
diff --git a/src/output/cairo-pager.c b/src/output/cairo-pager.c
new file mode 100644 (file)
index 0000000..224ad03
--- /dev/null
@@ -0,0 +1,355 @@
+/* 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)
+\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;
+}
+
+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]);
+          if (ps->font)
+            pango_font_description_free (ps->font);
+          free (ps);
+        }
+    }
+}
+
+static bool
+pfd_equals (const PangoFontDescription *a,
+            const PangoFontDescription *b)
+{
+  return a && b ? pango_font_description_equal (a, b) : !a && !b;
+}
+
+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++)
+    {
+      if (a->size[i] != b->size[i])
+        return false;
+
+      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 (pfd_equals (a->font, b->font)
+          && a->font_scale == b->font_scale
+          && a->initial_page_number == b->initial_page_number
+          && a->object_spacing == b->object_spacing);
+}
+\f
+struct xr_pager
+  {
+    struct xr_page_style *page_style;
+    int page_index;
+    int heading_heights[2];
+
+    /* Current output item. */
+    struct xr_fsm *fsm;
+    struct xr_fsm_style *fsm_style;
+    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, bool draw, int base_y)
+{
+  PangoLayout *layout = pango_cairo_create_layout (cairo);
+  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));
+      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));
+    }
+
+  g_object_unref (G_OBJECT (layout));
+
+  return y;
+}
+
+static void
+xr_measure_headings (struct xr_pager *p)
+{
+  const struct xr_page_style *ps = p->page_style;
+
+  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 = xr_render_page_heading (cairo, ps->font, &ps->headings[i], -1,
+                                      ps->size[TABLE_HORZ], false, 0);
+
+      /* If the heading is nonempty, add a margin above or below it. */
+      if (h)
+        h += ps->object_spacing;
+
+      p->heading_heights[i] = h;
+    }
+  cairo_destroy (cairo);
+  cairo_surface_destroy (surface);
+
+  int total = p->heading_heights[0] + p->heading_heights[1];
+  if (total >= ps->size[TABLE_VERT])
+    p->heading_heights[0] = p->heading_heights[1] = 0;
+}
+
+struct xr_pager *
+xr_pager_create (const struct xr_page_style *ps)
+{
+  struct xr_pager *p = xmalloc (sizeof *p);
+  *p = (struct xr_pager) { .page_style = xr_page_style_ref (ps) };
+
+  xr_measure_headings (p);
+
+  return p;
+}
+
+void
+xr_pager_destroy (struct xr_pager *p)
+{
+  if (p)
+    {
+      xr_fsm_destroy (p->fsm);
+      xr_fsm_style_unref (p->fsm_style);
+      output_item_unref (p->item);
+
+      xr_page_style_unref (p->page_style);
+      if (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 xr_fsm_style *fsm_style,
+                   const struct output_item *item)
+{
+  assert (!p->item);
+  p->item = output_item_ref (item);
+  p->fsm_style = xr_fsm_style_ref (fsm_style);
+  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);
+  p->cr = cr;
+  p->y = 0;
+
+  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, ps->size[TABLE_HORZ], ps->size[TABLE_VERT]);
+      cairo_fill (cr);
+      cairo_restore (cr);
+    }
+  cairo_translate (cr,
+                   xr_to_pt (ps->margins[TABLE_HORZ][0]),
+                   xr_to_pt (ps->margins[TABLE_VERT][0]));
+
+  int page_number = p->page_index++ + ps->initial_page_number;
+  if (p->heading_heights[0])
+    xr_render_page_heading (cr, ps->font, &ps->headings[0], page_number,
+                            ps->size[TABLE_HORZ], true, 0);
+
+  if (p->heading_heights[1])
+    xr_render_page_heading (
+      cr, ps->font, &ps->headings[1], page_number,
+      ps->size[TABLE_HORZ], true,
+      ps->size[TABLE_VERT] - p->heading_heights[1] + ps->object_spacing);
+
+  cairo_translate (cr, 0, xr_to_pt (p->heading_heights[0]));
+
+  xr_pager_run (p);
+}
+
+bool
+xr_pager_needs_new_page (struct xr_pager *p)
+{
+  int vsize = (p->page_style->size[TABLE_VERT]
+               - p->heading_heights[0] - p->heading_heights[1]);
+  if (p->item && p->y >= vsize)
+    {
+      cairo_destroy (p->cr);
+      p->cr = NULL;
+      return true;
+    }
+  else
+    return false;
+}
+
+static void
+xr_pager_run (struct xr_pager *p)
+{
+  int vsize = (p->page_style->size[TABLE_VERT]
+               - p->heading_heights[0] - p->heading_heights[1]);
+  if (p->item && p->cr && p->y < vsize)
+    {
+      if (!p->fsm)
+        {
+          /* XXX fsm_style needs to account for heading_heights */
+          p->fsm = xr_fsm_create (p->item, p->fsm_style, p->cr);
+          if (!p->fsm)
+            {
+              xr_fsm_style_unref (p->fsm_style);
+              p->fsm_style = NULL;
+              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, vsize - 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;
+              xr_fsm_style_unref (p->fsm_style);
+              p->fsm_style = 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 (file)
index 0000000..2e722be
--- /dev/null
@@ -0,0 +1,72 @@
+/* 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 size[TABLE_N_AXES];          /* Page size minus margins. */
+    int margins[TABLE_N_AXES][2];    /* Margins. */
+
+    struct page_heading headings[2]; /* Top and bottom headings. */
+    PangoFontDescription *font;
+
+    struct cell_color bg;            /* Background color. */
+    double font_scale;
+    int initial_page_number;
+    int object_spacing;
+  };
+struct xr_page_style *xr_page_style_ref (const 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, enum table_axis a)
+{
+  return ps->size[a] + ps->margins[a][0] + ps->margins[a][1];
+}
+
+struct xr_pager *xr_pager_create (const struct xr_page_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 xr_fsm_style *,
+                        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 */
index f5bf1f6a93de3ec3f1438e252d61d89636b1cdbf..16738897130a2c8d951faee4f951a2baef257042 100644 (file)
@@ -30,6 +30,7 @@
 #include "data/file-handle-def.h"
 #include "output/cairo-chart.h"
 #include "output/cairo-fsm.h"
+#include "output/cairo-pager.h"
 #include "output/chart-item-provider.h"
 #include "output/driver-provider.h"
 #include "output/group-item.h"
@@ -96,30 +97,11 @@ 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];
+    struct xr_fsm_style *fsm_style;
+    struct xr_page_style *page_style;
+    struct xr_pager *pager;
 
     /* Internal state. */
-    struct xr_fsm_style *style;
-    double font_scale;
-    int char_width, char_height;
-    cairo_t *cairo;
     cairo_surface_t *surface;
     int page_number;           /* Current page number. */
     int y;
@@ -128,8 +110,6 @@ struct xr_driver
 
 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. */
 
@@ -194,254 +174,100 @@ 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)
+/* FONT_SCALE is a nasty kluge for an issue that does not make sense.  On any
+   surface other than a screen (e.g. for output to PDF or PS or SVG), the fonts
+   are way too big by default.  A "9-point" font seems to appear about 16
+   points tall.  We use a scale factor for these surfaces to help, but the
+   underlying issue is a mystery. */
+static struct xr_driver *
+xr_allocate (const char *name, int device_type, struct string_map *o,
+             double font_scale)
 {
+  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,
-             double font_scale)
-{
-  struct xr_driver *xr = xzalloc (sizeof *xr);
-  struct output_driver *d = &xr->driver;
-
-  output_driver_init (d, &cairo_driver_class, name, device_type);
-
-  /* This is a nasty kluge for an issue that does not make sense.  On any
-     surface other than a screen (e.g. for output to PDF or PS or SVG), the
-     fonts are way too big by default.  A "9-point" font seems to appear about
-     16 points tall.  We use a scale factor for these surfaces to help, but the
-     underlying issue is a mystery. */
-  xr->font_scale = font_scale;
-
-  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++)
-    {
-      PangoLayout *layout = pango_cairo_create_layout (cairo);
-      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));
+    if (min_break[a] <= 0)
+      min_break[a] = size[a] / 2;
 
-      g_object_unref (G_OBJECT (layout));
-    }
-}
+  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);
 
-static int
-get_layout_height (PangoLayout *layout)
-{
-  int w, h;
-  pango_layout_get_size (layout, &w, &h);
-  return h;
-}
+  struct cell_color fg = parse_color (opt (d, o, "foreground-color", "black"));
 
-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)
-{
-  PangoLayout *layout = pango_cairo_create_layout (cairo);
-  pango_layout_set_font_description (layout, font);
+  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")));
 
-  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));
-    }
-
-  g_object_unref (G_OBJECT (layout));
+  bool systemcolors = parse_boolean (opt (d, o, "systemcolors", "false"));
 
-  return y;
-}
+  int object_spacing
+    = parse_dimension (opt (d, o, "object-spacing", NULL)) * scale;
+  if (object_spacing <= 0)
+    object_spacing = XR_POINT * 12;
 
-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);
+  xr->page_style = xmalloc (sizeof *xr->page_style);
+  *xr->page_style = (struct xr_page_style) {
+    .ref_cnt = 1,
 
-      /* If the top heading is nonempty, add some space below it. */
-      if (h && i == 0)
-        h += object_spacing;
+    .size = { [H] = size[H], [V] = size[V] },
+    .margins = {
+      [H] = { margins[H][0], margins[H][1], },
+      [V] = { margins[V][0], margins[V][1], },
+    },
 
-      if (height)
-        height[i] = h;
-      total += h;
-    }
-  cairo_destroy (cairo);
-  return total;
-}
+    .font = pango_font_description_copy (proportional_font),
 
-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,
+    .font_scale = font_scale,
+    .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_scale = xr->font_scale,
-      };
-
-      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_scale = font_scale,
+  };
 
-  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 *
@@ -451,18 +277,15 @@ 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, 72.0 / 128.0);
 
-  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, 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 ();
 
@@ -474,9 +297,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;
 
@@ -511,28 +331,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);
 }
 
@@ -541,7 +354,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
@@ -550,45 +364,38 @@ 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];
-    }
+  struct xr_page_style *page_style = xmalloc (sizeof *page_style);
+  *page_style = (struct xr_page_style) {
+    .ref_cnt = 1,
+
+    .size = {
+      [H] = (ps->paper[h] - ps->margins[h][0] - ps->margins[h][1]) * scale,
+      [V] = (ps->paper[v] - ps->margins[v][0] - ps->margins[v][1]) * scale,
+    },
+    .margins = {
+      [H] = { ps->margins[h][0] * scale, ps->margins[h][1] * scale },
+      [V] = { ps->margins[v][0] * scale, ps->margins[v][1] * scale },
+    },
+
+    .font = pango_font_description_copy (xr->page_style->font),
+
+    .bg = xr->page_style->bg,
+    .font_scale = xr->page_style->font_scale,
+    .initial_page_number = ps->initial_page_number,
+    .object_spacing = ps->object_spacing * 72 * XR_POINT,
+  };
 
-  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);
+  for (size_t i = 0; i < 2; i++)
+    page_heading_copy (&page_style->headings[i], &ps->headings[i]);
+
+  xr_page_style_unref (xr->page_style);
+  xr->page_style = page_style;
 }
 
 static void
@@ -603,111 +410,17 @@ xr_submit (struct output_driver *driver, const struct output_item *output_item)
       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_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, xr->fsm_style, 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
@@ -725,23 +438,3 @@ static const struct output_driver_class cairo_driver_class =
   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, 1.0);
-  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);
-    }
-}
index b4f8e4c4caead1e887c2bf891f30e4dcf2a04bd7..45cf8c977f0d8c48456e67c6ebb9c678e2da35a6 100644 (file)
 
 #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)
 {
index 6fbe514797fd12cf61f638ec65a0c3945daa378c..f311ad2d5af94c9a43ab095b83da06785b921dee 100644 (file)
@@ -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
   {
index ccf23e8c4ca9b222bc2d9f866a1a45850d546091..38dbf1a060a19fcd1612ca3f2256b96845472a68 100644 (file)
@@ -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;
@@ -865,24 +868,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);
 
@@ -1012,37 +1003,62 @@ get_cairo_context_from_print_context (GtkPrintContext *context)
 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,
+  };
+
+  int margins[TABLE_N_AXES][2] = {
+    [H][0] = gtk_page_setup_get_left_margin (ps, GTK_UNIT_POINTS) * XR_POINT,
+    [H][1] = gtk_page_setup_get_right_margin (ps, GTK_UNIT_POINTS) * XR_POINT,
+    [V][0] = gtk_page_setup_get_top_margin (ps, GTK_UNIT_POINTS) * XR_POINT,
+    [V][1] = gtk_page_setup_get_bottom_margin (ps, GTK_UNIT_POINTS) * XR_POINT,
+  };
+
+  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,
+
+    .size = { [H] = size[H], [V] = size[V] },
+    .margins = {
+      [H] = { margins[H][0], margins[H][1] },
+      [V] = { margins[V][0], margins[V][1] },
+    },
+    .font = pango_font_description_copy (proportional_font),
+    .bg = { .alpha = 0 },
+    .font_scale = 72.0 / 128.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] },
+    .fonts = {
+      [XR_FONT_PROPORTIONAL] = proportional_font,
+      [XR_FONT_FIXED] = fixed_font,
+    },
+    .fg = CELL_COLOR_BLACK,
+    .use_system_colors = false,
+    .transparent = false,
+    .font_scale = 72.0 / 128.0
+  };
+
+  view->pager = xr_pager_create (view->page_style);
 }
 
 static gboolean
@@ -1058,11 +1074,12 @@ 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->fsm_style,
+                         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;
@@ -1072,8 +1089,8 @@ paginate (GtkPrintOperation *operation,
       gtk_print_operation_set_n_pages (operation, 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->print_item = 0;
       view->paginated = TRUE;
 
@@ -1098,7 +1115,7 @@ end_print (GtkPrintOperation *operation,
           GtkPrintContext   *context,
           struct psppire_output_view *view)
 {
-  xr_driver_destroy (view->print_xrd);
+  xr_pager_destroy (view->pager);
 }
 
 
@@ -1108,10 +1125,12 @@ 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->fsm_style,
+                       view->items [view->print_item++].item);
 }
 
 
index 6b0aa73c4a049a0593acaa3e610905ead457d206..8f95d3c2f68d0d185554af4aa7e0b6358470b004 100644 (file)
@@ -54,7 +54,7 @@ static int render_txt = true;
 static int render_stdout = true;
 
 /* --pdf: Also render PDF output. */
-static int render_pdf;
+static int render_pdf = true;
 
 /* --csv: Also render CSV output. */
 static int render_csv;