#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"
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];
+
+ /* Minimum and maximum widths of columns based on headings.
+
+ For this purpose, a table has the following three regions:
+
+ +------------------+-------------------------------------------------+
+ | | column headings |
+ | +-------------------------------------------------+
+ | corner | |
+ | and | |
+ | row headings | data |
+ | | |
+ | | |
+ +------------------+-------------------------------------------------+
+
+ - width_ranges[TABLE_HORZ] controls the minimum and maximum width that
+ columns in the column headings will be based on the column headings
+ themselves. That is, these columns will have width at least
+ width_ranges[TABLE_HORZ][0] wide, and no more than
+ width_ranges[TABLE_HORZ][1] unless the data requires it.
+
+ - width_ranges[TABLE_VERT] controls the minimum and maximum width that
+ columns in the corner and row headings will be based on the corner and
+ row headings themselves. That is, these columns will have width at
+ least width_ranges[TABLE_VERT][0] wide, and no more than
+ width_ranges[TABLE_VERT][1]. (The corner and row headings don't have
+ data in their columns so data can't affect their widths.)
+ */
+ int width_ranges[TABLE_N_AXES][2];
};
static struct render_page *render_page_create (const struct render_params *,
- struct table *, int min_width);
+ struct table *, int min_width,
+ const struct pivot_table_look *);
struct render_page *render_page_ref (const struct render_page *page_);
static void render_page_unref (struct render_page *);
\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;
}
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]);
+ page->cp[i] = xcalloc ((2 * n[i] + 2) , sizeof *page->cp[i]);
+ page->join_crossing[i] = xcalloc ((n[i] + 1) , sizeof *page->join_crossing[i]);
}
hmap_init (&page->overflows);
}
/* 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. */
}
}
-/* Creates and returns a new render_page for rendering TABLE on a device
- described by PARAMS.
+/* Creates and returns a new render_page for rendering TABLE with the given
+ LOOK on a device described by PARAMS.
The new render_page will be suitable for rendering on a device whose page
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,
- int min_width)
+ int min_width, const struct pivot_table_look *look)
{
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];
rules[axis][z] = measure_rule (params, table, axis, z);
}
+ int col_heading_width_range[2];
+ int row_heading_width_range[2];
+ for (int i = 0; i < 2; i++)
+ col_heading_width_range[i] = look->col_heading_width_range[i] * params->px_size;
+ for (int i = 0; i < 2; i++)
+ row_heading_width_range[i] = look->row_heading_width_range[i] * params->px_size;
+
/* Calculate minimum and maximum widths of cells that do not
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;)
{
int w[2];
params->ops->measure_cell_width (params->aux, &cell,
&w[MIN], &w[MAX]);
+
+ if (params->px_size)
+ {
+ const int *wr = (x < table->h[H][0] ? row_heading_width_range
+ : y < table->h[V][0] ? col_heading_width_range
+ : NULL);
+ if (wr)
+ {
+ if (w[0] < wr[0])
+ {
+ w[0] = wr[0];
+ if (w[0] > w[1])
+ w[1] = w[0];
+ }
+ else if (w[1] > wr[1])
+ {
+ w[1] = wr[1];
+ if (w[1] < w[0])
+ w[0] = w[1];
+ }
+ }
+ }
+
for (int i = 0; i < 2; i++)
if (columns[i][x].unspanned < w[i])
columns[i][x].unspanned = w[i];
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;)
{
{
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];
\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;
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)
{
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;
+ /* An array of "render_page"s to be rendered, in order, vertically. There
+ may be up to 5 pages, for the pivot table's title, layers, body,
+ captions, and footnotes. */
+ struct render_page *pages[5];
+ size_t n_pages;
size_t cur_page;
struct render_break x_break;
struct render_break y_break;
};
-static const struct render_page *
+static void
render_pager_add_table (struct render_pager *p, struct table *table,
- int min_width)
+ int min_width, const struct pivot_table_look *look)
{
- 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;
+ if (table)
+ p->pages[p->n_pages++] = render_page_create (p->params, table, min_width,
+ look);
}
static void
render_break_init_empty (&p->y_break);
}
-static void
-add_footnote_page (struct render_pager *p, const struct table_item *item)
-{
- const struct footnote **f;
- size_t n_footnotes = table_collect_footnotes (item, &f);
- if (!n_footnotes)
- return;
-
- struct table *t = table_create (1, n_footnotes, 0, 0, 0, 0);
-
- for (size_t i = 0; i < n_footnotes; i++)
- {
- table_text_format (t, 0, i, 0, "%s. %s", f[i]->marker, f[i]->content);
- table_add_style (t, 0, i, f[i]->style);
- }
- render_pager_add_table (p, t, 0);
-
- free (f);
-}
-
-static void
-add_text_page (struct render_pager *p, const struct table_item_text *t,
- int min_width)
-{
- if (!t)
- return;
-
- 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++)
- {
- 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]);
- }
- 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. */
+/* 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 struct table *table = table_item_get_table (table_item);
+ if (!layer_indexes)
+ layer_indexes = pt->current_layer;
+
+ struct table *title, *layers, *body, *caption, *footnotes;
+ pivot_output (pt, layer_indexes, params->printing,
+ &title, &layers, &body, &caption, &footnotes, NULL, NULL);
/* Figure out the width of the body of the table. Use this to determine the
base scale. */
- struct render_page *page = render_page_create (params, table_ref (table), 0);
- int body_width = table_width (page, H);
+ struct render_page *body_page = render_page_create (params, body, 0, pt->look);
+ int body_width = table_width (body_page, H);
double scale = 1.0;
if (body_width > params->size[H])
{
- if (table_item->pt
- && table_item->pt->look->shrink_to_fit[H]
- && params->ops->scale)
+ if (pt->look->shrink_to_fit[H] && params->ops->scale)
scale = params->size[H] / (double) body_width;
else
{
struct render_break b;
- render_break_init (&b, page, H);
+ render_break_init (&b, render_page_ref (body_page), H);
struct render_page *subpage
= render_break_next (&b, params->size[H]);
body_width = subpage ? subpage->cp[H][2 * subpage->n[H] + 1] : 0;
/* Create the pager. */
struct render_pager *p = xmalloc (sizeof *p);
*p = (struct render_pager) { .params = params, .scale = scale };
- add_text_page (p, table_item_get_title (table_item), body_width);
- add_layers_page (p, table_item_get_layers (table_item), body_width);
- render_pager_add_table (p, table_ref (table_item_get_table (table_item)), 0);
- add_text_page (p, table_item_get_caption (table_item), 0);
- add_footnote_page (p, table_item);
+ render_pager_add_table (p, title, body_width, pt->look);
+ render_pager_add_table (p, layers, body_width, pt->look);
+ p->pages[p->n_pages++] = body_page;
+ render_pager_add_table (p, caption, 0, pt->look);
+ render_pager_add_table (p, footnotes, 0, pt->look);
+ assert (p->n_pages <= sizeof p->pages / sizeof *p->pages);
/* If we're shrinking tables to fit the page length, then adjust the scale
factor.
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 (table_item->pt
- && table_item->pt->look->shrink_to_fit[V]
- && params->ops->scale)
+ if (pt->look->shrink_to_fit[V] && params->ops->scale)
{
int total_height = 0;
for (size_t i = 0; i < p->n_pages; i++)
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);
free (p);
}
}
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]));