}
static void
-fill_cell (struct table *t, int x1, int y1, int x2, int y2,
+fill_cell (struct table *t, int x, int y,
int style_idx, const struct pivot_value *value,
bool rotate_label)
{
if (rotate_label)
options |= TABLE_CELL_ROTATE;
+ table_put (t, x, y, x, y, options, value);
+}
+
+static void
+fill_cell_spanned (struct table *t, int x1, int y1, int x2, int y2,
+ int style_idx, const struct pivot_value *value,
+ bool rotate_label, int options)
+{
+ options |= style_idx << TABLE_CELL_STYLE_SHIFT;
+ if (rotate_label)
+ options |= TABLE_CELL_ROTATE;
+
table_put (t, x1, y1, x2, y2, options, value);
}
enum pivot_border cat_col_vert,
const size_t *column_enumeration, size_t n_columns,
int label_style_idx,
- bool rotate_inner_labels, bool rotate_outer_labels)
+ bool rotate_inner_labels, bool rotate_outer_labels,
+ int area[TABLE_N_AXES][2])
{
const enum table_axis v = !h;
const int v_size = h_axis->label_depth;
- const int h_ofs = v_axis->label_depth;
+ const int h_ofs = v_axis->label_depth + area[h][0];
if (!h_axis->n_dimensions || !n_columns || !v_size)
return;
/* Below, we're going to iterate through the dimensions. Each dimension
occupies one or more rows in the heading. 'top_row' is the top row of
these (and 'top_row + d->label_depth - 1' is the bottom row). */
- int top_row = 0;
+ int top_row = area[v][0];
+
+ const int h_max = area[h][1];
+ const int v_max = area[v][1];
/* We're going to iterate through dimensions and the rows that label them
from top to bottom (from outer to inner dimensions). As we move downward,
int y1 = top_row + row_ofs;
int y2 = top_row + row_ofs + c->extra_depth + 1;
- bool is_outer_row = y1 == 0;
- bool is_inner_row = y2 == v_size;
if (pivot_category_is_leaf (c) || c->show_label)
{
int bb[TABLE_N_AXES][2];
bb[h][1] = x2 + h_ofs - 1;
bb[v][0] = y1;
bb[v][1] = y2 - 1;
+ bool is_outer_row = y1 == area[v][0];
+ bool is_inner_row = y2 == v_size + area[v][0];
bool rotate = ((rotate_inner_labels && is_inner_row)
|| (rotate_outer_labels && is_outer_row));
- fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
- label_style_idx, c->name, rotate);
+ fill_cell_spanned (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
+ label_style_idx, c->name, rotate, 0);
/* Draw all the vertical lines in our running example, other
than the far left and far right ones. Only the ones that
= (y1 == v_size - 1 ? cat_col_vert : dim_col_vert);
if (!vrules[x2])
{
- draw_line (t, style, v, x2 + h_ofs, y1, t->n[v] - 1);
+ draw_line (t, style, v, x2 + h_ofs, y1, v_max);
vrules[x2] = true;
}
if (!vrules[x1])
{
- draw_line (t, style, v, x1 + h_ofs, y1, t->n[v] - 1);
+ draw_line (t, style, v, x1 + h_ofs, y1, v_max);
vrules[x1] = true;
}
}
}
}
- if (d->root->show_label_in_corner && h_ofs > 0)
+ if (d->root->show_label_in_corner && h_ofs > area[h][0])
{
int bb[TABLE_N_AXES][2];
bb[h][0] = 0;
bb[h][1] = h_ofs - 1;
bb[v][0] = top_row;
bb[v][1] = top_row + d->label_depth - 1;
- fill_cell (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
- PIVOT_AREA_CORNER, d->root->name, false);
+ fill_cell_spanned (t, bb[H][0], bb[V][0], bb[H][1], bb[V][1],
+ PIVOT_AREA_CORNER, d->root->name, false, 0);
}
/* Draw the horizontal line between dimensions, e.g. the ===== line here:
+-----+-----+-----+-----+-----+-----+-----+-----+-----+
*/
if (dim_index != h_axis->n_dimensions - 1)
- draw_line (t, dim_col_horz, h, top_row, h_ofs, t->n[h] - 1);
+ draw_line (t, dim_col_horz, h, top_row, h_ofs, h_max);
top_row += d->label_depth;
}
free (vrules);
return table;
}
-
static void
add_references (const struct pivot_table *pt, const struct table *table,
bool *refs, size_t *n_refs)
return footnotes;
}
+static struct table *
+pivot_output_title (const struct pivot_table *pt)
+{
+ if (!pt->title || !pt->show_title)
+ return NULL;
+ struct table *title = create_aux_table (pt, 1, 1, PIVOT_AREA_TITLE);
+ fill_cell (title, 0, 0, PIVOT_AREA_TITLE, pt->title, false);
+ return title;
+}
+
+static int
+pivot_count_layers (const struct pivot_table *pt)
+{
+ const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
+ int n_layers = 0;
+ for (size_t i = 0; i < layer_axis->n_dimensions; i++)
+ {
+ const struct pivot_dimension *d = layer_axis->dimensions[i];
+ if (d->n_leaves)
+ n_layers++;
+ }
+ return n_layers;
+}
+
+static void
+put_layers (struct table *t, const struct pivot_table *pt,
+ const size_t *layer_indexes, int x1, int y1 UNUSED, int x2, int y2)
+{
+ assert (y1 + pivot_count_layers (pt) - 1 == y2);
+ const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
+ for (size_t i = 0; i < layer_axis->n_dimensions; i++)
+ {
+ const struct pivot_dimension *d = layer_axis->dimensions[i];
+ if (!d->n_leaves)
+ continue;
+
+ struct string s = DS_EMPTY_INITIALIZER;
+ pivot_value_format (d->data_leaves[layer_indexes[i]]->name, pt, &s);
+ int y = y2 - i;
+ fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_LAYERS, &s, false);
+ }
+}
+
+static struct table *
+pivot_output_layers (const struct pivot_table *pt, const size_t *layer_indexes)
+{
+ int n_layers = pivot_count_layers (pt);
+ if (!n_layers)
+ return NULL;
+
+ struct table *layers = create_aux_table (pt, 1, n_layers, PIVOT_AREA_LAYERS);
+ put_layers (layers, pt, layer_indexes, 0, 0, 0, n_layers - 1);
+ return layers;
+}
+
+static struct table *
+pivot_output_caption (const struct pivot_table *pt)
+{
+ if (!pt->caption || !pt->show_caption)
+ return NULL;
+
+ struct table *caption = create_aux_table (pt, 1, 1, PIVOT_AREA_CAPTION);
+ fill_cell (caption, 0, 0, PIVOT_AREA_CAPTION, pt->caption, false);
+ return caption;
+}
+
+static void
+put_footnotes (struct table *t, const struct pivot_table *pt,
+ struct pivot_footnote **f, size_t nf,
+ int x1, int x2, int y1)
+{
+ for (size_t i = 0; i < nf; i++)
+ {
+ struct string s = DS_EMPTY_INITIALIZER;
+ pivot_footnote_format_marker (f[i], pt, &s);
+ ds_put_cstr (&s, ". ");
+ pivot_value_format (f[i]->content, pt, &s);
+ int y = y1 + i;
+ fill_cell_owned (t, x1, y, x2, y, PIVOT_AREA_FOOTER, &s, false);
+ }
+}
+
+static struct table *
+pivot_output_footnotes (const struct pivot_table *pt,
+ struct pivot_footnote **f, size_t nf)
+{
+ if (!nf)
+ return NULL;
+
+ struct table *footnotes = create_aux_table (pt, 1, nf, PIVOT_AREA_FOOTER);
+ put_footnotes (footnotes, pt, f, nf, 0, 0, 0);
+ return footnotes;
+}
+
void
pivot_output (const struct pivot_table *pt,
const size_t *layer_indexes,
- bool printing UNUSED,
+ bool printing,
struct table **titlep,
struct table **layersp,
struct table **bodyp,
[H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
[V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
};
- struct table *body = table_create (data[H] + stub[H],
- data[V] + stub[V],
- stub[H], 0, stub[V], 0);
+ int frame[TABLE_N_AXES][2] = {
+ [H] = {
+ pt->look->borders[PIVOT_BORDER_OUTER_LEFT].stroke != TABLE_STROKE_NONE,
+ pt->look->borders[PIVOT_BORDER_OUTER_RIGHT].stroke != TABLE_STROKE_NONE,
+ },
+ [V] = {
+ pt->look->borders[PIVOT_BORDER_OUTER_TOP].stroke != TABLE_STROKE_NONE,
+ pt->look->borders[PIVOT_BORDER_OUTER_BOTTOM].stroke != TABLE_STROKE_NONE,
+ },
+ };
+ int fs[TABLE_N_AXES] = {
+ [H] = stub[H] + frame[H][0],
+ [V] = stub[V] + frame[V][0],
+ };
+
+ struct table *body = table_create (
+ frame[H][0] + stub[H] + data[H] + frame[H][1],
+ frame[V][0] + stub[V] + data[V] + frame[V][1],
+ frame[H][0] + stub[H], frame[H][1],
+ frame[V][0] + stub[V], frame[V][1]);
for (size_t i = 0; i < PIVOT_N_AREAS; i++)
body->styles[i] = table_area_style_override (
body->container, &pt->look->areas[i], NULL, NULL, false);
: *src);
}
+ int body_area[TABLE_N_AXES][2] = {
+ [H] = { frame[H][0], body->n[H] - frame[H][1] - 1 },
+ [V] = { frame[V][0], body->n[V] - frame[V][1] - 1 },
+ };
compose_headings (body,
&pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
PIVOT_BORDER_DIM_COL_HORZ,
PIVOT_BORDER_CAT_COL_VERT,
column_enumeration, data[H],
PIVOT_AREA_COLUMN_LABELS,
- pt->rotate_outer_row_labels, false);
+ pt->rotate_outer_row_labels, false, body_area);
compose_headings (body,
&pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
PIVOT_BORDER_CAT_ROW_HORZ,
row_enumeration, data[V],
PIVOT_AREA_ROW_LABELS,
- false, pt->rotate_inner_column_labels);
+ false, pt->rotate_inner_column_labels, body_area);
size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
size_t y = 0;
{
pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
const struct pivot_value *value = pivot_table_get (pt, dindexes);
- fill_cell (body, x + stub[H], y + stub[V], x + stub[H], y + stub[V],
+ fill_cell (body, x + fs[H], y + fs[V],
PIVOT_AREA_DATA, value, false);
x++;
if ((pt->corner_text || !pt->look->row_labels_in_corner)
&& stub[H] && stub[V])
- fill_cell (body, 0, 0, stub[H] - 1, stub[V] - 1,
- PIVOT_AREA_CORNER, pt->corner_text, false);
+ fill_cell_spanned (body, 0, 0, fs[H] - 1, fs[V] - 1,
+ PIVOT_AREA_CORNER, pt->corner_text, false,
+ TABLE_CELL_FULL_WIDTH);
if (body->n[H] && body->n[V])
{
- table_hline (body, PIVOT_BORDER_INNER_TOP, 0, body->n[H] - 1, 0);
- table_hline (body, PIVOT_BORDER_INNER_BOTTOM, 0, body->n[H] - 1,
- body->n[V]);
- table_vline (body, PIVOT_BORDER_INNER_LEFT, 0, 0, body->n[V] - 1);
- table_vline (body, PIVOT_BORDER_INNER_RIGHT, body->n[H], 0,
- body->n[V] - 1);
+ int inner[TABLE_N_AXES][2] = {
+ [H] = { frame[H][0], body->n[H] - frame[H][1] },
+ [V] = { frame[V][0], body->n[V] - frame[V][1] },
+ };
+ table_hline (body, PIVOT_BORDER_INNER_TOP,
+ inner[H][0], inner[H][1] - 1, inner[V][0]);
+ table_hline (body, PIVOT_BORDER_INNER_BOTTOM,
+ inner[H][0], inner[H][1] - 1, inner[V][1]);
+ table_vline (body, PIVOT_BORDER_INNER_LEFT,
+ inner[H][0], inner[V][0], inner[V][1] - 1);
+ table_vline (body, PIVOT_BORDER_INNER_LEFT,
+ inner[H][1], inner[V][0], inner[V][1] - 1);
if (stub[V])
- table_hline (body, PIVOT_BORDER_DATA_TOP, 0, body->n[H] - 1, stub[V]);
+ table_hline (body, PIVOT_BORDER_DATA_TOP,
+ inner[H][0], inner[H][1] - 1, fs[V]);
if (stub[H])
- table_vline (body, PIVOT_BORDER_DATA_LEFT, stub[H], 0, body->n[V] - 1);
+ table_vline (body, PIVOT_BORDER_DATA_LEFT,
+ fs[H], inner[V][0], inner[V][1] - 1);
+ }
+ if (frame[H][0])
+ {
+ for (int y = 0; y < body->n[V]; y++)
+ table_put (body, 0, y, 0, y, TABLE_CELL_PADDING, NULL);
+ table_vline (body, PIVOT_BORDER_OUTER_LEFT, 0, 0, body->n[V] - 1);
}
+ if (frame[H][1])
+ {
+ int x = body->n[H] - 1;
+ for (int y = 0; y < body->n[V]; y++)
+ table_put (body, x, y, x, y, TABLE_CELL_PADDING, NULL);
+ table_vline (body, PIVOT_BORDER_OUTER_RIGHT,
+ body->n[H], 0, body->n[V] - 1);
+ }
+ if (frame[V][0])
+ {
+ for (int x = 0; x < body->n[H]; x++)
+ table_put (body, x, 0, x, 0, TABLE_CELL_PADDING, NULL);
+ table_hline (body, PIVOT_BORDER_OUTER_TOP, 0, body->n[H] - 1, 0);
+ }
+ if (frame[V][1])
+ {
+ int y = body->n[V] - 1;
+ for (int x = 0; x < body->n[H]; x++)
+ table_put (body, x, y, x, y, TABLE_CELL_PADDING, NULL);
+ table_hline (body, PIVOT_BORDER_OUTER_BOTTOM, 0, body->n[H] - 1, body->n[V]);
+ }
+
free (column_enumeration);
free (row_enumeration);
- /* Title. */
- struct table *title;
- if (pt->title && pt->show_title && titlep)
+ *bodyp = body;
+ if (titlep)
+ *titlep = pivot_output_title (pt);
+ if (layersp)
+ *layersp = pivot_output_layers (pt, layer_indexes);
+ if (captionp)
+ *captionp = pivot_output_caption (pt);
+
+ if (fp || footnotesp)
{
- title = create_aux_table (pt, 1, 1, PIVOT_AREA_TITLE);
- fill_cell (title, 0, 0, 0, 0, PIVOT_AREA_TITLE, pt->title, false);
+ size_t nf;
+ struct pivot_footnote **f = collect_footnotes (
+ pt, titlep ? *titlep : NULL, layersp ? *layersp : NULL, body,
+ captionp ? *captionp : NULL, &nf);
+
+ if (footnotesp)
+ *footnotesp = pivot_output_footnotes (pt, f, nf);
+
+ if (fp)
+ {
+ *fp = f;
+ *nfp = nf;
+ }
+ else
+ free (f);
}
- else
- title = NULL;
+}
- /* Layers. */
- const struct pivot_axis *layer_axis = &pt->axes[PIVOT_AXIS_LAYER];
- int n_layers = 0;
- if (layersp)
- for (size_t i = 0; i < layer_axis->n_dimensions; i++)
- {
- const struct pivot_dimension *d = layer_axis->dimensions[i];
- if (d->n_leaves)
- n_layers++;
- }
+struct table *
+pivot_output_monolithic (const struct pivot_table *pt,
+ const size_t *layer_indexes,
+ bool printing)
+{
+ /* Table structure:
- struct table *layers;
- if (n_layers > 0)
+ #============ Frame ============#
+ # Title #
+ # Layers #
+ # +--- Body ------------------+ #
+ # | Stub | #
+ # | +--------------------+ #
+ # | | Data | #
+ # | | | #
+ # | | | #
+ # | | | #
+ # +------+--------------------+ #
+ # Caption #
+ # Footnotes #
+ #===============================#
+
+ The regions are the following size:
+
+ - Frame: Either 0 or 1 row or column wide, depending on whether the table
+ style has an outer border on each side. The frame cells are always
+ empty and marked as TABLE_CELL_PADDING.
+
+ - Title: Either 0 or 1 row high.
+
+ - Layers: Usually, one row for each layer dimension, but less if a layer
+ dimension is empty (has no value).
+
+ - Body and data: Variable (and can be empty).
+
+ - Caption: Either 0 or 1 row high.
+
+ - Footnotes: From zero rows up to the number of footnotes in PT (only
+ footnotes referenced in the other regions are shown).
+
+ The title, layers, caption, and footnote Region join all the body's
+ columns into single cells.
+ */
+
+ /* Size of data region. */
+ size_t data[TABLE_N_AXES];
+ size_t *column_enumeration = pivot_table_enumerate_axis (
+ pt, PIVOT_AXIS_COLUMN, layer_indexes, pt->look->omit_empty, &data[H]);
+ size_t *row_enumeration = pivot_table_enumerate_axis (
+ pt, PIVOT_AXIS_ROW, layer_indexes, pt->look->omit_empty, &data[V]);
+
+ /* Size of stub region. */
+ int stub[TABLE_N_AXES] = {
+ [H] = pt->axes[PIVOT_AXIS_ROW].label_depth,
+ [V] = pt->axes[PIVOT_AXIS_COLUMN].label_depth,
+ };
+
+ /* Width of each side of the frame. */
+ int frame[TABLE_N_AXES][2] = {
+ [H] = {
+ pt->look->borders[PIVOT_BORDER_OUTER_LEFT].stroke != TABLE_STROKE_NONE,
+ pt->look->borders[PIVOT_BORDER_OUTER_RIGHT].stroke != TABLE_STROKE_NONE,
+ },
+ [V] = {
+ pt->look->borders[PIVOT_BORDER_OUTER_TOP].stroke != TABLE_STROKE_NONE,
+ pt->look->borders[PIVOT_BORDER_OUTER_BOTTOM].stroke != TABLE_STROKE_NONE,
+ },
+ };
+
+ int n_title = pt->title && pt->show_title;
+ int n_layers = pivot_count_layers (pt);
+ int n_caption = pt->caption && pt->show_caption;
+ int max_footnotes = pt->n_footnotes;
+
+ int min_body_width = n_title || n_layers || n_caption;
+ int n[TABLE_N_AXES] = {
+ [H] = frame[H][0] + MAX (min_body_width, stub[H] + data[H]) + frame[H][1],
+ [V] = frame[V][0] + n_title + n_layers + stub[V] + data[V] + n_caption + max_footnotes + frame[V][1],
+ };
+
+ struct table *t = table_create (
+ n[H],
+ n[V],
+#if 1
+ frame[H][0] + stub[H], frame[H][1],
+ frame[V][0] + n_title + n_layers + stub[V], frame[V][1]
+#else
+ 0, 0, 0, 0
+#endif
+ );
+ for (size_t i = 0; i < PIVOT_N_AREAS; i++)
+ t->styles[i] = table_area_style_override (
+ t->container, &pt->look->areas[i], NULL, NULL, false);
+
+ t->n_borders = PIVOT_N_BORDERS;
+ t->borders = pool_nmalloc (t->container, PIVOT_N_BORDERS, sizeof *t->borders);
+ for (size_t i = 0; i < PIVOT_N_BORDERS; i++)
+ {
+ const struct table_border_style *src = &pt->look->borders[i];
+ struct table_border_style *dst = &t->borders[i];
+ *dst = (!printing && pt->show_grid_lines && src->stroke == TABLE_STROKE_NONE
+ ? (struct table_border_style) { .stroke = TABLE_STROKE_DASHED,
+ .color = CELL_COLOR_BLACK }
+ : *src);
+ }
+
+ int body[TABLE_N_AXES][2] = {
+ [H] = { frame[H][0], n[H] - frame[H][1] - 1 },
+ [V] = {
+ frame[V][0] + n_title + n_layers,
+ frame[V][0] + n_title + n_layers + stub[V] + data[V] - 1
+ },
+ };
+
+ if (n_layers)
+ put_layers (t, pt, layer_indexes,
+ body[H][0], frame[V][0] + n_title,
+ body[H][1], frame[V][0] + n_title + n_layers - 1);
+
+ compose_headings (t,
+ &pt->axes[PIVOT_AXIS_COLUMN], H, &pt->axes[PIVOT_AXIS_ROW],
+ PIVOT_BORDER_DIM_COL_HORZ,
+ PIVOT_BORDER_DIM_COL_VERT,
+ PIVOT_BORDER_CAT_COL_HORZ,
+ PIVOT_BORDER_CAT_COL_VERT,
+ column_enumeration, data[H],
+ PIVOT_AREA_COLUMN_LABELS,
+ pt->rotate_outer_row_labels, false, body);
+
+ compose_headings (t,
+ &pt->axes[PIVOT_AXIS_ROW], V, &pt->axes[PIVOT_AXIS_COLUMN],
+ PIVOT_BORDER_DIM_ROW_VERT,
+ PIVOT_BORDER_DIM_ROW_HORZ,
+ PIVOT_BORDER_CAT_ROW_VERT,
+ PIVOT_BORDER_CAT_ROW_HORZ,
+ row_enumeration, data[V],
+ PIVOT_AREA_ROW_LABELS,
+ false, pt->rotate_inner_column_labels, body);
+
+ int data_ofs[TABLE_N_AXES] = {
+ [H] = body[H][0] + stub[H],
+ [V] = body[V][0] + stub[V],
+ };
+
+ const size_t *pindexes[PIVOT_N_AXES] = { [PIVOT_AXIS_LAYER] = layer_indexes };
+ size_t *dindexes = XCALLOC (pt->n_dimensions, size_t);
+ int y = data_ofs[V];
+ PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_ROW], row_enumeration,
+ &pt->axes[PIVOT_AXIS_ROW])
{
- layers = create_aux_table (pt, 1, n_layers, PIVOT_AREA_LAYERS);
- size_t y = n_layers - 1;
- for (size_t i = 0; i < layer_axis->n_dimensions; i++)
+ int x = data_ofs[H];
+ PIVOT_ENUMERATION_FOR_EACH (pindexes[PIVOT_AXIS_COLUMN],
+ column_enumeration,
+ &pt->axes[PIVOT_AXIS_COLUMN])
{
- const struct pivot_dimension *d = layer_axis->dimensions[i];
- if (!d->n_leaves)
- continue;
-
- struct string s = DS_EMPTY_INITIALIZER;
- pivot_value_format (d->data_leaves[layer_indexes[i]]->name, pt, &s);
- fill_cell_owned (layers, 0, y, 0, y, PIVOT_AREA_LAYERS, &s, false);
- y--;
+ pivot_table_convert_indexes_ptod (pt, pindexes, dindexes);
+ const struct pivot_value *value = pivot_table_get (pt, dindexes);
+ fill_cell (t, x, y, PIVOT_AREA_DATA, value, false);
+ x++;
}
+
+ y++;
}
- else
- layers = NULL;
+ free (dindexes);
+
+ if ((pt->corner_text || !pt->look->row_labels_in_corner)
+ && stub[H] && stub[V])
+ fill_cell_spanned (t, body[H][0], body[V][0],
+ body[H][0] + stub[H] - 1, body[V][0] + stub[V] - 1,
+ PIVOT_AREA_CORNER, pt->corner_text, false,
+ TABLE_CELL_FULL_WIDTH);
- /* Caption. */
- struct table *caption;
- if (pt->caption && pt->show_caption && captionp)
+ if (body[H][1] >= body[H][0] && body[V][1] >= body[V][0])
{
- caption = create_aux_table (pt, 1, 1, PIVOT_AREA_CAPTION);
- fill_cell (caption, 0, 0, 0, 0, PIVOT_AREA_CAPTION, pt->caption, false);
+ table_hline (t, PIVOT_BORDER_INNER_TOP,
+ body[H][0], body[H][1], body[V][0]);
+ table_hline (t, PIVOT_BORDER_INNER_BOTTOM,
+ body[H][0], body[H][1], body[V][1] + 1);
+ table_vline (t, PIVOT_BORDER_INNER_LEFT,
+ body[H][0], body[V][0], body[V][1]);
+ table_vline (t, PIVOT_BORDER_INNER_RIGHT,
+ body[H][1] + 1, body[V][0], body[V][1]);
+
+ if (stub[V])
+ table_hline (t, PIVOT_BORDER_DATA_TOP,
+ body[H][0], body[H][1], data_ofs[V]);
+ if (stub[H])
+ table_vline (t, PIVOT_BORDER_DATA_LEFT,
+ data_ofs[H], body[V][0], body[V][1]);
}
- else
- caption = NULL;
- /* Footnotes. */
+ if (n_caption)
+ fill_cell_spanned (t,
+ body[H][0], body[V][1] + 1,
+ body[H][1], body[V][1] + 1,
+ PIVOT_AREA_CAPTION, pt->caption, false,
+ TABLE_CELL_FULL_WIDTH);
+
size_t nf;
- struct pivot_footnote **f = collect_footnotes (pt, title, layers, body,
- caption, &nf);
- struct table *footnotes;
- if (nf && footnotesp)
+ struct pivot_footnote **f = collect_footnotes (pt, t, NULL, NULL, NULL, &nf);
+ assert (nf <= max_footnotes);
+ put_footnotes (t, pt, f, nf, body[H][0], body[H][1],
+ body[V][1] + 1 + n_caption);
+ t->n[V] = body[V][1] + 1 + n_caption + nf + frame[V][1];
+ free (f);
+
+ if (n_title)
{
- footnotes = create_aux_table (pt, 1, nf, PIVOT_AREA_FOOTER);
-
- for (size_t i = 0; i < nf; i++)
- {
- struct string s = DS_EMPTY_INITIALIZER;
- pivot_footnote_format_marker (f[i], pt, &s);
- ds_put_cstr (&s, ". ");
- pivot_value_format (f[i]->content, pt, &s);
- fill_cell_owned (footnotes, 0, i, 0, i, PIVOT_AREA_FOOTER, &s,
- false);
- }
+ printf ("%d,%d - %d,%d\n",
+ body[H][0], frame[V][0],
+ body[H][1], frame[V][0]);
+ fill_cell_spanned (t,
+ body[H][0], frame[V][0],
+ body[H][1], frame[V][0],
+ PIVOT_AREA_TITLE, pt->title, false,
+ TABLE_CELL_FULL_WIDTH);
}
- else
- footnotes = NULL;
- *titlep = title;
- if (layersp)
- *layersp = layers;
- *bodyp = body;
- if (captionp)
- *captionp = caption;
- if (footnotesp)
- *footnotesp = footnotes;
- if (fp)
+ if (frame[H][0])
{
- *fp = f;
- *nfp = nf;
+ for (int y = 0; y < t->n[V]; y++)
+ table_put (t, 0, y, 0, y, TABLE_CELL_PADDING, NULL);
+ table_vline (t, PIVOT_BORDER_OUTER_LEFT, 0, 0, t->n[V] - 1);
}
- else
- free (f);
+ if (frame[H][1])
+ {
+ int x = t->n[H] - 1;
+ for (int y = 0; y < t->n[V]; y++)
+ table_put (t, x, y, x, y, TABLE_CELL_PADDING, NULL);
+ table_vline (t, PIVOT_BORDER_OUTER_RIGHT,
+ t->n[H], 0, t->n[V] - 1);
+ }
+ if (frame[V][0])
+ {
+ for (int x = 0; x < t->n[H]; x++)
+ table_put (t, x, 0, x, 0, TABLE_CELL_PADDING, NULL);
+ table_hline (t, PIVOT_BORDER_OUTER_TOP, 0, t->n[H] - 1, 0);
+ }
+ if (frame[V][1])
+ {
+ int y = t->n[V] - 1;
+ for (int x = 0; x < t->n[H]; x++)
+ table_put (t, x, y, x, y, TABLE_CELL_PADDING, NULL);
+ table_hline (t, PIVOT_BORDER_OUTER_TOP, 0, t->n[H] - 1, t->n[V]);
+ }
+
+ free (column_enumeration);
+ free (row_enumeration);
+
+ printf ("output page:\n");
+ for (int y = 0; y < t->n[V]; y++)
+ for (int x = 0; x < t->n[H]; x++)
+ {
+ struct table_cell cell;
+
+ table_get_cell (t, x, y, &cell);
+ char *s = pivot_value_to_string (cell.value, NULL);
+ printf ("%d,%d - %d,%d: %s\n",
+ cell.d[H][0], cell.d[V][0],
+ cell.d[H][1], cell.d[V][1],
+ s);
+ free (s);
+ }
+
+ return t;
}
void
#define H TABLE_HORZ
#define V TABLE_VERT
\f
+/* Maps a contiguous range of cells from a page to the underlying table along
+ the horizontal or vertical dimension. */
+struct map
+ {
+ int p[2]; /* Range of ordinates in the page. */
+ int t[2]; /* Range of ordinates in the table. */
+ int ofs; /* p[0] - t[0]. */
+ int n; /* p[1] - p[0] == t[1] - t[0]. */
+ };
+
/* A layout for rendering a specific table on a specific device.
May represent the layout of an entire table presented to
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]
*/
+ int n[TABLE_N_AXES];
int h[TABLE_N_AXES][2];
int r[TABLE_N_AXES][2];
- int n[TABLE_N_AXES];
+
+ struct map maps[TABLE_N_AXES][3];
/* "Cell positions".
return width;
}
+static struct map
+make_map (int p0, int t0, int n)
+{
+ return (struct map) {
+ .p = { p0, p0 + n },
+ .t = { t0, t0 + n },
+ .ofs = p0 - t0,
+ .n = n,
+ };
+}
+
/* 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][2],
+ 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];
+ *page = (struct render_page) {
+ .params = params,
+ .table = table,
+ .ref_cnt = 1,
+ .n = { [H] = n[H], [V] = n[V] },
+ .h = { [H] = { h[H][0], h[H][1] }, [V] = { h[V][0], h[V][1] } },
+ .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]) },
+ .join_crossing = {
+ [H] = xcalloc (n[H] + 1, sizeof *page->join_crossing[H]),
+ [V] = xcalloc (n[V] + 1, sizeof *page->join_crossing[V])
+ },
+ .overflows = HMAP_INITIALIZER (page->overflows),
+ };
- for (int i = 0; i < TABLE_N_AXES; i++)
+ for (enum table_axis a = 0; a < TABLE_N_AXES; a++)
{
- 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]);
+ struct map *m = page->maps[a];
+ if (h[a][0])
+ *m++ = make_map (0, 0, page->h[a][0]);
+ *m++ = make_map (h[a][0], r[a][0], r[a][1] - r[a][0]);
+ if (h[a][1])
+ *m++ = make_map (n[a] - h[a][1], table->n[a] - table->h[a][1], h[a][1]);
}
-
- hmap_init (&page->overflows);
- memset (page->is_edge_cutoff, 0, sizeof page->is_edge_cutoff);
-
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][2];
+ 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][0] = table->h[a][0];
+ h[a][1] = table->h[a][1];
+ r[a][0] = table->h[a][0];
+ r[a][1] = table->n[a] - table->h[a][1];
}
- return page;
+ return render_page_allocate__ (params, table, table->n, h, r);
}
/* Allocates and returns a new render_page for PARAMS and TABLE, initializing
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. */
-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
-get_map (const struct render_page *page, enum table_axis a, int z,
- struct map *m)
+/* Returns a mapping from page to table ordinates along axis A that includes
+ ordinate Z (in PAGE). */
+static const struct map *
+get_map (const struct render_page *page, enum table_axis a, int z)
{
- if (z < page->h[a][0])
- {
- m->p0 = 0;
- m->t0 = 0;
- m->n = page->h[a][0];
- }
- else if (z < page->n[a] - page->h[a][1])
+ for (size_t i = 0; i < 3; i++)
{
- m->p0 = page->h[a][0];
- 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];
+ const struct map *m = &page->maps[a][i];
+ if (z < m->p[1])
+ {
+ assert (z >= m->p[0]);
+ return m;
+ }
}
+ NOT_REACHED ();
}
/* Initializes CELL with the contents of the table cell at column X and row Y
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];
+ const struct map *map[TABLE_N_AXES] = {
+ [H] = get_map (page, H, x),
+ [V] = get_map (page, V, y),
+ };
- 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);
+ /* Get CELL, translating X and Y into table coordinates. CELL is also
+ returned in table coordinates. */
+ table_get_cell (page->table, x + map[H]->ofs, y + map[V]->ofs, cell);
+ /* Translate CELL into page coordinates. */
for (enum table_axis a = 0; a < TABLE_N_AXES; a++)
{
- struct map *m = &map[a];
+ const 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 0
+ if (a == H && cell->options & TABLE_CELL_FULL_WIDTH)
+ {
+ printf ("special case\n");
+ cell->d[a][0] -= m->ofs;
+ cell->d[a][1] -= get_map (page, H, cell->d[a][1] - 1)->ofs;
+ }
+ else
+#endif
+ {
+ for (int i = 0; i < 2; i++)
+ cell->d[a][i] -= m->ofs;
+ cell->d[a][0] = MAX (cell->d[a][0], m->p[0]);
+ cell->d[a][1] = MIN (cell->d[a][1], m->p[1]);
+ }
}
}
/* Fits even with maximum widths. Use them. */
page = create_page_with_exact_widths (params, table, columns[MAX],
rules[H]);
+ //page->h[H][0] = page->h[H][1] = 0;
}
else if (table_widths[MIN] <= params->size[H])
{
page = create_page_with_interpolated_widths (
params, table, columns[MIN], columns[MAX],
table_widths[MIN], table_widths[MAX], rules[H]);
+ //page->h[H][0] = page->h[H][1] = 0;
}
else
{
- (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;
+ d[b] += get_map (page, b, d[b])->ofs;
struct table_border_style border
= table_get_rule (page->table, axis, d[H], d[V]);
render_page_draw_cells (const struct render_page *page,
int ofs[TABLE_N_AXES], int bb[TABLE_N_AXES][2])
{
+ /* OK, the problem is the headers.... the left-side headers shouldn't apply
+ to the title and caption and so on */
+ printf ("render page %d,%d - %d,%d\n",
+ bb[H][0], bb[V][0],
+ bb[H][1], bb[V][1]);
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;
+ if (!is_rule (y))
+ for (int x = bb[H][0]; x < bb[H][1]; x++)
+ if (!is_rule (x))
+ {
+ struct table_cell cell;
+
+ render_get_cell (page, x / 2, y / 2, &cell);
+ char *s = pivot_value_to_string (cell.value, NULL);
+ printf ("%d,%d - %d,%d: %s\n",
+ cell.d[H][0], cell.d[V][0],
+ cell.d[H][1], cell.d[V][1],
+ s);
+ free (s);
+ }
- 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++)
+ if (!is_rule (y))
+ {
+ for (int x = bb[H][0]; x < bb[H][1];)
+ if (!is_rule (x))
+ {
+ struct table_cell cell;
+
+ render_get_cell (page, x / 2, y / 2, &cell);
+ if (y / 2 == bb[V][0] / 2 || y / 2 == cell.d[V][0])
+ {
+ if (cell.d[H][1] - cell.d[H][0] != 1 ||
+ cell.d[V][1] - cell.d[V][0] != 1)
+ {
+ char *s = pivot_value_to_string (cell.value, NULL);
+ printf ("rendering %d,%d - %d,%d (%s)\n",
+ cell.d[H][0], cell.d[V][0],
+ cell.d[H][1], cell.d[V][1],
+ s);
+ free (s);
+ }
+ 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++)
const struct render_params *params;
double scale;
- /* 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;
+ struct render_page *page;
- size_t cur_page;
struct render_break x_break;
struct render_break y_break;
};
-static void
-render_pager_add_table (struct render_pager *p, struct table *table,
- int min_width)
-{
- if (table)
- p->pages[p->n_pages++] = render_page_create (p->params, table, min_width);
-}
-
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);
}
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);
+ struct table *table = pivot_output_monolithic (pt, layer_indexes,
+ params->printing);
- /* Figure out the width of the body of the table. Use this to determine the
- base scale. */
- struct render_page *body_page = render_page_create (params, body, 0);
- int body_width = table_width (body_page, H);
+ /* 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 (body_width > params->size[H])
+ if (width > params->size[H])
{
if (pt->look->shrink_to_fit[H] && params->ops->scale)
- scale = params->size[H] / (double) body_width;
+ scale = params->size[H] / (double) width;
else
{
struct render_break b;
- render_break_init (&b, render_page_ref (body_page), H);
+ render_break_init (&b, render_page_ref (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;
+ width = subpage ? subpage->cp[H][2 * subpage->n[H] + 1] : 0;
render_page_unref (subpage);
render_break_destroy (&b);
}
/* Create the pager. */
struct render_pager *p = xmalloc (sizeof *p);
- *p = (struct render_pager) { .params = params, .scale = scale };
- render_pager_add_table (p, title, body_width);
- render_pager_add_table (p, layers, body_width);
- p->pages[p->n_pages++] = body_page;
- render_pager_add_table (p, caption, 0);
- render_pager_add_table (p, footnotes, 0);
- assert (p->n_pages <= sizeof p->pages / sizeof *p->pages);
+ *p = (struct render_pager) { .params = params, .scale = scale, .page = page };
/* If we're shrinking tables to fit the page length, then adjust the scale
factor.
necessary would require an iterative search. */
if (pt->look->shrink_to_fit[V] && params->ops->scale)
{
- int total_height = 0;
- for (size_t i = 0; i < p->n_pages; i++)
- total_height += table_width (p->pages[i], V);
- if (total_height * p->scale >= params->size[V])
- p->scale *= params->size[V] / (double) total_height;
+ 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);
{
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]);
+ 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 (
}
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)
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. */
/* Allocate subpage. */
int trim[2] = { z0 - page->h[a][0], (page->n[a] - page->h[a][1]) - 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. */