font resolution works!
[pspp] / src / output / cairo-pager.c
index 224ad0381f2f1ae4fda152f69b59657d5c90c6d7..36823bd9452344168722b6b7b5c4a0d81823aa1f 100644 (file)
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
+
+/* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
+#define H TABLE_HORZ
+#define V TABLE_VERT
 \f
 struct xr_page_style *
 xr_page_style_ref (const struct xr_page_style *ps_)
@@ -38,6 +42,23 @@ xr_page_style_ref (const struct xr_page_style *ps_)
   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)
 {
@@ -48,53 +69,37 @@ xr_page_style_unref (struct xr_page_style *ps)
         {
           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])
+    for (int j = 0; j < 2; j++)
+      if (a->margins[i][j] != b->margins[i][j])
         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
+  return (a->initial_page_number == b->initial_page_number
           && a->object_spacing == b->object_spacing);
 }
 \f
 struct xr_pager
   {
     struct xr_page_style *page_style;
+    struct xr_fsm_style *fsm_style;
     int page_index;
     int heading_heights[2];
 
     /* Current output item. */
     struct xr_fsm *fsm;
-    struct xr_fsm_style *fsm_style;
     struct output_item *item;
 
     /* Current output page. */
@@ -138,9 +143,13 @@ get_layout_height (PangoLayout *layout)
 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)
+                        int width, int base_y, double font_resolution)
 {
-  PangoLayout *layout = pango_cairo_create_layout (cairo);
+  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;
@@ -160,13 +169,11 @@ xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
          : 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);
-        }
+
+      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));
     }
@@ -177,40 +184,48 @@ xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
 }
 
 static void
-xr_measure_headings (struct xr_pager *p)
+xr_measure_headings (const struct xr_page_style *ps,
+                     const struct xr_fsm_style *fs,
+                     int heading_heights[2])
 {
-  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;
+      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);
-
-  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)
+xr_pager_create (const struct xr_page_style *ps_,
+                 const struct xr_fsm_style *fs_)
 {
-  struct xr_pager *p = xmalloc (sizeof *p);
-  *p = (struct xr_pager) { .page_style = xr_page_style_ref (ps) };
+  struct xr_page_style *ps = xr_page_style_ref (ps_);
+  struct xr_fsm_style *fs = xr_fsm_style_ref (fs_);
 
-  xr_measure_headings (p);
+  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;
 }
 
@@ -219,13 +234,17 @@ xr_pager_destroy (struct xr_pager *p)
 {
   if (p)
     {
-      xr_fsm_destroy (p->fsm);
+      xr_page_style_unref (p->page_style);
       xr_fsm_style_unref (p->fsm_style);
+
+      xr_fsm_destroy (p->fsm);
       output_item_unref (p->item);
 
-      xr_page_style_unref (p->page_style);
       if (p->cr)
-        cairo_destroy (p->cr);
+        {
+          cairo_restore (p->cr);
+          cairo_destroy (p->cr);
+        }
       free (p);
     }
 }
@@ -237,12 +256,10 @@ xr_pager_has_item (const struct xr_pager *p)
 }
 
 void
-xr_pager_add_item (struct xr_pager *p, const struct xr_fsm_style *fsm_style,
-                   const struct output_item *item)
+xr_pager_add_item (struct xr_pager *p, 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);
 }
 
@@ -256,9 +273,11 @@ 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)
@@ -266,26 +285,25 @@ xr_pager_add_page (struct xr_pager *p, cairo_t *cr)
       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_rectangle (cr, 0, 0, fs->size[H], fs->size[V]);
       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]));
+                   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, ps->font, &ps->headings[0], page_number,
-                            ps->size[TABLE_HORZ], true, 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, 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_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);
 }
@@ -293,12 +311,14 @@ xr_pager_add_page (struct xr_pager *p, cairo_t *cr)
 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)
+  if (p->item && (!p->cr || p->y >= p->fsm_style->size[V]))
     {
-      cairo_destroy (p->cr);
-      p->cr = NULL;
+      if (p->cr)
+        {
+          cairo_restore (p->cr);
+          cairo_destroy (p->cr);
+          p->cr = NULL;
+        }
       return true;
     }
   else
@@ -308,18 +328,13 @@ xr_pager_needs_new_page (struct xr_pager *p)
 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->item && p->cr && p->y < p->fsm_style->size[V])
     {
       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;
 
@@ -330,7 +345,8 @@ xr_pager_run (struct xr_pager *p)
       for (;;)
         {
           int spacing = p->page_style->object_spacing;
-          int chunk = xr_fsm_draw_slice (p->fsm, p->cr, vsize - p->y);
+          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));
 
@@ -338,8 +354,6 @@ xr_pager_run (struct xr_pager *p)
             {
               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;