#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/table-item.h"
#include "output/table.h"
#include "gl/minmax.h"
/* Region of 'table' to render.
- The horizontal cells rendered are the leftmost h[H][0], then
- r[H][0] through r[H][1], exclusive, then the rightmost h[H][1].
+ 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][0], then r[V][0]
- through r[V][1], exclusive, then the bottommost h[V][1].
+ The vertical cells rendered are the topmost h[V], then r[V][0]
+ through r[V][1].
- 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]
+ 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][2];
+ int h[TABLE_N_AXES];
int r[TABLE_N_AXES][2];
int n[TABLE_N_AXES];
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. */
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 *,
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 x0 = page->h[axis];
+ int x1 = page->n[axis];
int max = 0;
for (int x = x0; x < x1; x++)
\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 TABLE_STROKE_NONE:
- return RENDER_LINE_NONE;
- case TABLE_STROKE_SOLID:
- return RENDER_LINE_SINGLE;
- case TABLE_STROKE_DASHED:
- return RENDER_LINE_DASHED;
- case TABLE_STROKE_THICK:
- return RENDER_LINE_THICK;
- case TABLE_STROKE_THIN:
- return RENDER_LINE_THIN;
- case TABLE_STROKE_DOUBLE:
- 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
/* 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. */
- struct cell_color color;
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], &color);
+ rules |= 1u << table_get_rule (table, a, d[H], d[V]).stroke;
/* 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
int width = 0;
for (size_t i = 0; i < TABLE_N_STROKES; i++)
if (rules & (1u << i))
- width = MAX (width, params->line_widths[rule_to_render_type (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. 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],
+ 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];
-
- for (int i = 0; i < TABLE_N_AXES; i++)
- {
- page->cp[i] = xmalloc ((2 * n[i] + 2) * sizeof *page->cp[i]);
- page->join_crossing[i] = xzalloc ((n[i] + 1)
- * sizeof *page->join_crossing[i]);
- }
-
- hmap_init (&page->overflows);
- memset (page->is_edge_cutoff, 0, sizeof page->is_edge_cutoff);
-
+ *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;
}
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];
+ 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] = table->h[a];
+ r[a][0] = table->h[a];
+ r[a][1] = table->n[a];
}
- return page;
+ return render_page_allocate__ (params, table, table->n, h, r);
}
/* Allocates and returns a new render_page for PARAMS and TABLE, initializing
return page;
}
\f
-static void
-set_join_crossings (struct render_page *page, enum table_axis axis,
- const struct table_cell *cell, int *rules)
-{
- for (int z = cell->d[axis][0] + 1; z <= cell->d[axis][1] - 1; z++)
- 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. */
+ the horizontal or vertical dimension. */
struct map
{
int p0; /* First ordinate in the page. */
get_map (const struct render_page *page, enum table_axis a, int z,
struct map *m)
{
- if (z < page->h[a][0])
+ if (z < page->h[a])
{
m->p0 = 0;
m->t0 = 0;
- m->n = page->h[a][0];
+ m->n = page->h[a];
}
- else if (z < page->n[a] - page->h[a][1])
+ else
{
- m->p0 = page->h[a][0];
+ 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];
}
- 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];
- }
}
/* Initializes CELL with the contents of the table cell at column X and row Y
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
{
enum { MIN, MAX };
- int nc = table_nc (table);
- int nr = table_nr (table);
+ int nc = table->n[H];
+ int nr = table->n[V];
/* Figure out rule widths. */
int *rules[TABLE_N_AXES];
span multiple columns. */
struct render_row *columns[2];
for (int i = 0; i < 2; i++)
- columns[i] = xzalloc (nc * sizeof *columns[i]);
+ columns[i] = xcalloc (nc, sizeof *columns[i]);
for (int y = 0; y < nr; y++)
for (int x = 0; x < nc;)
{
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], table_cell_colspan (&cell));
+ 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];
}
/* Decide final column widths. */
int table_widths[2];
for (int i = 0; i < 2; i++)
- table_widths[i] = calculate_table_width (table_nc (table),
+ table_widths[i] = calculate_table_width (table->n[H],
columns[i], rules[H]);
struct render_page *page;
}
/* Calculate heights of cells that do not span multiple rows. */
- struct render_row *rows = xzalloc (nr * sizeof *rows);
+ struct render_row *rows = XCALLOC (nr, struct render_row);
for (int y = 0; y < nr; y++)
for (int x = 0; x < nc;)
{
struct table_cell cell;
render_get_cell (page, x, y, &cell);
- if (y == cell.d[V][0])
+ if (y == cell.d[V][0] && table_cell_rowspan (&cell) == 1)
{
- if (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;
- }
- else
- set_join_crossings (page, V, &cell, rules[V]);
-
- if (table_cell_colspan (&cell) > 1)
- set_join_crossings (page, H, &cell, rules[H]);
+ 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];
}
{
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);
- distribute_spanned_width (h, &rows[cell.d[V][0]], rules[V],
+ distribute_spanned_width (h,
+ &rows[cell.d[V][0]],
+ &rules[V][cell.d[V][0]],
table_cell_rowspan (&cell));
}
x = cell.d[H][1];
if (hw * 2 >= page->params->size[axis]
|| hw + max_cell_width (page, axis) > page->params->size[axis])
{
- 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];
}
table_unref (page->table);
for (int i = 0; i < TABLE_N_AXES; ++i)
- {
- free (page->join_crossing[i]);
- free (page->cp[i]);
- }
+ free (page->cp[i]);
free (page);
}
\f
/* Drawing render_pages. */
-/* This is like table_get_rule() except:
-
- - D is in terms of the page's rows and column rather than the underlying
- table's.
-
- - The result is in the form of a render_line_style. */
-static 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], struct cell_color *color)
+ const int d_[TABLE_N_AXES])
{
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][0])
+ if (d[a] < page->h[a])
/* Nothing to do */;
- else if (d[a] <= page->n[a] - page->h[a][1])
+ else if (d[a] <= page->n[a])
{
- if (page->h[a][0] && d[a] == page->h[a][0])
- d2 = page->h[a][0];
- else if (page->h[a][1] && d[a] == page->n[a] - page->h[a][1])
- d2 = page->table->n[a] - page->h[a][1];
- d[a] += page->r[a][0] - page->h[a][0];
+ if (page->h[a] && d[a] == page->h[a])
+ d2 = page->h[a];
+ d[a] += page->r[a][0] - page->h[a];
}
- else
- d[a] += ((page->table->n[a] - page->table->h[a][1])
- - (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;
- int r = table_get_rule (page->table, axis, d[H], d[V], color);
+ struct table_border_style border
+ = table_get_rule (page->table, axis, d[H], d[V]);
if (d2 >= 0)
{
d[a] = d2;
- int r2 = table_get_rule (page->table, axis, d[H], d[V], color);
- r = table_stroke_combine (r, r2);
+ 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 rule_to_render_type (r);
+ return border;
}
static bool
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];
- struct cell_color colors[TABLE_N_AXES][2];
+ const struct table_border_style none = { .stroke = TABLE_STROKE_NONE };
+ struct table_border_style styles[TABLE_N_AXES][2];
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[H] = d[H];
e[V] = d[V];
e[b]--;
- styles[a][0] = get_rule (page, a, e, &colors[a][0]);
+ styles[a][0] = get_rule (page, a, e);
}
+ else
+ styles[a][0] = none;
if (d[b] / 2 < page->n[b])
- styles[a][1] = get_rule (page, a, d, &colors[a][1]);
+ 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, &colors[a][0]);
- colors[a][1] = colors[a][0];
- }
+ 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[V][0] = ofs[V] + page->cp[V][d[V]];
bb[V][1] = ofs[V] + page->cp[V][d[V] + 1];
- page->params->ops->draw_line (page->params->aux, bb, styles, colors);
+ 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 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[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];
- enum table_valign valign = cell->style->cell_style.valign;
+ enum table_valign valign = cell->cell_style->valign;
int valign_offset = 0;
if (valign != TABLE_VALIGN_TOP)
{
spill[axis][1] = rule_width (page, axis, cell->d[axis][1]) / 2;
}
- int color_idx = (cell->d[V][0] < page->h[V][0]
- || page->n[V] - (cell->d[V][0] + 1) < page->h[V][1]
+ int color_idx = (cell->d[V][0] < page->h[V]
? 0
- : (cell->d[V][0] - page->h[V][0]) & 1);
+ : (cell->d[V][0] - page->h[V]) & 1);
page->params->ops->draw_cell (page->params->aux, cell, color_idx,
bb, valign_offset, spill, clip);
}
{
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.
int pixel = 0;
int z;
- for (z = b->z; z < page->n[axis] - page->h[axis][1]; 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'. */
enum table_axis axis = b->axis;
/* Width of left header not including its rightmost rule. */
- int 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;
- /* An array of "render_page"s to be rendered, in order, vertically. From
- the user's perspective, there's only one table per render_pager, but the
- implementation treats the title, table body, caption, footnotes,
- etc. each as a table, and that's why we have an array here. */
- 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,
- int min_width)
-{
- if (p->n_pages >= p->allocated_pages)
- p->pages = x2nrealloc (p->pages, &p->allocated_pages, sizeof *p->pages);
-
- struct render_page *page = render_page_create (p->params, table, min_width);
- p->pages[p->n_pages++] = page;
- 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 table_item *item)
+/* 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 pivot_table *pt,
+ const size_t *layer_indexes)
{
- const struct footnote **f;
- size_t n_footnotes = table_collect_footnotes (item, &f);
- if (!n_footnotes)
- return;
+ if (!layer_indexes)
+ layer_indexes = pt->current_layer;
- struct table *t = table_create (1, n_footnotes, 0, 0, 0, 0);
+ struct table *table = pivot_output_monolithic (pt, layer_indexes,
+ params->printing);
- for (size_t i = 0; i < n_footnotes; i++)
+ /* 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])
{
- table_text_format (t, 0, i, 0, "%s. %s", f[i]->marker, f[i]->content);
- table_add_style (t, 0, i, f[i]->style);
+ 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);
+ }
}
- render_pager_add_table (p, t, 0);
- free (f);
-}
+ /* Create the pager. */
+ struct render_pager *p = xmalloc (sizeof *p);
+ *p = (struct render_pager) { .params = params, .scale = scale, .page = page };
-static void
-add_text_page (struct render_pager *p, const struct table_item_text *t,
- int min_width)
-{
- if (!t)
- return;
+ /* If we're shrinking tables to fit the page length, then adjust the scale
+ factor.
- struct table *tab = table_create (1, 1, 0, 0, 0, 0);
- table_text (tab, 0, 0, 0, t->content);
- for (size_t i = 0; i < t->n_footnotes; i++)
- table_add_footnote (tab, 0, 0, t->footnotes[i]);
- if (t->style)
- tab->styles[0] = table_area_style_clone (tab->container, t->style);
- render_pager_add_table (p, tab, min_width);
-}
-
-static void
-add_layers_page (struct render_pager *p,
- const struct table_item_layers *layers, int min_width)
-{
- if (!layers)
- return;
-
- struct table *tab = table_create (1, layers->n_layers, 0, 0, 0, 0);
- for (size_t i = 0; i < layers->n_layers; i++)
+ 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)
{
- const struct table_item_layer *layer = &layers->layers[i];
- table_text (tab, 0, i, 0, layer->content);
- for (size_t j = 0; j < layer->n_footnotes; j++)
- table_add_footnote (tab, 0, i, layer->footnotes[j]);
+ double height = table_width (p->page, V);
+ if (height * p->scale >= params->size[V])
+ p->scale *= params->size[V] / height;
}
- if (layers->style)
- tab->styles[0] = table_area_style_clone (tab->container, layers->style);
- render_pager_add_table (p, tab, min_width);
-}
-
-/* Creates and returns a new render_pager for rendering TABLE_ITEM 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 table *table = table_item_get_table (table_item);
-
- struct render_pager *p = xzalloc (sizeof *p);
- p->params = params;
-
- struct render_page *page = render_page_create (params, table_ref (table), 0);
- struct render_break b;
- render_break_init (&b, page, H);
- struct render_page *subpage = render_break_next (&b, p->params->size[H]);
- int title_width = subpage ? subpage->cp[H][2 * subpage->n[H] + 1] : 0;
- render_page_unref (subpage);
- render_break_destroy (&b);
-
- /* Title. */
- add_text_page (p, table_item_get_title (table_item), title_width);
-
- /* Layers. */
- add_layers_page (p, table_item_get_layers (table_item), title_width);
-
- /* Body. */
- render_pager_add_table (p, table_ref (table_item_get_table (table_item)), 0);
-
- /* Caption. */
- add_text_page (p, table_item_get_caption (table_item), 0);
-
- /* Footnotes. */
- add_footnote_page (p, table_item);
render_pager_start_page (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]);
- 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);
+ &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))
{
- 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)
+ ofs[V] *= p->scale;
+
return ofs[V];
}
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
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. */
/* 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. */
- int trim[2] = { z0 - page->h[a][0], (page->n[a] - page->h[a][1]) - z1 };
+ 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];
- 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. */
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. */
- int *jc = subpage->join_crossing[a];
- for (int z = 0; z < page->h[a][0]; z++)
- *jc++ = page->join_crossing[a][z];
- for (int z = z0; z <= z1; z++)
- *jc++ = page->join_crossing[a][z];
- for (int 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. */
int *scp = page->cp[a];
int *dcp = subpage->cp[a];
*dcp = 0;
- for (int z = 0; z <= rule_ofs (subpage->h[a][0]); z++, dcp++)
+ for (int z = 0; z <= rule_ofs (subpage->h[a]); z++, dcp++)
{
int w = !z && subpage->is_edge_cutoff[a][0] ? 0 : scp[z + 1] - scp[z];
dcp[1] = dcp[0] + w;
{
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 (int 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])
.subpage = subpage,
};
- if (!page->h[a][0] || z0 > page->h[a][0] || p0)
+ if (!page->h[a] || z0 > page->h[a] || p0)
for (int z = 0; z < page->n[b];)
{
int d[TABLE_N_AXES];
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];
}
- if (!page->h[a][1] || z1 < page->n[a] - page->h[a][1] || p1)
- for (int z = 0; z < page->n[b];)
- {
- int d[TABLE_N_AXES];
- d[a] = z1 - 1;
- d[b] = z;
+ for (int z = 0; z < page->n[b];)
+ {
+ int d[TABLE_N_AXES];
+ d[a] = z1 - 1;
+ d[b] = z;
- 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];
- }
+ 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;
{
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)
{
- struct render_overflow *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]));