#include "libpspp/assertion.h"
#include "libpspp/hash-functions.h"
#include "libpspp/hmap.h"
+#include "libpspp/pool.h"
+#include "output/pivot-output.h"
+#include "output/pivot-table.h"
#include "output/render.h"
-#include "output/tab.h"
-#include "output/table-item.h"
#include "output/table.h"
#include "gl/minmax.h"
struct table *table; /* Table rendered. */
int ref_cnt;
- /* Local copies of table->n and table->h, for convenience. */
+ /* Region of 'table' to render.
+
+ The horizontal cells rendered are the leftmost h[H], then
+ r[H] through r[H][1].
+
+ The vertical cells rendered are the topmost h[V], then r[V][0]
+ through r[V][1].
+
+ n[H] = h[H] + (r[H][1] - r[H][0])
+ n[V] = h[V] + (r[V][1] - r[V][0])
+ */
+ int h[TABLE_N_AXES];
+ int r[TABLE_N_AXES][2];
int n[TABLE_N_AXES];
- int h[TABLE_N_AXES][2];
- /* cp[H] represents x positions within the table.
+ /* "Cell positions".
+
+ cp[H] represents x positions within the table.
cp[H][0] = 0.
cp[H][1] = the width of the leftmost vertical rule.
cp[H][2] = cp[H][1] + the width of the leftmost column.
cp[H][3] = cp[H][2] + the width of the second-from-left vertical rule.
and so on:
- cp[H][2 * nc] = x position of the rightmost vertical rule.
- cp[H][2 * nc + 1] = total table width including all rules.
+ cp[H][2 * n[H]] = x position of the rightmost vertical rule.
+ cp[H][2 * n[H] + 1] = total table width including all rules.
Similarly, cp[V] represents y positions within the table.
cp[V][0] = 0.
cp[V][2] = cp[V][1] + the height of the topmost row.
cp[V][3] = cp[V][2] + the height of the second-from-top horizontal rule.
and so on:
- cp[V][2 * nr] = y position of the bottommost horizontal rule.
- cp[V][2 * nr + 1] = total table height including all rules.
+ cp[V][2 * n[V]] = y position of the bottommost horizontal rule.
+ cp[V][2 * n[V] + 1] = total table height including all rules.
Rules and columns can have width or height 0, in which case consecutive
values in this array are equal. */
entire page can overflow on all four sides!) */
struct hmap overflows;
- /* Contains "struct render_footnote"s, one for each cell with one or more
- footnotes.
-
- 'n_footnotes' is the number of footnotes in the table. There might be
- more than hmap_count(&page->footnotes) because there can be more than
- one footnote in a cell. */
- struct hmap footnotes;
- size_t n_footnotes;
-
/* If a single column (or row) is too wide (or tall) to fit on a page
reasonably, then render_break_next() will split a single row or column
across multiple render_pages. This member indicates when this has
When is_edge_cutoff is true for a given edge, the 'overflows' hmap will
contain a node for each cell along that edge. */
bool is_edge_cutoff[TABLE_N_AXES][2];
-
- /* If part of a joined cell would be cut off by breaking a table along
- 'axis' at the rule with offset 'z' (where 0 <= z <= n[axis]), then
- join_crossing[axis][z] is the thickness of the rule that would be cut
- off.
-
- This is used to know to allocate extra space for breaking at such a
- position, so that part of the cell's content is not lost.
-
- This affects breaking a table only when headers are present. When
- headers are not present, the rule's thickness is used for cell content,
- so no part of the cell's content is lost (and in fact it is duplicated
- across both pages). */
- int *join_crossing[TABLE_N_AXES];
};
static struct render_page *render_page_create (const struct render_params *,
- struct table *);
+ struct table *, int min_width);
struct render_page *render_page_ref (const struct render_page *page_);
static void render_page_unref (struct render_page *);
return page->cp[axis][ofs1] - page->cp[axis][ofs0];
}
+/* Returns the total width of PAGE along AXIS. */
+static int
+table_width (const struct render_page *page, int axis)
+{
+ return page->cp[axis][2 * page->n[axis] + 1];
+}
+
/* Returns the width of the headers in PAGE along AXIS. */
static int
headers_width (const struct render_page *page, int axis)
{
- int h0 = page->h[axis][0];
- int w0 = axis_width (page, axis, rule_ofs (0), cell_ofs (h0));
- int n = page->n[axis];
- int h1 = page->h[axis][1];
- int w1 = axis_width (page, axis, rule_ofs_r (page, axis, h1), cell_ofs (n));
- return w0 + w1;
+ return axis_width (page, axis, rule_ofs (0), cell_ofs (page->h[axis]));
}
/* Returns the width of cell X along AXIS in PAGE. */
static int
max_cell_width (const struct render_page *page, int axis)
{
- int n = page->n[axis];
- int x0 = page->h[axis][0];
- int x1 = n - page->h[axis][1];
- int x, max;
+ int x0 = page->h[axis];
+ int x1 = page->n[axis];
- max = 0;
- for (x = x0; x < x1; x++)
+ int max = 0;
+ for (int x = x0; x < x1; x++)
{
int w = cell_width (page, axis, x);
if (w > max)
return NULL;
}
\f
-/* A footnote. */
-struct render_footnote
- {
- struct hmap_node node;
-
- /* The area of the table covered by the cell that has the footnote.
-
- d[H][0] is the leftmost column.
- d[H][1] is the rightmost column, plus 1.
- d[V][0] is the top row.
- d[V][1] is the bottom row, plus 1.
-
- The cell in its original table might occupy a larger region. This
- member reflects the size of the cell in the current render_page, after
- trimming off any rows or columns due to page-breaking. */
- int d[TABLE_N_AXES][2];
-
- /* The index of the first footnote in the cell. */
- int idx;
- };
-
-static int
-count_footnotes (const struct table_cell *cell)
-{
- size_t i;
- int n;
-
- n = 0;
- for (i = 0; i < cell->n_contents; i++)
- n += cell->contents[i].n_footnotes;
- return n;
-}
-
-static int
-find_footnote_idx (const struct table_cell *cell, const struct hmap *footnotes)
-{
- const struct render_footnote *f;
-
- if (!count_footnotes (cell))
- return 0;
-
- HMAP_FOR_EACH_WITH_HASH (f, struct render_footnote, node,
- hash_cell (cell->d[H][0], cell->d[V][0]), footnotes)
- if (f->d[H][0] == cell->d[H][0] && f->d[V][0] == cell->d[V][0])
- return f->idx;
-
- NOT_REACHED ();
-}
-\f
/* Row or column dimensions. Used to figure the size of a table in
render_page_create() and discarded after that. */
struct render_row
const struct render_row *rows, const int *rules)
{
int n = page->n[axis];
- int *cp;
- int z;
-
- cp = page->cp[axis];
+ int *cp = page->cp[axis];
cp[0] = 0;
- for (z = 0; z < n; z++)
+ for (int z = 0; z < n; z++)
{
cp[1] = cp[0] + rules[z];
cp[2] = cp[1] + rows[z].width;
static int
calculate_table_width (int n, const struct render_row *rows, int *rules)
{
- int width;
- int x;
-
- width = 0;
- for (x = 0; x < n; x++)
+ int width = 0;
+ for (int x = 0; x < n; x++)
width += rows[x].width;
- for (x = 0; x <= n; x++)
+ for (int x = 0; x <= n; x++)
width += rules[x];
return width;
\f
/* Rendering utility functions. */
-/* Returns the line style to use for drawing a rule of the given TYPE. */
-static enum render_line_style
-rule_to_render_type (unsigned char type)
-{
- switch (type)
- {
- case TAL_0:
- return RENDER_LINE_NONE;
- case TAL_1:
- return RENDER_LINE_SINGLE;
- case TAL_2:
- return RENDER_LINE_DOUBLE;
- default:
- NOT_REACHED ();
- }
-}
-
/* Returns the width of the rule in TABLE that is at offset Z along axis A, if
rendered with PARAMS. */
static int
enum table_axis a, int z)
{
enum table_axis b = !a;
- unsigned int rules;
- int d[TABLE_N_AXES];
- int width;
/* Determine all types of rules that are present, as a bitmap in 'rules'
where rule type 't' is present if bit 2**t is set. */
- rules = 0;
+ unsigned int rules = 0;
+ int d[TABLE_N_AXES];
d[a] = z;
for (d[b] = 0; d[b] < table->n[b]; d[b]++)
- rules |= 1u << table_get_rule (table, a, d[H], d[V]);
+ rules |= 1u << table_get_rule (table, a, d[H], d[V]).stroke;
- /* Turn off TAL_NONE because it has width 0 and we needn't bother. However,
- if the device doesn't support margins, make sure that there is at least a
- small gap between cells (but we don't need any at the left or right edge
- of the table). */
- if (rules & (1u << TAL_0))
+ /* Turn off TABLE_STROKE_NONE because it has width 0 and we needn't bother.
+ However, if the device doesn't support margins, make sure that there is at
+ least a small gap between cells (but we don't need any at the left or
+ right edge of the table). */
+ if (rules & (1u << TABLE_STROKE_NONE))
{
- rules &= ~(1u << TAL_0);
+ rules &= ~(1u << TABLE_STROKE_NONE);
if (z > 0 && z < table->n[a] && !params->supports_margins && a == H)
- rules |= 1u << TAL_1;
+ rules |= 1u << TABLE_STROKE_SOLID;
}
/* Calculate maximum width of the rules that are present. */
- width = 0;
- if (rules & (1u << TAL_1)
- || (z > 0 && z < table->n[a] && rules & (1u << TAL_0)))
- width = params->line_widths[a][RENDER_LINE_SINGLE];
- if (rules & (1u << TAL_2))
- width = MAX (width, params->line_widths[a][RENDER_LINE_DOUBLE]);
+ int width = 0;
+ for (size_t i = 0; i < TABLE_N_STROKES; i++)
+ if (rules & (1u << i))
+ width = MAX (width, params->line_widths[i]);
return width;
}
+/* Allocates and returns a new render_page using PARAMS and TABLE. Allocates
+ 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,
+ const int n[TABLE_N_AXES],
+ const int h[TABLE_N_AXES],
+ const int r[TABLE_N_AXES][2])
+{
+ struct render_page *page = xmalloc (sizeof *page);
+ *page = (struct render_page) {
+ .params = params,
+ .table = table,
+ .ref_cnt = 1,
+ .n = { [H] = n[H], [V] = n[V] },
+ .h = { [H] = h[H], [V] = h[V] },
+ .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]) },
+ .overflows = HMAP_INITIALIZER (page->overflows),
+ };
+ return page;
+}
+
/* Allocates and returns a new render_page using PARAMS and TABLE. Allocates
space for all of the members of the new page, but the caller must initialize
the 'cp' member itself. */
static struct render_page *
-render_page_allocate (const struct render_params *params,
- struct table *table)
+render_page_allocate (const struct render_params *params, struct table *table)
{
- struct render_page *page;
- int i;
-
- page = xmalloc (sizeof *page);
- page->params = params;
- page->table = table;
- page->ref_cnt = 1;
- page->n[H] = table->n[H];
- page->n[V] = table->n[V];
- page->h[H][0] = table->h[H][0];
- page->h[H][1] = table->h[H][1];
- page->h[V][0] = table->h[V][0];
- page->h[V][1] = table->h[V][1];
-
- for (i = 0; i < TABLE_N_AXES; i++)
+ int h[TABLE_N_AXES];
+ int r[TABLE_N_AXES][2];
+ for (enum table_axis a = 0; a < TABLE_N_AXES; a++)
{
- page->cp[i] = xmalloc ((2 * page->n[i] + 2) * sizeof *page->cp[i]);
- page->join_crossing[i] = xzalloc ((page->n[i] + 1) * sizeof *page->join_crossing[i]);
+ h[a] = table->h[a];
+ r[a][0] = table->h[a];
+ r[a][1] = table->n[a];
}
-
- hmap_init (&page->overflows);
- hmap_init (&page->footnotes);
- page->n_footnotes = 0;
- memset (page->is_edge_cutoff, 0, sizeof page->is_edge_cutoff);
-
- return page;
+ return render_page_allocate__ (params, table, table->n, h, r);
}
/* Allocates and returns a new render_page for PARAMS and TABLE, initializing
assert (page->cp[H][n * 2 + 1] == params->size[H]);
return page;
}
-
\f
+/* Maps a contiguous range of cells from a page to the underlying table along
+ the horizontal 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
-set_join_crossings (struct render_page *page, enum table_axis axis,
- const struct table_cell *cell, int *rules)
+get_map (const struct render_page *page, enum table_axis a, int z,
+ struct map *m)
{
- int z;
+ if (z < page->h[a])
+ {
+ m->p0 = 0;
+ m->t0 = 0;
+ m->n = page->h[a];
+ }
+ else
+ {
+ assert (z < page->n[a]);
+ m->p0 = page->h[a];
+ m->t0 = page->r[a][0];
+ m->n = page->r[a][1] - page->r[a][0];
+ }
+}
+
+/* Initializes CELL with the contents of the table cell at column X and row Y
+ within PAGE. When CELL is no longer needed, the caller is responsible for
+ freeing it by calling table_cell_free(CELL).
+
+ The caller must ensure that CELL is destroyed before TABLE is unref'ed.
+
+ This is equivalent to table_get_cell(), except X and Y are in terms of the
+ page's rows and columns rather than the underlying table's. */
+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];
- for (z = cell->d[axis][0] + 1; z <= cell->d[axis][1] - 1; z++)
- page->join_crossing[axis][z] = rules[z];
+ 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);
+
+ for (enum table_axis a = 0; a < TABLE_N_AXES; a++)
+ {
+ 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 (cell->options & TABLE_CELL_FULL_WIDTH)
+ {
+ cell->d[H][0] = 0;
+ cell->d[H][1] = page->n[H];
+ }
}
/* Creates and returns a new render_page for rendering TABLE on a device
size is PARAMS->size, but the caller is responsible for actually breaking it
up to fit on such a device, using the render_break abstraction. */
static struct render_page *
-render_page_create (const struct render_params *params, struct table *table)
+render_page_create (const struct render_params *params, struct table *table,
+ int min_width)
{
- struct render_page *page;
enum { MIN, MAX };
- struct render_row *columns[2];
- struct render_row *rows;
- int table_widths[2];
- int *rules[TABLE_N_AXES];
- struct hmap footnotes;
- int footnote_idx;
- int nr, nc;
- int x, y;
- int i;
- enum table_axis axis;
- nc = table_nc (table);
- nr = table_nr (table);
+ int nc = table->n[H];
+ int nr = table->n[V];
/* Figure out rule widths. */
- for (axis = 0; axis < TABLE_N_AXES; axis++)
+ int *rules[TABLE_N_AXES];
+ for (enum table_axis axis = 0; axis < TABLE_N_AXES; axis++)
{
int n = table->n[axis] + 1;
- int z;
rules[axis] = xnmalloc (n, sizeof *rules);
- for (z = 0; z < n; z++)
+ for (int z = 0; z < n; z++)
rules[axis][z] = measure_rule (params, table, axis, z);
}
/* Calculate minimum and maximum widths of cells that do not
- span multiple columns. Assign footnote markers. */
- hmap_init (&footnotes);
- footnote_idx = 0;
- for (i = 0; i < 2; i++)
- columns[i] = xzalloc (nc * sizeof *columns[i]);
- for (y = 0; y < nr; y++)
- for (x = 0; x < nc; )
+ span multiple columns. */
+ struct render_row *columns[2];
+ for (int i = 0; i < 2; i++)
+ columns[i] = xcalloc (nc, sizeof *columns[i]);
+ for (int y = 0; y < nr; y++)
+ for (int x = 0; x < nc;)
{
struct table_cell cell;
table_get_cell (table, x, y, &cell);
if (y == cell.d[V][0])
{
- int n;
-
if (table_cell_colspan (&cell) == 1)
{
int w[2];
- int i;
-
- params->measure_cell_width (params->aux, &cell, footnote_idx,
- &w[MIN], &w[MAX]);
- for (i = 0; i < 2; i++)
+ params->ops->measure_cell_width (params->aux, &cell,
+ &w[MIN], &w[MAX]);
+ for (int i = 0; i < 2; i++)
if (columns[i][x].unspanned < w[i])
columns[i][x].unspanned = w[i];
}
-
- n = count_footnotes (&cell);
- if (n > 0)
- {
- struct render_footnote *f = xmalloc (sizeof *f);
- f->d[H][0] = cell.d[H][0];
- f->d[H][1] = cell.d[H][1];
- f->d[V][0] = cell.d[V][0];
- f->d[V][1] = cell.d[V][1];
- f->idx = footnote_idx;
- hmap_insert (&footnotes, &f->node, hash_cell (x, y));
-
- footnote_idx += n;
- }
}
x = cell.d[H][1];
- table_cell_free (&cell);
}
/* Distribute widths of spanned columns. */
- for (i = 0; i < 2; i++)
- for (x = 0; x < nc; x++)
+ for (int i = 0; i < 2; i++)
+ for (int x = 0; x < nc; x++)
columns[i][x].width = columns[i][x].unspanned;
- for (y = 0; y < nr; y++)
- for (x = 0; x < nc; )
+ for (int y = 0; y < nr; y++)
+ for (int x = 0; x < nc;)
{
struct table_cell cell;
{
int w[2];
- params->measure_cell_width (params->aux, &cell,
- find_footnote_idx (&cell, &footnotes),
- &w[MIN], &w[MAX]);
- for (i = 0; i < 2; i++)
- distribute_spanned_width (w[i], &columns[i][cell.d[H][0]],
- rules[H], table_cell_colspan (&cell));
+ params->ops->measure_cell_width (params->aux, &cell,
+ &w[MIN], &w[MAX]);
+ for (int i = 0; i < 2; i++)
+ distribute_spanned_width (w[i],
+ &columns[i][cell.d[H][0]],
+ &rules[H][cell.d[H][0]],
+ table_cell_colspan (&cell));
}
x = cell.d[H][1];
- table_cell_free (&cell);
}
+ if (min_width > 0)
+ for (int i = 0; i < 2; i++)
+ distribute_spanned_width (min_width, &columns[i][0], rules[H], nc);
/* In pathological cases, spans can cause the minimum width of a column to
exceed the maximum width. This bollixes our interpolation algorithm
later, so fix it up. */
- for (i = 0; i < nc; i++)
+ for (int i = 0; i < nc; i++)
if (columns[MIN][i].width > columns[MAX][i].width)
columns[MAX][i].width = columns[MIN][i].width;
/* Decide final column widths. */
- for (i = 0; i < 2; i++)
- table_widths[i] = calculate_table_width (table_nc (table),
+ int table_widths[2];
+ for (int i = 0; i < 2; i++)
+ table_widths[i] = calculate_table_width (table->n[H],
columns[i], rules[H]);
+
+ struct render_page *page;
if (table_widths[MAX] <= params->size[H])
{
/* Fits even with maximum widths. Use them. */
}
/* Calculate heights of cells that do not span multiple rows. */
- rows = xzalloc (nr * sizeof *rows);
- for (y = 0; y < nr; y++)
- {
- for (x = 0; x < nc; )
- {
- struct render_row *r = &rows[y];
- struct table_cell cell;
+ struct render_row *rows = XCALLOC (nr, struct render_row);
+ for (int y = 0; y < nr; y++)
+ for (int x = 0; x < nc;)
+ {
+ struct render_row *r = &rows[y];
+ struct table_cell cell;
- table_get_cell (table, x, y, &cell);
- if (y == cell.d[V][0])
- {
- if (table_cell_rowspan (&cell) == 1)
- {
- int w = joined_width (page, H, cell.d[H][0], cell.d[H][1]);
- int h = params->measure_cell_height (
- params->aux, &cell, find_footnote_idx (&cell, &footnotes), w);
- if (h > r->unspanned)
- r->unspanned = r->width = h;
- }
- else
- set_join_crossings (page, V, &cell, rules[V]);
-
- if (table_cell_colspan (&cell) > 1)
- set_join_crossings (page, H, &cell, rules[H]);
- }
- x = cell.d[H][1];
- table_cell_free (&cell);
- }
- }
- for (i = 0; i < 2; i++)
+ render_get_cell (page, x, y, &cell);
+ if (y == cell.d[V][0] && table_cell_rowspan (&cell) == 1)
+ {
+ int w = joined_width (page, H, cell.d[H][0], cell.d[H][1]);
+ int h = params->ops->measure_cell_height (params->aux,
+ &cell, w);
+ if (h > r->unspanned)
+ r->unspanned = r->width = h;
+ }
+ x = cell.d[H][1];
+ }
+ for (int i = 0; i < 2; i++)
free (columns[i]);
/* Distribute heights of spanned rows. */
- for (y = 0; y < nr; y++)
- for (x = 0; x < nc; )
+ for (int y = 0; y < nr; y++)
+ for (int x = 0; x < nc;)
{
struct table_cell cell;
- table_get_cell (table, x, y, &cell);
+ render_get_cell (page, x, y, &cell);
if (y == cell.d[V][0] && table_cell_rowspan (&cell) > 1)
{
int w = joined_width (page, H, cell.d[H][0], cell.d[H][1]);
- int h = params->measure_cell_height (
- params->aux, &cell, find_footnote_idx (&cell, &footnotes), w);
- distribute_spanned_width (h, &rows[cell.d[V][0]], rules[V],
+ int h = params->ops->measure_cell_height (params->aux, &cell, w);
+ distribute_spanned_width (h,
+ &rows[cell.d[V][0]],
+ &rules[V][cell.d[V][0]],
table_cell_rowspan (&cell));
}
x = cell.d[H][1];
- table_cell_free (&cell);
}
/* Decide final row heights. */
free (rows);
/* Measure headers. If they are "too big", get rid of them. */
- for (axis = 0; axis < TABLE_N_AXES; axis++)
+ for (enum table_axis axis = 0; axis < TABLE_N_AXES; axis++)
{
int hw = headers_width (page, axis);
if (hw * 2 >= page->params->size[axis]
|| hw + max_cell_width (page, axis) > page->params->size[axis])
{
- page->table = table_unshare (page->table);
- page->table->h[axis][0] = page->table->h[axis][1] = 0;
- page->h[axis][0] = page->h[axis][1] = 0;
+ page->h[axis] = 0;
+ page->r[axis][0] = 0;
+ page->r[axis][1] = page->n[axis];
}
}
- hmap_swap (&page->footnotes, &footnotes);
- hmap_destroy (&footnotes);
- page->n_footnotes = footnote_idx;
-
free (rules[H]);
free (rules[V]);
{
if (page != NULL && --page->ref_cnt == 0)
{
- int i;
struct render_overflow *overflow, *next;
-
HMAP_FOR_EACH_SAFE (overflow, next, struct render_overflow, node,
&page->overflows)
free (overflow);
table_unref (page->table);
- for (i = 0; i < TABLE_N_AXES; ++i)
- {
- free (page->join_crossing[i]);
- free (page->cp[i]);
- }
+ for (int i = 0; i < TABLE_N_AXES; ++i)
+ free (page->cp[i]);
free (page);
}
static 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)
+ for (int 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. */
-static inline enum render_line_style
+/* This is like table_get_rule() except that D is in terms of the page's rows
+ and column rather than the underlying table's. */
+static struct table_border_style
get_rule (const struct render_page *page, enum table_axis axis,
- const int d[TABLE_N_AXES])
+ const int d_[TABLE_N_AXES])
{
- return rule_to_render_type (table_get_rule (page->table,
- axis, d[H] / 2, d[V] / 2));
+ int d[TABLE_N_AXES] = { d_[0] / 2, d_[1] / 2 };
+ int d2 = -1;
+
+ enum table_axis a = axis;
+ if (d[a] < page->h[a])
+ /* Nothing to do */;
+ else if (d[a] <= page->n[a])
+ {
+ if (page->h[a] && d[a] == page->h[a])
+ d2 = page->h[a];
+ d[a] += page->r[a][0] - page->h[a];
+ }
+
+ enum table_axis b = !axis;
+ struct map m;
+ get_map (page, b, d[b], &m);
+ d[b] += m.t0 - m.p0;
+
+ struct table_border_style border
+ = table_get_rule (page->table, axis, d[H], d[V]);
+ if (d2 >= 0)
+ {
+ d[a] = d2;
+ struct table_border_style border2 = table_get_rule (page->table, axis,
+ d[H], d[V]);
+ border.stroke = table_stroke_combine (border.stroke, border2.stroke);
+ }
+ return border;
}
static bool
this string with "output-direction-rtl". Otherwise either leave it
untranslated or copy it verbatim. */
const char *dir = _("output-direction-ltr");
- if ( 0 == strcmp ("output-direction-rtl", dir))
+ if (0 == strcmp ("output-direction-rtl", dir))
return true;
- if ( 0 != strcmp ("output-direction-ltr", dir))
- fprintf (stderr, "This localisation has been incorrectly translated. Complain to the translator.\n");
+ if (0 != strcmp ("output-direction-ltr", dir))
+ fprintf (stderr, "This localisation has been incorrectly translated. "
+ "Complain to the translator.\n");
return false;
}
render_rule (const struct render_page *page, const int ofs[TABLE_N_AXES],
const int d[TABLE_N_AXES])
{
- enum render_line_style styles[TABLE_N_AXES][2];
- enum table_axis a;
+ const struct table_border_style none = { .stroke = TABLE_STROKE_NONE };
+ struct table_border_style styles[TABLE_N_AXES][2];
- for (a = 0; a < TABLE_N_AXES; a++)
+ for (enum table_axis a = 0; a < TABLE_N_AXES; a++)
{
enum table_axis b = !a;
- styles[a][0] = styles[a][1] = RENDER_LINE_NONE;
-
if (!is_rule (d[a])
|| (page->is_edge_cutoff[a][0] && d[a] == 0)
|| (page->is_edge_cutoff[a][1] && d[a] == page->n[a] * 2))
- continue;
-
- if (is_rule (d[b]))
+ styles[a][0] = styles[a][1] = none;
+ else if (is_rule (d[b]))
{
if (d[b] > 0)
{
e[b]--;
styles[a][0] = get_rule (page, a, e);
}
+ else
+ styles[a][0] = none;
- if (d[b] / 2 < page->table->n[b])
+ if (d[b] / 2 < page->n[b])
styles[a][1] = get_rule (page, a, d);
+ else
+ styles[a][1] = none;
}
else
styles[a][0] = styles[a][1] = get_rule (page, a, d);
}
- if (styles[H][0] != RENDER_LINE_NONE || styles[H][1] != RENDER_LINE_NONE
- || styles[V][0] != RENDER_LINE_NONE || styles[V][1] != RENDER_LINE_NONE)
+ if (styles[H][0].stroke != TABLE_STROKE_NONE
+ || styles[H][1].stroke != TABLE_STROKE_NONE
+ || styles[V][0].stroke != TABLE_STROKE_NONE
+ || styles[V][1].stroke != TABLE_STROKE_NONE)
{
int bb[TABLE_N_AXES][2];
bb[H][0] = ofs[H] + page->cp[H][d[H]];
bb[H][1] = ofs[H] + page->cp[H][d[H] + 1];
- if (render_direction_rtl ())
+ if (page->params->rtl)
{
int temp = bb[H][0];
bb[H][0] = render_page_get_size (page, H) - bb[H][1];
}
bb[V][0] = ofs[V] + page->cp[V][d[V]];
bb[V][1] = ofs[V] + page->cp[V][d[V] + 1];
- page->params->draw_line (page->params->aux, bb, styles);
+ page->params->ops->draw_line (page->params->aux, bb, styles);
}
}
render_cell (const struct render_page *page, const int ofs[TABLE_N_AXES],
const struct table_cell *cell)
{
- const struct render_overflow *of;
+ const bool debugging = false;
+ if (debugging)
+ {
+ printf ("render ");
+ if (cell->d[H][0] + 1 == cell->d[H][1])
+ printf ("%d", cell->d[H][0]);
+ else
+ printf ("%d-%d", cell->d[H][0], cell->d[H][1] - 1);
+ printf (",");
+ if (cell->d[V][0] + 1 == cell->d[V][1])
+ printf ("%d", cell->d[V][0]);
+ else
+ printf ("%d-%d", cell->d[V][0], cell->d[V][1] - 1);
+
+ char *value = pivot_value_to_string (cell->value, NULL);
+ printf (": \"%s\"\n", value);
+ free (value);
+ }
+
int bb[TABLE_N_AXES][2];
int clip[TABLE_N_AXES][2];
bb[H][0] = clip[H][0] = ofs[H] + page->cp[H][cell->d[H][0] * 2 + 1];
bb[H][1] = clip[H][1] = ofs[H] + page->cp[H][cell->d[H][1] * 2];
- if (render_direction_rtl ())
+ if (page->params->rtl)
{
int temp = bb[H][0];
bb[H][0] = clip[H][0] = render_page_get_size (page, H) - bb[H][1];
bb[V][0] = clip[V][0] = ofs[V] + page->cp[V][cell->d[V][0] * 2 + 1];
bb[V][1] = clip[V][1] = ofs[V] + page->cp[V][cell->d[V][1] * 2];
- of = find_overflow (page, cell->d[H][0], cell->d[V][0]);
- if (of)
+ enum table_valign valign = cell->cell_style->valign;
+ int valign_offset = 0;
+ if (valign != TABLE_VALIGN_TOP)
{
- enum table_axis axis;
-
- for (axis = 0; axis < TABLE_N_AXES; axis++)
+ int height = page->params->ops->measure_cell_height (
+ page->params->aux, cell, bb[H][1] - bb[H][0]);
+ int extra = bb[V][1] - bb[V][0] - height;
+ if (extra > 0)
{
- if (of->overflow[axis][0])
- {
- bb[axis][0] -= of->overflow[axis][0];
- if (cell->d[axis][0] == 0 && !page->is_edge_cutoff[axis][0])
- clip[axis][0] = ofs[axis] + page->cp[axis][cell->d[axis][0] * 2];
- }
- if (of->overflow[axis][1])
- {
- bb[axis][1] += of->overflow[axis][1];
- if (cell->d[axis][1] == page->n[axis] && !page->is_edge_cutoff[axis][1])
- clip[axis][1] = ofs[axis] + page->cp[axis][cell->d[axis][1] * 2 + 1];
- }
+ if (valign == TABLE_VALIGN_CENTER)
+ extra /= 2;
+ valign_offset += extra;
}
}
- page->params->draw_cell (page->params->aux, cell,
- find_footnote_idx (cell, &page->footnotes), bb, clip);
+ const struct render_overflow *of = find_overflow (
+ page, cell->d[H][0], cell->d[V][0]);
+ if (of)
+ for (enum table_axis axis = 0; axis < TABLE_N_AXES; axis++)
+ {
+ if (of->overflow[axis][0])
+ {
+ bb[axis][0] -= of->overflow[axis][0];
+ if (cell->d[axis][0] == 0 && !page->is_edge_cutoff[axis][0])
+ clip[axis][0] = ofs[axis] + page->cp[axis][cell->d[axis][0] * 2];
+ }
+ if (of->overflow[axis][1])
+ {
+ bb[axis][1] += of->overflow[axis][1];
+ if (cell->d[axis][1] == page->n[axis]
+ && !page->is_edge_cutoff[axis][1])
+ clip[axis][1] = ofs[axis] + page->cp[axis][cell->d[axis][1] * 2
+ + 1];
+ }
+ }
+
+ int spill[TABLE_N_AXES][2];
+ for (enum table_axis axis = 0; axis < TABLE_N_AXES; axis++)
+ {
+ spill[axis][0] = rule_width (page, axis, cell->d[axis][0]) / 2;
+ spill[axis][1] = rule_width (page, axis, cell->d[axis][1]) / 2;
+ }
+
+ int color_idx = (cell->d[V][0] < page->h[V]
+ ? 0
+ : (cell->d[V][0] - page->h[V]) & 1);
+ page->params->ops->draw_cell (page->params->aux, cell, color_idx,
+ bb, valign_offset, spill, clip);
}
/* Draws the cells of PAGE indicated in BB. */
render_page_draw_cells (const struct render_page *page,
int ofs[TABLE_N_AXES], int bb[TABLE_N_AXES][2])
{
- int x, y;
+ 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;
- for (y = bb[V][0]; y < bb[V][1]; y++)
- for (x = bb[H][0]; x < bb[H][1]; )
+ 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++)
+ for (int x = bb[H][0]; x < bb[H][1]; x++)
if (is_rule (x) || is_rule (y))
{
int d[TABLE_N_AXES];
d[H] = x;
d[V] = y;
render_rule (page, ofs, d);
- x++;
- }
- else
- {
- struct table_cell cell;
-
- table_get_cell (page->table, 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]);
- table_cell_free (&cell);
}
}
static int
get_clip_min_extent (int x0, const int cp[], int n)
{
- int low, high, best;
-
- low = 0;
- high = n;
- best = 0;
+ int low = 0;
+ int high = n;
+ int best = 0;
while (low < high)
{
int middle = low + (high - low) / 2;
static int
get_clip_max_extent (int x1, const int cp[], int n)
{
- int low, high, best;
-
- low = 0;
- high = n;
- best = n;
+ int low = 0;
+ int high = n;
+ int best = n;
while (low < high)
{
int middle = low + (high - low) / 2;
{
b->page = page;
b->axis = axis;
- b->z = page->h[axis][0];
+ b->z = page->h[axis];
b->pixel = 0;
b->hw = headers_width (page, axis);
}
const struct render_page *page = b->page;
enum table_axis axis = b->axis;
- return page != NULL && b->z < page->n[axis] - page->h[axis][1];
+ return page != NULL && b->z < page->n[axis];
}
/* Returns a new render_page that is up to SIZE pixels wide along B's axis.
const struct render_page *page = b->page;
enum table_axis axis = b->axis;
struct render_page *subpage;
- int z, pixel;
if (!render_break_has_next (b))
return NULL;
- pixel = 0;
- for (z = b->z; z < page->n[axis] - page->h[axis][1]; z++)
+ int pixel = 0;
+ int z;
+ for (z = b->z; z < page->n[axis]; z++)
{
int needed = needed_size (b, z + 1);
if (needed > size)
cell.
This is similar to code for the left side in needed_size(). */
- int rule_allowance = (page->h[axis][1]
- ? 0
- : rule_width (page, axis, z));
+ int rule_allowance = rule_width (page, axis, z);
/* The amount that, if we added cell 'z', the rendering would
overfill the allocated 'size'. */
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,
- find_footnote_idx (&cell, &page->footnotes), 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;
- }
- }
- }
- }
+ if (axis == TABLE_VERT && page->params->ops->adjust_break)
+ for (int x = 0; x < page->n[H];)
+ {
+ struct table_cell cell;
+
+ render_get_cell (page, x, z, &cell);
+ int w = joined_width (page, H, cell.d[H][0], cell.d[H][1]);
+ int better_pixel = page->params->ops->adjust_break (
+ page->params->aux, &cell, w, pixel);
+ x = cell.d[H][1];
+
+ 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;
}
{
const struct render_page *page = b->page;
enum table_axis axis = b->axis;
- int size;
/* Width of left header not including its rightmost rule. */
- size = axis_width (page, axis, 0, rule_ofs (page->h[axis][0]));
+ int size = axis_width (page, axis, 0, rule_ofs (page->h[axis]));
/* If we have a pixel offset and there is no left header, then we omit the
leftmost rule of the body. Otherwise the rendering is deceptive because
rightmost rule in the header and the leftmost rule in the body. We assume
that the width of a merged rule is the larger of the widths of either rule
invidiually. */
- if (b->pixel == 0 || page->h[axis][0])
- size += MAX (rule_width (page, axis, page->h[axis][0]),
+ if (b->pixel == 0 || page->h[axis])
+ size += MAX (rule_width (page, axis, page->h[axis]),
rule_width (page, axis, b->z));
/* Width of body, minus any pixel offset in the leftmost cell. */
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]),
- rule_width (page, axis, cell));
-
- /* Width of right header not including its leftmost rule. */
- size += axis_width (page, axis, rule_ofs_r (page, axis, page->h[axis][1]),
- rule_ofs_r (page, axis, 0));
-
- /* Join crossing. */
- if (page->h[axis][0] && page->h[axis][1])
- size += page->join_crossing[axis][b->z];
+ size += MAX (rule_width_r (page, axis, 0), rule_width (page, axis, cell));
return size;
}
struct render_pager
{
const struct render_params *params;
+ double scale;
- struct render_page **pages;
- size_t n_pages, allocated_pages;
+ struct render_page *page;
- size_t cur_page;
struct render_break x_break;
struct render_break y_break;
};
-static const struct render_page *
-render_pager_add_table (struct render_pager *p, struct table *table)
-{
- struct render_page *page;
-
- if (p->n_pages >= p->allocated_pages)
- p->pages = x2nrealloc (p->pages, &p->allocated_pages, sizeof *p->pages);
- page = p->pages[p->n_pages++] = render_page_create (p->params, table);
- return page;
-}
-
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);
}
-static void
-add_footnote_page (struct render_pager *p, const struct render_page *body)
-{
- const struct table *table = body->table;
- int nc = table_nc (table);
- int nr = table_nr (table);
- int footnote_idx = 0;
- struct tab_table *t;
- int x, y;
-
- if (!body->n_footnotes)
- return;
-
- t = tab_create (2, body->n_footnotes);
- for (y = 0; y < nr; y++)
- for (x = 0; x < nc; )
- {
- struct table_cell cell;
-
- table_get_cell (table, x, y, &cell);
- if (y == cell.d[V][0])
- {
- size_t i;
-
- for (i = 0; i < cell.n_contents; i++)
- {
- const struct cell_contents *cc = &cell.contents[i];
- size_t j;
-
- for (j = 0; j < cc->n_footnotes; j++)
- {
- const char *f = cc->footnotes[j];
-
- tab_text (t, 0, footnote_idx, TAB_LEFT, "");
- tab_footnote (t, 0, footnote_idx, "(none)");
- tab_text (t, 1, footnote_idx, TAB_LEFT, f);
- footnote_idx++;
- }
- }
- }
- x = cell.d[H][1];
- table_cell_free (&cell);
- }
- render_pager_add_table (p, &t->table);
-}
-
-/* Creates and returns a new render_pager for rendering TABLE_ITEM on the
- device with the given PARAMS. */
+/* Creates and returns a new render_pager for rendering PT on the device
+ with the given PARAMS. */
struct render_pager *
render_pager_create (const struct render_params *params,
- const struct table_item *table_item)
+ const struct pivot_table *pt,
+ const size_t *layer_indexes)
{
- const char *caption = table_item_get_caption (table_item);
- const char *title = table_item_get_title (table_item);
- const struct render_page *body_page;
- struct render_pager *p;
+ if (!layer_indexes)
+ layer_indexes = pt->current_layer;
- p = xzalloc (sizeof *p);
- p->params = params;
+ struct table *table = pivot_output_monolithic (pt, layer_indexes,
+ params->printing);
- /* Title. */
- if (title)
- render_pager_add_table (p, table_from_string (TAB_LEFT, title));
+ /* 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 (width > params->size[H])
+ {
+ if (pt->look->shrink_to_fit[H] && params->ops->scale)
+ scale = params->size[H] / (double) width;
+ else
+ {
+ struct render_break b;
+ render_break_init (&b, render_page_ref (page), H);
+ struct render_page *subpage
+ = render_break_next (&b, params->size[H]);
+ width = subpage ? subpage->cp[H][2 * subpage->n[H] + 1] : 0;
+ render_page_unref (subpage);
+ render_break_destroy (&b);
+ }
+ }
- /* Body. */
- body_page = render_pager_add_table (p, table_ref (table_item_get_table (
- table_item)));
+ /* Create the pager. */
+ struct render_pager *p = xmalloc (sizeof *p);
+ *p = (struct render_pager) { .params = params, .scale = scale, .page = page };
- /* Caption. */
- if (caption)
- render_pager_add_table (p, table_from_string (TAB_LEFT, caption));
+ /* If we're shrinking tables to fit the page length, then adjust the scale
+ factor.
- /* Footnotes. */
- add_footnote_page (p, body_page);
+ XXX This will sometimes shrink more than needed, because adjusting the
+ scale factor allows for cells to be "wider", which means that sometimes
+ they won't break across as much vertical space, thus shrinking the table
+ vertically more than the scale would imply. Shrinking only as much as
+ necessary would require an iterative search. */
+ if (pt->look->shrink_to_fit[V] && params->ops->scale)
+ {
+ 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);
{
if (p)
{
- size_t i;
-
render_break_destroy (&p->x_break);
render_break_destroy (&p->y_break);
- for (i = 0; i < p->n_pages; i++)
- render_page_unref (p->pages[i]);
- free (p->pages);
+ render_page_unref (p->page);
free (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 (&p->y_break,
- render_break_next (&p->x_break, p->params->size[H]), V);
+ render_break_init (
+ &p->y_break, render_break_next (&p->x_break,
+ p->params->size[H] / p->scale), V);
}
return true;
}
int
render_pager_draw_next (struct render_pager *p, int space)
{
+ if (p->scale != 1.0)
+ {
+ p->params->ops->scale (p->params->aux, p->scale);
+ space /= p->scale;
+ }
+
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))
{
- struct render_page *page;
-
- 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)
+ {
+ render_page_draw (page, ofs);
+ ofs[V] += render_page_get_size (page, V);
+ render_page_unref (page);
+ }
+ }
- page = render_break_next (&p->y_break, space - ofs[V]);
- if (!page)
- break;
+ if (p->scale != 1.0)
+ ofs[V] *= p->scale;
- render_page_draw (page, ofs);
- ofs[V] += render_page_get_size (page, V);
- render_page_unref (page);
- }
return ofs[V];
}
{
int ofs[TABLE_N_AXES] = { 0, 0 };
int clip[TABLE_N_AXES][2];
- size_t i;
clip[H][0] = x;
clip[H][1] = x + w;
- for (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
int
render_pager_get_size (const struct render_pager *p, enum table_axis axis)
{
- int size = 0;
- size_t i;
-
- for (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. */
render_page_select (const struct render_page *page, enum table_axis axis,
int z0, int p0, int z1, int p1)
{
- const struct render_footnote *f;
- struct render_page_selection s;
enum table_axis a = axis;
enum table_axis b = !a;
- struct render_page *subpage;
- struct render_overflow *ro;
- int *dcp, *scp;
- int *jc;
- int z;
-
/* Optimize case where all of PAGE is selected by just incrementing the
reference count. */
- if (z0 == page->h[a][0] && p0 == 0
- && z1 == page->n[a] - page->h[a][1] && p1 == 0)
+ if (z0 == page->h[a] && p0 == 0 && z1 == page->n[a] && p1 == 0)
{
struct render_page *page_rw = CONST_CAST (struct render_page *, page);
page_rw->ref_cnt++;
}
/* Allocate subpage. */
- subpage = render_page_allocate (page->params,
- table_select_slice (
- table_ref (page->table),
- a, z0, z1, true));
+ int trim[2] = { z0 - page->h[a], page->n[a] - z1 };
+
+ int n[TABLE_N_AXES] = { [H] = page->n[H], [V] = page->n[V] };
+ n[a] -= trim[0] + trim[1];
+
+ int r[TABLE_N_AXES][2];
+ for (enum table_axis k = 0; k < TABLE_N_AXES; k++)
+ {
+ r[k][0] = page->r[k][0];
+ r[k][1] = page->r[k][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. */
subpage->is_edge_cutoff[a][0] =
- subpage->h[a][0] == 0 && (p0 || (z0 == 0 && page->is_edge_cutoff[a][0]));
+ subpage->h[a] == 0 && (p0 || (z0 == 0 && page->is_edge_cutoff[a][0]));
subpage->is_edge_cutoff[a][1] =
- subpage->h[a][1] == 0 && (p1 || (z1 == page->n[a]
- && page->is_edge_cutoff[a][1]));
+ p1 || (z1 == page->n[a] && page->is_edge_cutoff[a][1]);
subpage->is_edge_cutoff[b][0] = page->is_edge_cutoff[b][0];
subpage->is_edge_cutoff[b][1] = page->is_edge_cutoff[b][1];
- /* Select join crossings from PAGE into subpage. */
- jc = subpage->join_crossing[a];
- for (z = 0; z < page->h[a][0]; z++)
- *jc++ = page->join_crossing[a][z];
- for (z = z0; z <= z1; z++)
- *jc++ = page->join_crossing[a][z];
- for (z = page->n[a] - page->h[a][1]; z < page->n[a]; z++)
- *jc++ = page->join_crossing[a][z];
- assert (jc == &subpage->join_crossing[a][subpage->n[a] + 1]);
-
- memcpy (subpage->join_crossing[b], page->join_crossing[b],
- (subpage->n[b] + 1) * sizeof **subpage->join_crossing);
-
/* Select widths from PAGE into subpage. */
- scp = page->cp[a];
- dcp = subpage->cp[a];
+ int *scp = page->cp[a];
+ int *dcp = subpage->cp[a];
*dcp = 0;
- for (z = 0; z <= rule_ofs (subpage->h[a][0]); z++, dcp++)
+ for (int z = 0; z <= rule_ofs (subpage->h[a]); z++, dcp++)
{
- if (z == 0 && subpage->is_edge_cutoff[a][0])
- dcp[1] = dcp[0];
- else
- dcp[1] = dcp[0] + (scp[z + 1] - scp[z]);
+ int w = !z && subpage->is_edge_cutoff[a][0] ? 0 : scp[z + 1] - scp[z];
+ dcp[1] = dcp[0] + w;
}
- for (z = cell_ofs (z0); z <= cell_ofs (z1 - 1); z++, dcp++)
+ for (int z = cell_ofs (z0); z <= cell_ofs (z1 - 1); z++, dcp++)
{
dcp[1] = dcp[0] + (scp[z + 1] - scp[z]);
if (z == cell_ofs (z0))
- {
- dcp[1] -= p0;
- if (page->h[a][0] && page->h[a][1])
- dcp[1] += page->join_crossing[a][z / 2];
- }
+ dcp[1] -= p0;
if (z == cell_ofs (z1 - 1))
dcp[1] -= p1;
}
- for (z = rule_ofs_r (page, a, subpage->h[a][1]);
+ for (int z = rule_ofs_r (page, a, 0);
z <= rule_ofs_r (page, a, 0); z++, dcp++)
{
if (z == rule_ofs_r (page, a, 0) && subpage->is_edge_cutoff[a][1])
}
assert (dcp == &subpage->cp[a][2 * subpage->n[a] + 1]);
- for (z = 0; z < page->n[b] * 2 + 2; z++)
+ for (int z = 0; z < page->n[b] * 2 + 2; z++)
subpage->cp[b][z] = page->cp[b][z];
/* Add new overflows. */
- s.page = page;
- s.a = a;
- s.b = b;
- s.z0 = z0;
- s.z1 = z1;
- s.p0 = p0;
- s.p1 = p1;
- s.subpage = subpage;
-
- if (!page->h[a][0] || z0 > page->h[a][0] || p0)
- for (z = 0; z < page->n[b]; )
+ struct render_page_selection s = {
+ .page = page,
+ .a = a,
+ .b = b,
+ .z0 = z0,
+ .z1 = z1,
+ .p0 = p0,
+ .p1 = p1,
+ .subpage = subpage,
+ };
+
+ if (!page->h[a] || z0 > page->h[a] || p0)
+ for (int z = 0; z < page->n[b];)
{
- struct table_cell cell;
int d[TABLE_N_AXES];
- bool overflow0;
- bool overflow1;
-
d[a] = z0;
d[b] = z;
- table_get_cell (page->table, d[H], d[V], &cell);
- overflow0 = p0 || cell.d[a][0] < z0;
- overflow1 = cell.d[a][1] > z1 || (cell.d[a][1] == z1 && p1);
+ struct table_cell cell;
+ render_get_cell (page, d[H], d[V], &cell);
+ bool overflow0 = p0 || cell.d[a][0] < z0;
+ bool overflow1 = cell.d[a][1] > z1 || (cell.d[a][1] == z1 && p1);
if (overflow0 || overflow1)
{
- ro = insert_overflow (&s, &cell);
+ struct render_overflow *ro = insert_overflow (&s, &cell);
if (overflow0)
- {
- ro->overflow[a][0] += p0 + axis_width (
- page, a, cell_ofs (cell.d[a][0]), cell_ofs (z0));
- if (page->h[a][0] && page->h[a][1])
- ro->overflow[a][0] -= page->join_crossing[a][cell.d[a][0]
- + 1];
- }
+ ro->overflow[a][0] += p0 + axis_width (
+ page, a, cell_ofs (cell.d[a][0]), cell_ofs (z0));
if (overflow1)
- {
- ro->overflow[a][1] += p1 + axis_width (
- page, a, cell_ofs (z1), cell_ofs (cell.d[a][1]));
- if (page->h[a][0] && page->h[a][1])
- ro->overflow[a][1] -= page->join_crossing[a][cell.d[a][1]];
- }
+ ro->overflow[a][1] += p1 + axis_width (
+ page, a, cell_ofs (z1), cell_ofs (cell.d[a][1]));
}
z = cell.d[b][1];
- table_cell_free (&cell);
}
- if (!page->h[a][1] || z1 < page->n[a] - page->h[a][1] || p1)
- for (z = 0; z < page->n[b]; )
- {
- struct table_cell cell;
- int d[TABLE_N_AXES];
+ for (int z = 0; z < page->n[b];)
+ {
+ int d[TABLE_N_AXES];
+ d[a] = z1 - 1;
+ d[b] = z;
- d[a] = z1 - 1;
- d[b] = z;
- table_get_cell (page->table, d[H], d[V], &cell);
- if ((cell.d[a][1] > z1 || (cell.d[a][1] == z1 && p1))
- && find_overflow_for_cell (&s, &cell) == NULL)
- {
- ro = insert_overflow (&s, &cell);
- ro->overflow[a][1] += p1 + axis_width (page, a, cell_ofs (z1),
- cell_ofs (cell.d[a][1]));
- }
- z = cell.d[b][1];
- table_cell_free (&cell);
- }
+ struct table_cell cell;
+ render_get_cell (page, d[H], d[V], &cell);
+ if ((cell.d[a][1] > z1 || (cell.d[a][1] == z1 && p1))
+ && find_overflow_for_cell (&s, &cell) == NULL)
+ {
+ struct render_overflow *ro = insert_overflow (&s, &cell);
+ ro->overflow[a][1] += p1 + axis_width (page, a, cell_ofs (z1),
+ cell_ofs (cell.d[a][1]));
+ }
+ z = cell.d[b][1];
+ }
/* Copy overflows from PAGE into subpage. */
+ struct render_overflow *ro;
HMAP_FOR_EACH (ro, struct render_overflow, node, &page->overflows)
{
struct table_cell cell;
if (cell.d[a][1] > z0 && cell.d[a][0] < z1
&& find_overflow_for_cell (&s, &cell) == NULL)
insert_overflow (&s, &cell);
- table_cell_free (&cell);
}
- /* Copy footnotes from PAGE into subpage. */
- HMAP_FOR_EACH (f, struct render_footnote, node, &page->footnotes)
- if ((f->d[a][0] >= z0 && f->d[a][0] < z1)
- || (f->d[a][1] - 1 >= z0 && f->d[a][1] - 1 < z1))
- {
- struct render_footnote *nf = xmalloc (sizeof *nf);
- nf->d[a][0] = MAX (z0, f->d[a][0]) - z0 + page->h[a][0];
- nf->d[a][1] = MIN (z1, f->d[a][1]) - z0 + page->h[a][0];
- nf->d[b][0] = f->d[b][0];
- nf->d[b][1] = f->d[b][1];
- nf->idx = f->idx;
- hmap_insert (&subpage->footnotes, &nf->node,
- hash_cell (nf->d[H][0], nf->d[V][0]));
- }
-
return subpage;
}
{
enum table_axis a = s->a;
enum table_axis b = s->b;
- int ha0 = s->subpage->h[a][0];
+ int ha0 = s->subpage->h[a];
subcell[a] = MAX (cell->d[a][0] - s->z0 + ha0, ha0);
subcell[b] = cell->d[b][0];
insert_overflow (struct render_page_selection *s,
const struct table_cell *cell)
{
- const struct render_overflow *old;
- struct render_overflow *of;
-
- of = xzalloc (sizeof *of);
+ struct render_overflow *of = XZALLOC (struct render_overflow);
cell_to_subpage (s, cell, of->d);
hmap_insert (&s->subpage->overflows, &of->node,
hash_cell (of->d[H], of->d[V]));
- old = find_overflow (s->page, cell->d[H][0], cell->d[V][0]);
+ const struct render_overflow *old
+ = find_overflow (s->page, cell->d[H][0], cell->d[V][0]);
if (old != NULL)
memcpy (of->overflow, old->overflow, sizeof of->overflow);