output: Implement nested tables.
[pspp] / src / output / render.c
index 274d692c61c72946fc786666c284d470149a71bb..503d5f692d7d7d3d3cfb654d869573c5f30978f4 100644 (file)
@@ -827,6 +827,23 @@ render_page_get_size (const struct render_page *page, enum table_axis axis)
 {
   return page->cp[axis][page->n[axis] * 2 + 1];
 }
+
+int
+render_page_get_best_breakpoint (const struct render_page *page, int height)
+{
+  int y;
+
+  /* If there's no room for at least the top row and the rules above and below
+     it, don't include any of the table. */
+  if (page->cp[V][3] > height)
+    return 0;
+
+  /* Otherwise include as many rows and rules as we can. */
+  for (y = 5; y <= 2 * page->n[V] + 1; y += 2)
+    if (page->cp[V][y] > height)
+      return page->cp[V][y - 2];
+  return height;
+}
 \f
 /* Drawing render_pages. */
 
@@ -951,7 +968,7 @@ render_page_draw_cells (const struct render_page *page,
           struct table_cell cell;
 
           table_get_cell (page->table, x / 2, y / 2, &cell);
-          if (y == bb[V][0] || y / 2 == cell.d[V][0])
+          if (y / 2 == bb[V][0] / 2 || y / 2 == cell.d[V][0])
             render_cell (page, &cell);
           x = rule_ofs (cell.d[H][1]);
           table_cell_free (&cell);
@@ -998,7 +1015,7 @@ get_clip_min_extent (int x0, const int cp[], int n)
   return best;
 }
 
-/* Returns the least value i, 0 <= i < n, such that cp[i + 1] >= x1. */
+/* Returns the least value i, 0 <= i < n, such that cp[i] >= x1. */
 static int
 get_clip_max_extent (int x1, const int cp[], int n)
 {
@@ -1017,6 +1034,9 @@ get_clip_max_extent (int x1, const int cp[], int n)
         low = middle + 1;
     }
 
+  while (best > 0 && cp[best - 1] == cp[best])
+    best--;
+
   return best;
 }
 
@@ -1056,7 +1076,7 @@ render_break_init (struct render_break *b, struct render_page *page,
 {
   b->page = page;
   b->axis = axis;
-  b->cell = page->h[axis][0];
+  b->z = page->h[axis][0];
   b->pixel = 0;
   b->hw = headers_width (page, axis);
 }
@@ -1068,7 +1088,7 @@ render_break_init_empty (struct render_break *b)
 {
   b->page = NULL;
   b->axis = TABLE_HORZ;
-  b->cell = 0;
+  b->z = 0;
   b->pixel = 0;
   b->hw = 0;
 }
@@ -1092,7 +1112,7 @@ render_break_has_next (const struct render_break *b)
   const struct render_page *page = b->page;
   enum table_axis axis = b->axis;
 
-  return page != NULL && b->cell < page->n[axis] - page->h[axis][1];
+  return page != NULL && b->z < page->n[axis] - page->h[axis][1];
 }
 
 /* Returns the minimum SIZE argument that, if passed to render_break_next(),
@@ -1104,7 +1124,7 @@ render_break_next_size (const struct render_break *b)
   enum table_axis axis = b->axis;
 
   return (!render_break_has_next (b) ? 0
-          : !cell_is_breakable (b, b->cell) ? needed_size (b, b->cell + 1)
+          : !cell_is_breakable (b, b->z) ? needed_size (b, b->z + 1)
           : b->hw + page->params->font_size[axis]);
 }
 
@@ -1119,18 +1139,18 @@ render_break_next (struct render_break *b, int size)
   const struct render_page *page = b->page;
   enum table_axis axis = b->axis;
   struct render_page *subpage;
-  int cell, pixel;
+  int z, pixel;
 
   if (!render_break_has_next (b))
     return NULL;
 
   pixel = 0;
-  for (cell = b->cell; cell < page->n[axis] - page->h[axis][1]; cell++)
+  for (z = b->z; z < page->n[axis] - page->h[axis][1]; z++)
     {
-      int needed = needed_size (b, cell + 1);
+      int needed = needed_size (b, z + 1);
       if (needed > size)
         {
-          if (cell_is_breakable (b, cell))
+          if (cell_is_breakable (b, z))
             {
               /* If there is no right header and we render a partial cell on
                  the right side of the body, then we omit the rightmost rule of
@@ -1141,18 +1161,18 @@ render_break_next (struct render_break *b, int size)
                  This is similar to code for the left side in needed_size(). */
               int rule_allowance = (page->h[axis][1]
                                     ? 0
-                                    : rule_width (page, axis, cell));
+                                    : rule_width (page, axis, z));
 
