/* PSPP - a program for statistical analysis.
- Copyright (C) 2009, 2010, 2011, 2013, 2014 Free Software Foundation, Inc.
+ Copyright (C) 2009, 2010, 2011, 2013, 2014, 2016 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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.
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
};
static struct render_page *render_page_create (const struct render_params *,
- const struct table *);
+ struct table *);
struct render_page *render_page_ref (const struct render_page *page_);
static void render_page_unref (struct render_page *);
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
distribute_spanned_width (int width,
struct render_row *rows, const int *rules, int n)
{
- int total_unspanned;
- double w, d0, d1, d;
- int x;
-
/* Sum up the unspanned widths of the N rows for use as weights. */
- total_unspanned = 0;
- for (x = 0; x < n; x++)
+ int total_unspanned = 0;
+ for (int x = 0; x < n; x++)
total_unspanned += rows[x].unspanned;
- for (x = 0; x < n - 1; x++)
+ for (int x = 0; x < n - 1; x++)
total_unspanned += rules[x + 1];
if (total_unspanned >= width)
return;
unspanned weights when 'total_unspanned' is 0 (because that would cause a
division by zero).
- This implementation uses floating-point types and operators, but all the
- values involved are integers. For integers smaller than 53 bits, this
- should not lose any precision, and it should degrade gracefully for larger
- values.
-
The calculation we want to do is this:
w0 = width / n
the rule on the right. That way each rule contributes to both the cell on
its left and on its right.)
*/
- d0 = n;
- d1 = 2.0 * (total_unspanned > 0 ? total_unspanned : 1.0);
- d = d0 * d1;
+ long long int d0 = n;
+ long long int d1 = 2LL * MAX (total_unspanned, 1);
+ long long int d = d0 * d1;
if (total_unspanned > 0)
- d *= 2.0;
- w = floor (d / 2.0);
- for (x = 0; x < n; x++)
+ d *= 2;
+ long long int w = d / 2;
+ for (int x = 0; x < n; x++)
{
w += width * d1;
if (total_unspanned > 0)
{
- double unspanned = rows[x].unspanned * 2.0;
+ long long int unspanned = rows[x].unspanned * 2LL;
if (x < n - 1)
unspanned += rules[x + 1];
if (x > 0)
{
switch (type)
{
- case TAL_0:
- case TAL_GAP:
+ case TAL_NONE:
return RENDER_LINE_NONE;
- case TAL_1:
+ case TAL_SOLID:
return RENDER_LINE_SINGLE;
- case TAL_2:
+ case TAL_DASHED:
+ return RENDER_LINE_DASHED;
+ case TAL_THICK:
+ return RENDER_LINE_THICK;
+ case TAL_THIN:
+ return RENDER_LINE_THIN;
+ case TAL_DOUBLE:
return RENDER_LINE_DOUBLE;
default:
NOT_REACHED ();
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. */
for (d[b] = 0; d[b] < table->n[b]; d[b]++)
rules |= 1u << table_get_rule (table, a, d[H], d[V]);
+ /* 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_NONE))
+ {
+ rules &= ~(1u << TAL_NONE);
+ if (z > 0 && z < table->n[a] && !params->supports_margins && a == H)
+ rules |= 1u << TAL_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_GAP)))
- 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 < N_LINES; i++)
+ if (rules & (1u << i))
+ width = MAX (width, params->line_widths[a][rule_to_render_type (i)]);
return width;
}
}
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;
const struct render_row *rows_max,
int w_min, int w_max, const int *rules)
{
- /* This implementation uses floating-point types and operators, but all the
- values involved are integers. For integers smaller than 53 bits, this
- should not lose any precision, and it should degrade gracefully for larger
- values. */
const int n = table->n[H];
- const double avail = params->size[H] - w_min;
- const double wanted = w_max - w_min;
- struct render_page *page;
- double w;
- int *cph;
- int x;
+ const long long int avail = params->size[H] - w_min;
+ const long long int wanted = w_max - w_min;
assert (wanted > 0);
- page = render_page_allocate (params, table);
+ struct render_page *page = render_page_allocate (params, table);
- cph = page->cp[H];
+ int *cph = page->cp[H];
*cph = 0;
- w = (int) wanted / 2;
- for (x = 0; x < n; x++)
+ long long int w = wanted / 2;
+ for (int x = 0; x < n; x++)
{
- int extra;
-
w += avail * (rows_max[x].width - rows_min[x].width);
- extra = w / wanted;
+ int extra = w / wanted;
w -= extra * wanted;
cph[1] = cph[0] + rules[x];
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,
- const struct table *table_)
+render_page_create (const struct render_params *params, struct table *table)
{
struct render_page *page;
- struct table *table;
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;
- table = table_ref (table_);
nc = table_nc (table);
nr = table_nr (table);
}
/* Calculate minimum and maximum widths of cells that do not
- span multiple columns. Assign footnote markers. */
- hmap_init (&footnotes);
- footnote_idx = 0;
+ span multiple columns. */
for (i = 0; i < 2; i++)
columns[i] = xzalloc (nc * sizeof *columns[i]);
for (y = 0; y < nr; y++)
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,
+ params->measure_cell_width (params->aux, &cell,
&w[MIN], &w[MAX]);
for (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);
{
int w[2];
- params->measure_cell_width (params->aux, &cell,
- find_footnote_idx (&cell, &footnotes),
- &w[MIN], &w[MAX]);
+ params->measure_cell_width (params->aux, &cell, &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));
table_cell_free (&cell);
}
+ /* 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++)
+ 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),
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);
+ int h = params->measure_cell_height (params->aux, &cell, w);
if (h > r->unspanned)
r->unspanned = r->width = h;
}
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);
+ int h = params->measure_cell_height (params->aux, &cell, w);
distribute_spanned_width (h, &rows[cell.d[V][0]], rules[V],
table_cell_rowspan (&cell));
}
}
}
- hmap_swap (&page->footnotes, &footnotes);
- hmap_destroy (&footnotes);
- page->n_footnotes = footnote_idx;
-
free (rules[H]);
free (rules[V]);
hmap_destroy (&page->overflows);
table_unref (page->table);
-
+
for (i = 0; i < TABLE_N_AXES; ++i)
{
free (page->join_crossing[i]);
bool
render_direction_rtl (void)
{
- /* TRANSLATORS: Do not translate this string. If the script of your language
- reads from right to left (eg Persian, Arabic, Hebrew etc), then replace
- this string with "output-direction-rtl". Otherwise either leave it
+ /* TRANSLATORS: Do not translate this string. If the script of your language
+ reads from right to left (eg Persian, Arabic, Hebrew etc), then replace
+ 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 (render_direction_rtl ())
{
int temp = bb[H][0];
- bb[H][0] = page->params->size[H] - bb[H][1];
- bb[H][1] = page->params->size[H] - temp;
+ bb[H][0] = render_page_get_size (page, H) - bb[H][1];
+ bb[H][1] = render_page_get_size (page, H) - temp;
}
bb[V][0] = ofs[V] + page->cp[V][d[V]];
bb[V][1] = ofs[V] + page->cp[V][d[V] + 1];
if (render_direction_rtl ())
{
int temp = bb[H][0];
- bb[H][0] = clip[H][0] = page->params->size[H] - bb[H][1];
- bb[H][1] = clip[H][1] = page->params->size[H] - temp;
+ bb[H][0] = clip[H][0] = render_page_get_size (page, H) - bb[H][1];
+ bb[H][1] = clip[H][1] = render_page_get_size (page, H) - temp;
}
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];
+ int valign = (cell->n_contents
+ ? cell->contents->options & TAB_VALIGN
+ : TAB_TOP);
+ if (valign != TAB_TOP)
+ {
+ int height = page->params->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 (valign == TAB_MIDDLE)
+ extra /= 2;
+ bb[V][0] += extra;
+ }
+ }
+
of = find_overflow (page, cell->d[H][0], cell->d[V][0]);
if (of)
{
}
}
- page->params->draw_cell (page->params->aux, cell,
- find_footnote_idx (cell, &page->footnotes), bb, clip);
+ page->params->draw_cell (page->params->aux, cell, bb, clip);
}
/* Draws the cells of PAGE indicated in BB. */
int z0, int p0,
int z1, int p1);
-/* Initializes render_break B for breaking PAGE along AXIS. */
+/* Initializes render_break B for breaking PAGE along AXIS.
+ Takes ownership of PAGE. */
static void
-render_break_init (struct render_break *b, const struct render_page *page,
+render_break_init (struct render_break *b, struct render_page *page,
enum table_axis axis)
{
- b->page = render_page_ref (page);
+ b->page = page;
b->axis = axis;
b->z = page->h[axis][0];
b->pixel = 0;
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);
+ page->params->aux, &cell, w, pixel);
x = cell.d[H][1];
table_cell_free (&cell);
static void
render_pager_start_page (struct render_pager *p)
{
- render_break_init (&p->x_break, p->pages[p->cur_page++], H);
+ render_break_init (&p->x_break, render_page_ref (p->pages[p->cur_page++]),
+ H);
render_break_init_empty (&p->y_break);
}
static void
-add_footnote_page (struct render_pager *p, const struct render_page *body)
+add_footnote_page (struct render_pager *p, const struct table_item *item)
{
- 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)
+ const struct footnote **f;
+ size_t n_footnotes = table_collect_footnotes (item, &f);
+ if (!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;
+ struct tab_table *t = tab_create (2, n_footnotes);
- 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);
+ for (size_t i = 0; i < n_footnotes; i++)
+ if (f[i])
+ {
+ tab_text_format (t, 0, i, TAB_LEFT, "%s.", f[i]->marker);
+ tab_text (t, 1, i, TAB_LEFT, f[i]->content);
}
render_pager_add_table (p, &t->table);
+
+ free (f);
+}
+
+static void
+add_text_page (struct render_pager *p, const struct table_item_text *t)
+{
+ if (!t)
+ return;
+
+ struct tab_table *tab = tab_create (1, 1);
+ tab_text (tab, 0, 0, TAB_LEFT, t->content);
+ for (size_t i = 0; i < t->n_footnotes; i++)
+ tab_add_footnote (tab, 0, 0, t->footnotes[i]);
+ render_pager_add_table (p, &tab->table);
}
/* Creates and returns a new render_pager for rendering TABLE_ITEM on the
render_pager_create (const struct render_params *params,
const struct table_item *table_item)
{
- 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;
p = xzalloc (sizeof *p);
p->params = params;
/* Title. */
- if (title)
- render_pager_add_table (p, table_from_string (TAB_LEFT, title));
+ add_text_page (p, table_item_get_title (table_item));
/* Body. */
- body_page = render_pager_add_table (p, table_ref (table_item_get_table (
- table_item)));
+ render_pager_add_table (p, table_ref (table_item_get_table (table_item)));
/* Caption. */
- if (caption)
- render_pager_add_table (p, table_from_string (TAB_LEFT, caption));
+ add_text_page (p, table_item_get_caption (table_item));
/* Footnotes. */
- add_footnote_page (p, body_page);
+ add_footnote_page (p, table_item);
render_pager_start_page (p);
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;
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;
}