work on monolithic output and outer borders
[pspp] / src / output / render.c
index 9e469a5e26182cd7ec2dd5bad89c645cc8db3d01..12c8f2c4949fc3193804f99e83669e4e63694e5f 100644 (file)
 #define H TABLE_HORZ
 #define V TABLE_VERT
 \f
+/* Maps a contiguous range of cells from a page to the underlying table along
+   the horizontal or vertical dimension. */
+struct map
+  {
+    int p[2];                   /* Range of ordinates in the page. */
+    int t[2];                   /* Range of ordinates in the table. */
+    int ofs;                    /* p[0] - t[0]. */
+    int n;                      /* p[1] - p[0] == t[1] - t[0]. */
+  };
+
 /* A layout for rendering a specific table on a specific device.
 
    May represent the layout of an entire table presented to
@@ -67,9 +77,11 @@ struct render_page
        n[H] = h[H][0] + (r[H][1] - r[H][0]) + h[H][1]
        n[V] = h[V][0] + (r[V][1] - r[V][0]) + h[V][1]
     */
+    int n[TABLE_N_AXES];
     int h[TABLE_N_AXES][2];
     int r[TABLE_N_AXES][2];
-    int n[TABLE_N_AXES];
+
+    struct map maps[TABLE_N_AXES][3];
 
     /* "Cell positions".
 
@@ -499,29 +511,53 @@ measure_rule (const struct render_params *params, const struct table *table,
   return width;
 }
 
+static struct map
+make_map (int p0, int t0, int n)
+{
+  return (struct map) {
+    .p = { p0, p0 + n },
+    .t = { t0, t0 + n },
+    .ofs = p0 - t0,
+    .n = n,
+  };
+}
+
 /* Allocates and returns a new render_page using PARAMS and TABLE.  Allocates
-   space for rendering a table with dimensions given in N.  The caller must
-   initialize most of the members itself. */
+   space for rendering a table with dimensions given in N, headers in H, and
+   content in R.  The caller must initialize most of the members itself. */
 static struct render_page *
 render_page_allocate__ (const struct render_params *params,
-                        struct table *table, int n[TABLE_N_AXES])
+                        struct table *table,
+                        const int n[TABLE_N_AXES],
+                        const int h[TABLE_N_AXES][2],
+                        const int r[TABLE_N_AXES][2])
 {
   struct render_page *page = xmalloc (sizeof *page);
-  page->params = params;
-  page->table = table;
-  page->ref_cnt = 1;
-  page->n[H] = n[H];
-  page->n[V] = n[V];
+  *page = (struct render_page) {
+    .params = params,
+    .table = table,
+    .ref_cnt = 1,
+    .n = { [H] = n[H], [V] = n[V] },
+    .h = { [H] = { h[H][0], h[H][1] }, [V] = { h[V][0], h[V][1] } },
+    .r = { [H] = { r[H][0], r[H][1] }, [V] = { r[V][0], r[V][1] } },
+    .cp = { [H] = xcalloc (2 * n[H] + 2, sizeof *page->cp[H]),
+            [V] = xcalloc (2 * n[V] + 2, sizeof *page->cp[V]) },
+    .join_crossing = {
+      [H] = xcalloc (n[H] + 1, sizeof *page->join_crossing[H]),
+      [V] = xcalloc (n[V] + 1, sizeof *page->join_crossing[V])
+    },
+    .overflows = HMAP_INITIALIZER (page->overflows),
+  };
 
-  for (int i = 0; i < TABLE_N_AXES; i++)
+  for (enum table_axis a = 0; a < TABLE_N_AXES; a++)
     {
-      page->cp[i] = xcalloc ((2 * n[i] + 2) , sizeof *page->cp[i]);
-      page->join_crossing[i] = xcalloc ((n[i] + 1) , sizeof *page->join_crossing[i]);
+      struct map *m = page->maps[a];
+      if (h[a][0])
+        *m++ = make_map (0, 0, page->h[a][0]);
+      *m++ = make_map (h[a][0], r[a][0], r[a][1] - r[a][0]);
+      if (h[a][1])
+        *m++ = make_map (n[a] - h[a][1], table->n[a] - table->h[a][1], h[a][1]);
     }
-
-  hmap_init (&page->overflows);
-  memset (page->is_edge_cutoff, 0, sizeof page->is_edge_cutoff);
-
   return page;
 }
 