-              /* The amount that, if we added 'cell', the rendering would
+              /* The amount that, if we added cell 'z', the rendering would
                  overfill the allocated 'size'. */
               int overhang = needed - size - rule_allowance;
 
-              /* The width of 'cell'. */
-              int cell_size = cell_width (page, axis, cell);
+              /* The width of cell 'z'. */
+              int cell_size = cell_width (page, axis, z);
 
-              /* The amount trimmed the left side of 'cell',
+              /* The amount trimmed off the left side of 'z',
                  and the amount left to render. */
-              int cell_ofs = cell == b->cell ? b->pixel : 0;
+              int cell_ofs = z == b->z ? b->pixel : 0;
               int cell_left = cell_size - cell_ofs;
 
               /* A small but visible width.  */
@@ -1170,19 +1190,56 @@ render_break_next (struct render_break *b, int size)
                  to make the output look a little better. */
               if (pixel + em > cell_size)
                 pixel = MAX (pixel - em, 0);
+
+              /* If we're breaking vertically, then consider whether the cells
+                 being broken have a better internal breakpoint than the exact
+                 number of pixels available, which might look bad e.g. because
+                 it breaks in the middle of a line of text. */
+              if (axis == TABLE_VERT && page->params->adjust_break)
+                {
+                  int x;
+
+                  for (x = 0; x < page->n[H]; )
+                    {
+                      struct table_cell cell;
+                      int better_pixel;
+                      int w;
+
+                      table_get_cell (page->table, x, z, &cell);
+                      w = joined_width (page, H, cell.d[H][0], cell.d[H][1]);
+                      better_pixel = page->params->adjust_break (
+                        page->params->aux, &cell, w, pixel);
+                      x = cell.d[H][1];
+                      table_cell_free (&cell);
+
+                      if (better_pixel < pixel)
+                        {
+                          if (better_pixel > (z == b->z ? b->pixel : 0))
+                            {
+                              pixel = better_pixel;
+                              break;
+                            }
+                          else if (better_pixel == 0 && z != b->z)
+                            {
+                              pixel = 0;
+                              break;
+                            }
+                        }
+                    }
+                }
             }
           break;
         }
     }
 
-  if (cell == b->cell && !pixel)
+  if (z == b->z && !pixel)
     return NULL;
 
-  subpage = render_page_select (page, axis, b->cell, b->pixel,
-                                pixel ? cell + 1 : cell,
-                                pixel ? cell_width (page, axis, cell) - pixel
+  subpage = render_page_select (page, axis, b->z, b->pixel,
+                                pixel ? z + 1 : z,
+                                pixel ? cell_width (page, axis, z) - pixel
                                 : 0);
-  b->cell = cell;
+  b->z = z;
   b->pixel = pixel;
   return subpage;
 }
@@ -1209,10 +1266,10 @@ needed_size (const struct render_break *b, int cell)
      invidiually. */
   if (b->pixel == 0 || page->h[axis][0])
     size += MAX (rule_width (page, axis, page->h[axis][0]),
-                 rule_width (page, axis, b->cell));
+                 rule_width (page, axis, b->z));
 
   /* Width of body, minus any pixel offset in the leftmost cell. */
-  size += joined_width (page, axis, b->cell, cell) - b->pixel;
+  size += joined_width (page, axis, b->z, cell) - b->pixel;
 
   /* Width of rightmost rule in body merged with leftmost rule in headers. */
   size += MAX (rule_width_r (page, axis, page->h[axis][1]),
@@ -1224,7 +1281,7 @@ needed_size (const struct render_break *b, int cell)
 
   /* Join crossing. */
   if (page->h[axis][0] && page->h[axis][1])
-    size += page->join_crossing[axis][b->cell];
+    size += page->join_crossing[axis][b->z];
 
   return size;
 }