@@ -531,15 +567,16 @@ render_page_allocate__ (const struct render_params *params,
 static struct render_page *
 render_page_allocate (const struct render_params *params, struct table *table)
 {
-  struct render_page *page = render_page_allocate__ (params, table, table->n);
+  int h[TABLE_N_AXES][2];
+  int r[TABLE_N_AXES][2];
   for (enum table_axis a = 0; a < TABLE_N_AXES; a++)
     {
-      page->h[a][0] = table->h[a][0];
-      page->h[a][1] = table->h[a][1];
-      page->r[a][0] = table->h[a][0];
-      page->r[a][1] = table->n[a] - table->h[a][1];
+      h[a][0] = table->h[a][0];
+      h[a][1] = table->h[a][1];
+      r[a][0] = table->h[a][0];
+      r[a][1] = table->n[a] - table->h[a][1];
     }
-  return page;
+  return render_page_allocate__ (params, table, table->n, h, r);
 }
 
 /* Allocates and returns a new render_page for PARAMS and TABLE, initializing
@@ -608,39 +645,21 @@ set_join_crossings (struct render_page *page, enum table_axis axis,
     page->join_crossing[axis][z] = rules[z];
 }
 
-/* Maps a contiguous range of cells from a page to the underlying table along
-   the horizpntal or vertical dimension. */
-struct map
-  {
-    int p0;                     /* First ordinate in the page. */
-    int t0;                     /* First ordinate in the table. */
-    int n;                      /* Number of ordinates in page and table. */
-  };
-
-/* Initializes M to a mapping from PAGE to PAGE->table along axis A.  The
-   mapping includes ordinate Z (in PAGE). */
-static void
-get_map (const struct render_page *page, enum table_axis a, int z,
-         struct map *m)
+/* Returns a mapping from page to table ordinates along axis A that includes
+   ordinate Z (in PAGE). */
+static const struct map *
+get_map (const struct render_page *page, enum table_axis a, int z)
 {
-  if (z < page->h[a][0])
-    {
-      m->p0 = 0;
-      m->t0 = 0;
-      m->n = page->h[a][0];
-    }
-  else if (z < page->n[a] - page->h[a][1])
+  for (size_t i = 0; i < 3; i++)
     {
-      m->p0 = page->h[a][0];
-      m->t0 = page->r[a][0];
-      m->n = page->r[a][1] - page->r[a][0];
-    }
-  else
-    {
-      m->p0 = page->n[a] - page->h[a][1];
-      m->t0 = page->table->n[a] - page->table->h[a][1];
-      m->n = page->h[a][1];
+      const struct map *m = &page->maps[a][i];
+      if (z < m->p[1])
+        {
+          assert (z >= m->p[0]);
+          return m;
+        }
     }
+  NOT_REACHED ();
 }
 
 /* Initializes CELL with the contents of the table cell at column X and row Y
@@ -655,25 +674,35 @@ static void
 render_get_cell (const struct render_page *page, int x, int y,
                  struct table_cell *cell)
 {
-  int d[TABLE_N_AXES] = { [H] = x, [V] = y };
-  struct map map[TABLE_N_AXES];
+  const struct map *map[TABLE_N_AXES] = {
+    [H] = get_map (page, H, x),
+    [V] = get_map (page, V, y),
+  };
 
-  for (enum table_axis a = 0; a < TABLE_N_AXES; a++)
-    {
-      struct map *m = &map[a];
-      get_map (page, a, d[a], m);
-      d[a] += m->t0 - m->p0;
-    }
-  table_get_cell (page->table, d[H], d[V], cell);
+  /* Get CELL, translating X and Y into table coordinates.  CELL is also
+     returned in table coordinates. */
+  table_get_cell (page->table, x + map[H]->ofs, y + map[V]->ofs, cell);
 
+  /* Translate CELL into page coordinates. */
   for (enum table_axis a = 0; a < TABLE_N_AXES; a++)
     {
-      struct map *m = &map[a];
+      const struct map *m = map[a];
 
-      for (int i = 0; i < 2; i++)
-        cell->d[a][i] -= m->t0 - m->p0;
-      cell->d[a][0] = MAX (cell->d[a][0], m->p0);
-      cell->d[a][1] = MIN (cell->d[a][1], m->p0 + m->n);
+#if 0
+      if (a == H && cell->options & TABLE_CELL_FULL_WIDTH)
+        {
+          printf ("special case\n");
+          cell->d[a][0] -= m->ofs;
+          cell->d[a][1] -= get_map (page, H, cell->d[a][1] - 1)->ofs;
+        }
+      else
+#endif
+        {
+          for (int i = 0; i < 2; i++)
+            cell->d[a][i] -= m->ofs;
+          cell->d[a][0] = MAX (cell->d[a][0], m->p[0]);
+          cell->d[a][1] = MIN (cell->d[a][1], m->p[1]);
+        }
     }
 }
 
@@ -774,6 +803,7 @@ render_page_create (const struct render_params *params, struct table *table,
       /* Fits even with maximum widths.  Use them. */
       page = create_page_with_exact_widths (params, table, columns[MAX],
                                             rules[H]);
+      //page->h[H][0] = page->h[H][1] = 0;
     }
   else if (table_widths[MIN] <= params->size[H])
     {
@@ -781,6 +811,7 @@ render_page_create (const struct render_params *params, struct table *table,
       page = create_page_with_interpolated_widths (
         params, table, columns[MIN], columns[MAX],
         table_widths[MIN], table_widths[MAX], rules[H]);
+      //page->h[H][0] = page->h[H][1] = 0;
     }
   else
     {
@@ -945,9 +976,7 @@ get_rule (const struct render_page *page, enum table_axis axis,
              - (page->n[a] - page->h[a][1]));
 
   enum table_axis b = !axis;
-  struct map m;
-  get_map (page, b, d[b], &m);
-  d[b] += m.t0 - m.p0;
+  d[b] += get_map (page, b, d[b])->ofs;
 
   struct table_border_style border
     = table_get_rule (page->table, axis, d[H], d[V]);
@@ -1117,19 +1146,55 @@ static void
 render_page_draw_cells (const struct render_page *page,
                         int ofs[TABLE_N_AXES], int bb[TABLE_N_AXES][2])
 {
+  /* OK, the problem is the headers.... the left-side headers shouldn't apply
+     to the title and caption and so on */
+  printf ("render page %d,%d - %d,%d\n",
+          bb[H][0], bb[V][0],
+          bb[H][1], bb[V][1]);
   for (int y = bb[V][0]; y < bb[V][1]; y++)
-    for (int x = bb[H][0]; x < bb[H][1];)
-      if (!is_rule (x) && !is_rule (y))
-        {
-          struct table_cell cell;
+    if (!is_rule (y))
+      for (int x = bb[H][0]; x < bb[H][1]; x++)
+        if (!is_rule (x))
+          {
+            struct table_cell cell;
+
+            render_get_cell (page, x / 2, y / 2, &cell);
+            char *s = pivot_value_to_string (cell.value, NULL);
+            printf ("%d,%d - %d,%d: %s\n",
+                    cell.d[H][0], cell.d[V][0],
+                    cell.d[H][1], cell.d[V][1],
+                    s);
+            free (s);
+          }
 
-          render_get_cell (page, x / 2, y / 2, &cell);
-          if (y / 2 == bb[V][0] / 2 || y / 2 == cell.d[V][0])
-            render_cell (page, ofs, &cell);
-          x = rule_ofs (cell.d[H][1]);
-        }
-      else
-        x++;
+  for (int y = bb[V][0]; y < bb[V][1]; y++)
+    if (!is_rule (y))
+      {
+        for (int x = bb[H][0]; x < bb[H][1];)
+          if (!is_rule (x))
+            {
+              struct table_cell cell;
+
+              render_get_cell (page, x / 2, y / 2, &cell);
+              if (y / 2 == bb[V][0] / 2 || y / 2 == cell.d[V][0])
+                {
+                  if (cell.d[H][1] - cell.d[H][0] != 1 ||
+                      cell.d[V][1] - cell.d[V][0] != 1)
+                    {
+                      char *s = pivot_value_to_string (cell.value, NULL); 
+                      printf ("rendering %d,%d - %d,%d (%s)\n",
+                              cell.d[H][0], cell.d[V][0],
+                              cell.d[H][1], cell.d[V][1],
+                              s);
+                      free (s);
+                    }
+                  render_cell (page, ofs, &cell);
+                }
+              x = rule_ofs (cell.d[H][1]);
+            }
+          else
+            x++;
+      }
 
   for (int y = bb[V][0]; y < bb[V][1]; y++)
     for (int x = bb[H][0]; x < bb[H][1]; x++)
@@ -1456,30 +1521,16 @@ struct render_pager
     const struct render_params *params;
     double scale;
 
-    /* An array of "render_page"s to be rendered, in order, vertically.  There
-       may be up to 5 pages, for the pivot table's title, layers, body,
-       captions, and footnotes. */
-    struct render_page *pages[5];
-    size_t n_pages;
+    struct render_page *page;
 
-    size_t cur_page;
     struct render_break x_break;
     struct render_break y_break;
   };
 
-static void
-render_pager_add_table (struct render_pager *p, struct table *table,
-                        int min_width)
-{
-  if (table)
-    p->pages[p->n_pages++] = render_page_create (p->params, table, min_width);
-}
-
 static void
 render_pager_start_page (struct render_pager *p)
 {
-  render_break_init (&p->x_break, render_page_ref (p->pages[p->cur_page++]),
-                     H);
+  render_break_init (&p->x_break, render_page_ref (p->page), H);
   render_break_init_empty (&p->y_break);
 }
 
@@ -1493,26 +1544,24 @@ render_pager_create (const struct render_params *params,
   if (!layer_indexes)
     layer_indexes = pt->current_layer;
 
-  struct table *title, *layers, *body, *caption, *footnotes;
-  pivot_output (pt, layer_indexes, params->printing,
-                &title, &layers, &body, &caption, &footnotes, NULL, NULL);
+  struct table *table = pivot_output_monolithic (pt, layer_indexes,
+                                                 params->printing);
 
-  /* Figure out the width of the body of the table.  Use this to determine the
-     base scale. */
-  struct render_page *body_page = render_page_create (params, body, 0);
-  int body_width = table_width (body_page, H);
+  /* Measure the table width and use it to determine the base scale. */
+  struct render_page *page = render_page_create (params, table, 0);
+  int width = table_width (page, H);
   double scale = 1.0;
-  if (body_width > params->size[H])
+  if (width > params->size[H])
     {
       if (pt->look->shrink_to_fit[H] && params->ops->scale)
-        scale = params->size[H] / (double) body_width;
+        scale = params->size[H] / (double) width;
       else
         {
           struct render_break b;
-          render_break_init (&b, render_page_ref (body_page), H);
+          render_break_init (&b, render_page_ref (page), H);
           struct render_page *subpage
             = render_break_next (&b, params->size[H]);
-          body_width = subpage ? subpage->cp[H][2 * subpage->n[H] + 1] : 0;
+          width = subpage ? subpage->cp[H][2 * subpage->n[H] + 1] : 0;
           render_page_unref (subpage);
           render_break_destroy (&b);
         }
@@ -1520,13 +1569,7 @@ render_pager_create (const struct render_params *params,
 
   /* Create the pager. */
   struct render_pager *p = xmalloc (sizeof *p);
-  *p = (struct render_pager) { .params = params, .scale = scale };
-  render_pager_add_table (p, title, body_width);
-  render_pager_add_table (p, layers, body_width);
-  p->pages[p->n_pages++] = body_page;
-  render_pager_add_table (p, caption, 0);
-  render_pager_add_table (p, footnotes, 0);
-  assert (p->n_pages <= sizeof p->pages / sizeof *p->pages);
+  *p = (struct render_pager) { .params = params, .scale = scale, .page = page };
 
   /* If we're shrinking tables to fit the page length, then adjust the scale
      factor.
@@ -1538,11 +1581,9 @@ render_pager_create (const struct render_params *params,
      necessary would require an iterative search. */
   if (pt->look->shrink_to_fit[V] && params->ops->scale)
     {
-      int total_height = 0;
-      for (size_t i = 0; i < p->n_pages; i++)
-        total_height += table_width (p->pages[i], V);
-      if (total_height * p->scale >= params->size[V])
-        p->scale *= params->size[V] / (double) total_height;
+      double height = table_width (p->page, V);
+      if (height * p->scale >= params->size[V])
+        p->scale *= params->size[V] / height;
     }
 
   render_pager_start_page (p);
@@ -1558,8 +1599,7 @@ render_pager_destroy (struct render_pager *p)
     {
       render_break_destroy (&p->x_break);
       render_break_destroy (&p->y_break);
-      for (size_t i = 0; i < p->n_pages; i++)
-        render_page_unref (p->pages[i]);
+      render_page_unref (p->page);
       free (p);
     }
 }
@@ -1577,13 +1617,9 @@ render_pager_has_next (const struct render_pager *p_)
       if (!render_break_has_next (&p->x_break))
         {
           render_break_destroy (&p->x_break);
-          if (p->cur_page >= p->n_pages)
-            {
-              render_break_init_empty (&p->x_break);
-              render_break_init_empty (&p->y_break);
-              return false;
-            }
-          render_pager_start_page (p);
+          render_break_init_empty (&p->x_break);
+          render_break_init_empty (&p->y_break);
+          return false;
         }
       else
         render_break_init (
@@ -1609,22 +1645,17 @@ render_pager_draw_next (struct render_pager *p, int space)
     }
 
   int ofs[TABLE_N_AXES] = { 0, 0 };
-  size_t start_page = SIZE_MAX;
 
-  while (render_pager_has_next (p))
+  if (render_pager_has_next (p))
     {
-      if (start_page == p->cur_page)
-        break;
-      start_page = p->cur_page;
-
       struct render_page *page
         = render_break_next (&p->y_break, space - ofs[V]);
-      if (!page)
-        break;
-
-      render_page_draw (page, ofs);
-      ofs[V] += render_page_get_size (page, V);
-      render_page_unref (page);
+      if (page)
+        {
+          render_page_draw (page, ofs);
+          ofs[V] += render_page_get_size (page, V);
+          render_page_unref (page);
+        }
     }
 
   if (p->scale != 1.0)
@@ -1652,18 +1683,14 @@ render_pager_draw_region (const struct render_pager *p,
 
   clip[H][0] = x;
   clip[H][1] = x + w;
-  for (size_t i = 0; i < p->n_pages; i++)
-    {
-      const struct render_page *page = p->pages[i];
-      int size = render_page_get_size (page, V);
+  int size = render_page_get_size (p->page, V);
 
-      clip[V][0] = MAX (y, ofs[V]) - ofs[V];
-      clip[V][1] = MIN (y + h, ofs[V] + size) - ofs[V];
-      if (clip[V][1] > clip[V][0])
-        render_page_draw_region (page, ofs, clip);
+  clip[V][0] = MAX (y, ofs[V]) - ofs[V];
+  clip[V][1] = MIN (y + h, ofs[V] + size) - ofs[V];
+  if (clip[V][1] > clip[V][0])
+    render_page_draw_region (p->page, ofs, clip);
 
-      ofs[V] += size;
-    }
+  ofs[V] += size;
 }
 
 /* Returns the size of P's content along AXIS; i.e. the content's width if AXIS
@@ -1671,32 +1698,16 @@ render_pager_draw_region (const struct render_pager *p,
 int
 render_pager_get_size (const struct render_pager *p, enum table_axis axis)
 {
-  int size = 0;
-
-  for (size_t i = 0; i < p->n_pages; i++)
-    {
-      int subsize = render_page_get_size (p->pages[i], axis);
-      size = axis == H ? MAX (size, subsize) : size + subsize;
-    }
-
-  return size;
+  return render_page_get_size (p->page, axis);
 }
 
 int
 render_pager_get_best_breakpoint (const struct render_pager *p, int height)
 {
-  int y = 0;
-  size_t i;
-
-  for (i = 0; i < p->n_pages; i++)
-    {
-      int size = render_page_get_size (p->pages[i], V);
-      if (y + size >= height)
-        return render_page_get_best_breakpoint (p->pages[i], height - y) + y;
-      y += size;
-    }
-
-  return height;
+  int size = render_page_get_size (p->page, V);
+  return (size < height
+          ? height
+          : render_page_get_best_breakpoint (p->page, height));
 }
 \f
 /* render_page_select() and helpers. */
@@ -1754,19 +1765,21 @@ render_page_select (const struct render_page *page, enum table_axis axis,
 
   /* Allocate subpage. */
   int trim[2] = { z0 - page->h[a][0], (page->n[a] - page->h[a][1]) - z1 };
+
   int n[TABLE_N_AXES] = { [H] = page->n[H], [V] = page->n[V] };
   n[a] -= trim[0] + trim[1];
-  struct render_page *subpage = render_page_allocate__ (
-    page->params, table_ref (page->table), n);
+
+  int r[TABLE_N_AXES][2];
   for (enum table_axis k = 0; k < TABLE_N_AXES; k++)
     {
-      subpage->h[k][0] = page->h[k][0];
-      subpage->h[k][1] = page->h[k][1];
-      subpage->r[k][0] = page->r[k][0];
-      subpage->r[k][1] = page->r[k][1];
+      r[k][0] = page->r[k][0];
+      r[k][1] = page->r[k][1];
     }
-  subpage->r[a][0] += trim[0];
-  subpage->r[a][1] -= trim[1];
+  r[a][0] += trim[0];
+  r[a][1] -= trim[1];
+
+  struct render_page *subpage = render_page_allocate__ (
+    page->params, table_ref (page->table), n, page->h, r);
 
   /* An edge is cut off if it was cut off in PAGE or if we're trimming pixels
      off that side of the page and there are no headers. */