#include "libpspp/pool.h"
#include "libpspp/str.h"
#include "output/table-item.h"
+#include "output/table.h"
+#include "output/text-item.h"
#include "gl/xalloc.h"
+/* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
+#define H TABLE_HORZ
+#define V TABLE_VERT
+
/* Increases TABLE's reference count, indicating that it has an additional
owner. An table that is shared among multiple owners must not be
modified. */
{
assert (table->ref_cnt > 0);
if (--table->ref_cnt == 0)
- table->klass->destroy (table);
+ pool_destroy (table->container);
}
}
{
return table->ref_cnt > 1;
}
-
-/* Sets the number of left header columns in TABLE to HL. */
-void
-table_set_hl (struct table *table, int hl)
-{
- assert (!table_is_shared (table));
- table->h[TABLE_HORZ][0] = hl;
-}
-
-/* Sets the number of right header columns in TABLE to HR. */
-void
-table_set_hr (struct table *table, int hr)
-{
- assert (!table_is_shared (table));
- table->h[TABLE_HORZ][1] = hr;
-}
-
-/* Sets the number of top header rows in TABLE to HT. */
-void
-table_set_ht (struct table *table, int ht)
-{
- assert (!table_is_shared (table));
- table->h[TABLE_VERT][0] = ht;
-}
-
-/* Sets the number of top header rows in TABLE to HB. */
-void
-table_set_hb (struct table *table, int hb)
-{
- assert (!table_is_shared (table));
- table->h[TABLE_VERT][1] = hb;
-}
\f
-/* Initializes TABLE as a table of the specified CLASS, initially with a
- reference count of 1.
-
- TABLE initially has 0 rows and columns and no headers. The table
- implementation should update the numbers of rows and columns. The table
- implementation (or its client) may update the header rows and columns.
-
- A table is an abstract class, that is, a plain struct table is not useful on
- its own. Thus, this function is normally called from the initialization
- function of some subclass of table. */
-void
-table_init (struct table *table, const struct table_class *class)
+struct table_area_style *
+table_area_style_clone (struct pool *pool, const struct table_area_style *old)
{
- table->klass = class;
- table->n[TABLE_HORZ] = table->n[TABLE_VERT] = 0;
- table->h[TABLE_HORZ][0] = table->h[TABLE_HORZ][1] = 0;
- table->h[TABLE_VERT][0] = table->h[TABLE_VERT][1] = 0;
- table->ref_cnt = 1;
-}
-
-/* Sets the number of columns in TABLE to NC. */
-void
-table_set_nc (struct table *table, int nc)
-{
- assert (!table_is_shared (table));
- table->n[TABLE_HORZ] = nc;
-}
-
-/* Sets the number of rows in TABLE to NR. */
-void
-table_set_nr (struct table *table, int nr)
-{
- assert (!table_is_shared (table));
- table->n[TABLE_VERT] = nr;
-}
-\f
-struct area_style *
-area_style_clone (struct pool *pool, const struct area_style *old)
-{
- struct area_style *new = pool_malloc (pool, sizeof *new);
+ struct table_area_style *new = pool_malloc (pool, sizeof *new);
*new = *old;
if (new->font_style.typeface)
new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
}
void
-area_style_free (struct area_style *style)
+table_area_style_free (struct table_area_style *style)
{
if (style)
{
}
}
-/* Initializes CELL with the contents of the table cell at column X and row Y
- within TABLE. When CELL is no longer needed, the caller is responsible for
- freeing it by calling table_cell_free(CELL).
-
- The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
-void
-table_get_cell (const struct table *table, int x, int y,
- struct table_cell *cell)
-{
- assert (x >= 0 && x < table->n[TABLE_HORZ]);
- assert (y >= 0 && y < table->n[TABLE_VERT]);
-
- static const struct area_style default_style = AREA_STYLE_INITIALIZER;
- cell->style = &default_style;
-
- table->klass->get_cell (table, x, y, cell);
-}
-
-/* Frees CELL, which should have been initialized by calling
- table_get_cell(). */
-void
-table_cell_free (struct table_cell *cell)
-{
- if (cell->destructor != NULL)
- cell->destructor (cell->destructor_aux);
-}
-
-/* Returns one of the TAL_* enumeration constants (declared in output/table.h)
- representing a rule running alongside one of the cells in TABLE.
-
- Suppose NC is the number of columns in TABLE and NR is the number of rows.
- Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR. If (X,Y) =
- (0,0), the return value is the rule that runs vertically on the left side of
- cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
- cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
- cell (NC-1,0).
-
- The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
- within a 7x7 table. The '|' characters at the intersection of the X labels
- and Y labels show the rule whose style would be returned by calling
- table_get_rule with those X and Y values:
-
- 0 1 2 3 4 5 6 7
- +--+--+--+--+--+--+--+
- 0 | | | | | | | |
- +--+--+--+--+--+--+--+
- 1 | | | | | | | |
- +--+--+--+--+--+--+--+
- 2 | | | | | | | |
- +--+--+--+--+--+--+--+
- 3 | | | | | | | |
- +--+--+--+--+--+--+--+
- 4 | | | | | | | |
- +--+--+--+--+--+--+--+
- 5 | | | | | | | |
- +--+--+--+--+--+--+--+
- 6 | | | | | | | |
- +--+--+--+--+--+--+--+
-
- Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR. If
- (X,Y) = (0,0), the return value is the rule that runs horizontally above
- the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
- between that cell and cell (0,1); and so on, up to (0,NR), which runs
- horizontally below cell (0,NR-1). */
-int
-table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
- struct cell_color *color)
-{
- assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
- assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
- *color = (struct cell_color) CELL_COLOR_BLACK;
- return table->klass->get_rule (table, axis, x, y, color);
-}
-
void
table_cell_format_footnote_markers (const struct table_cell *cell,
struct string *s)
size_t n = 0;
struct table *t = item->table;
- for (int y = 0; y < table_nr (t); y++)
+ for (int y = 0; y < t->n[V]; y++)
{
struct table_cell cell;
- for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
+ for (int x = 0; x < t->n[H]; x = cell.d[TABLE_HORZ][1])
{
table_get_cell (t, x, y, &cell);
if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
footnotes = add_footnotes (cell.footnotes, cell.n_footnotes,
footnotes, &allocated, &n);
- table_cell_free (&cell);
}
}
footnotes = add_footnotes (title->footnotes, title->n_footnotes,
footnotes, &allocated, &n);
+ const struct table_item_layers *layers = table_item_get_layers (item);
+ if (layers)
+ {
+ for (size_t i = 0; i < layers->n_layers; i++)
+ footnotes = add_footnotes (layers->layers[i].footnotes,
+ layers->layers[i].n_footnotes,
+ footnotes, &allocated, &n);
+ }
+
const struct table_item_text *caption = table_item_get_caption (item);
if (caption)
footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
footnotes, &allocated, &n);
- *footnotesp = footnotes;
- return n;
-}
-\f
-struct table_string
- {
- struct table table;
- char *string;
- enum table_halign halign;
- };
-
-static const struct table_class table_string_class;
+ size_t n_nonnull = 0;
+ for (size_t i = 0; i < n; i++)
+ if (footnotes[i])
+ footnotes[n_nonnull++] = footnotes[i];
-/* Returns a table that contains a single cell, whose contents are S with
- options OPTIONS (a combination of TAB_* values). */
-struct table *
-table_from_string (enum table_halign halign, const char *s)
-{
- struct table_string *ts = xmalloc (sizeof *ts);
- table_init (&ts->table, &table_string_class);
- ts->table.n[TABLE_HORZ] = ts->table.n[TABLE_VERT] = 1;
- ts->string = xstrdup (s);
- ts->halign = halign;
- return &ts->table;
-}
-
-static struct table_string *
-table_string_cast (const struct table *table)
-{
- assert (table->klass == &table_string_class);
- return UP_CAST (table, struct table_string, table);
-}
-
-static void
-table_string_destroy (struct table *ts_)
-{
- struct table_string *ts = table_string_cast (ts_);
- free (ts->string);
- free (ts);
-}
-
-static void
-table_string_get_cell (const struct table *ts_, int x UNUSED, int y UNUSED,
- struct table_cell *cell)
-{
- static const struct area_style styles[] = {
-#define S(H) [H] = { AREA_STYLE_INITIALIZER__, .cell_style.halign = H }
- S(TABLE_HALIGN_LEFT),
- S(TABLE_HALIGN_CENTER),
- S(TABLE_HALIGN_RIGHT),
- S(TABLE_HALIGN_MIXED),
- S(TABLE_HALIGN_DECIMAL),
- };
- struct table_string *ts = table_string_cast (ts_);
- cell->d[TABLE_HORZ][0] = 0;
- cell->d[TABLE_HORZ][1] = 1;
- cell->d[TABLE_VERT][0] = 0;
- cell->d[TABLE_VERT][1] = 1;
- cell->options = 0;
- cell->style = &styles[table_halign_interpret (ts->halign, false)];
- cell->text = ts->string;
- cell->n_footnotes = 0;
- cell->destructor = NULL;
-}
-
-
-static int
-table_string_get_rule (const struct table *ts UNUSED,
- enum table_axis axis UNUSED, int x UNUSED, int y UNUSED,
- struct cell_color *color UNUSED)
-{
- return TAL_0;
+ *footnotesp = footnotes;
+ return n_nonnull;
}
-
-static const struct table_class table_string_class =
- {
- table_string_destroy,
- table_string_get_cell,
- table_string_get_rule,
- };
\f
const char *
table_halign_to_string (enum table_halign halign)
}
void
-font_style_copy (struct font_style *dst, const struct font_style *src)
+font_style_copy (struct pool *container,
+ struct font_style *dst, const struct font_style *src)
{
*dst = *src;
if (dst->typeface)
- dst->typeface = xstrdup (dst->typeface);
+ dst->typeface = pool_strdup (container, dst->typeface);
}
void
}
void
-area_style_copy (struct area_style *dst, const struct area_style *src)
+table_area_style_copy (struct pool *container, struct table_area_style *dst,
+ const struct table_area_style *src)
{
- font_style_copy (&dst->font_style, &src->font_style);
+ font_style_copy (container, &dst->font_style, &src->font_style);
dst->cell_style = src->cell_style;
}
void
-area_style_uninit (struct area_style *area)
+table_area_style_uninit (struct table_area_style *area)
{
if (area)
font_style_uninit (&area->font_style);
c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);
}
+\f
+
+static const bool debugging = true;
+
+/* Creates and returns a new table with NC columns and NR rows and initially no
+ header rows or columns.
+
+ Sets the number of header rows on each side of TABLE to HL on the
+ left, HR on the right, HT on the top, HB on the bottom. Header rows
+ are repeated when a table is broken across multiple columns or
+ multiple pages.
+
+ The table's cells are initially empty. */
+struct table *
+table_create (int nc, int nr, int hl, int hr, int ht, int hb)
+{
+ struct table *t;
+
+ t = pool_create_container (struct table, container);
+ t->n[TABLE_HORZ] = nc;
+ t->n[TABLE_VERT] = nr;
+ t->h[TABLE_HORZ][0] = hl;
+ t->h[TABLE_HORZ][1] = hr;
+ t->h[TABLE_VERT][0] = ht;
+ t->h[TABLE_VERT][1] = hb;
+ t->ref_cnt = 1;
+
+ t->cc = pool_calloc (t->container, nr * nc, sizeof *t->cc);
+ t->ct = pool_calloc (t->container, nr * nc, sizeof *t->ct);
+
+ t->rh = pool_nmalloc (t->container, nc, nr + 1);
+ memset (t->rh, TABLE_STROKE_NONE, nc * (nr + 1));
+
+ t->rv = pool_nmalloc (t->container, nr, nc + 1);
+ memset (t->rv, TABLE_STROKE_NONE, nr * (nc + 1));
+
+ memset (t->styles, 0, sizeof t->styles);
+ memset (t->rule_colors, 0, sizeof t->rule_colors);
+
+ return t;
+}
+\f
+/* Rules. */
+
+/* Draws a vertical line to the left of cells at horizontal position X
+ from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
+void
+table_vline (struct table *t, int style, int x, int y1, int y2)
+{
+ if (debugging)
+ {
+ if (x < 0 || x > t->n[H]
+ || y1 < 0 || y1 >= t->n[V]
+ || y2 < 0 || y2 >= t->n[V])
+ {
+ printf ("bad vline: x=%d y=(%d,%d) in table size (%d,%d)\n",
+ x, y1, y2, t->n[H], t->n[V]);
+ return;
+ }
+ }
+
+ assert (x >= 0);
+ assert (x <= t->n[H]);
+ assert (y1 >= 0);
+ assert (y2 >= y1);
+ assert (y2 <= t->n[V]);
+
+ if (style != -1)
+ {
+ int y;
+ for (y = y1; y <= y2; y++)
+ t->rv[x + (t->n[H] + 1) * y] = style;
+ }
+}
+
+/* Draws a horizontal line above cells at vertical position Y from X1
+ to X2 inclusive in style STYLE, if style is not -1. */
+void
+table_hline (struct table *t, int style, int x1, int x2, int y)
+{
+ if (debugging)
+ {
+ if (y < 0 || y > t->n[V]
+ || x1 < 0 || x1 >= t->n[H]
+ || x2 < 0 || x2 >= t->n[H])
+ {
+ printf ("bad hline: x=(%d,%d) y=%d in table size (%d,%d)\n",
+ x1, x2, y, t->n[H], t->n[V]);
+ return;
+ }
+ }
+
+ assert (y >= 0);
+ assert (y <= t->n[V]);
+ assert (x2 >= x1);
+ assert (x1 >= 0);
+ assert (x2 < t->n[H]);
+
+ if (style != -1)
+ {
+ int x;
+ for (x = x1; x <= x2; x++)
+ t->rh[x + t->n[H] * y] = style;
+ }
+}
+
+/* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
+ lines of style F_H and vertical lines of style F_V. Fills the
+ interior of the box with horizontal lines of style I_H and vertical
+ lines of style I_V. Any of the line styles may be -1 to avoid
+ drawing those lines. This is distinct from 0, which draws a null
+ line. */
+void
+table_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
+ int x1, int y1, int x2, int y2)
+{
+ if (debugging)
+ {
+ if (x1 < 0 || x1 >= t->n[H]
+ || x2 < 0 || x2 >= t->n[H]
+ || y1 < 0 || y1 >= t->n[V]
+ || y2 < 0 || y2 >= t->n[V])
+ {
+ printf ("bad box: (%d,%d)-(%d,%d) in table size (%d,%d)\n",
+ x1, y1, x2, y2, t->n[H], t->n[V]);
+ NOT_REACHED ();
+ }
+ }
+
+ assert (x2 >= x1);
+ assert (y2 >= y1);
+ assert (x1 >= 0);
+ assert (y1 >= 0);
+ assert (x2 < t->n[H]);
+ assert (y2 < t->n[V]);
+
+ if (f_h != -1)
+ {
+ int x;
+ for (x = x1; x <= x2; x++)
+ {
+ t->rh[x + t->n[H] * y1] = f_h;
+ t->rh[x + t->n[H] * (y2 + 1)] = f_h;
+ }
+ }
+ if (f_v != -1)
+ {
+ int y;
+ for (y = y1; y <= y2; y++)
+ {
+ t->rv[x1 + (t->n[H] + 1) * y] = f_v;
+ t->rv[(x2 + 1) + (t->n[H] + 1) * y] = f_v;
+ }
+ }
+
+ if (i_h != -1)
+ {
+ int y;
+
+ for (y = y1 + 1; y <= y2; y++)
+ {
+ int x;
+
+ for (x = x1; x <= x2; x++)
+ t->rh[x + t->n[H] * y] = i_h;
+ }
+ }
+ if (i_v != -1)
+ {
+ int x;
+
+ for (x = x1 + 1; x <= x2; x++)
+ {
+ int y;
+
+ for (y = y1; y <= y2; y++)
+ t->rv[x + (t->n[H] + 1) * y] = i_v;
+ }
+ }
+}
+\f
+/* Cells. */
+
+static void
+do_table_text (struct table *table, int c, int r, unsigned opt, char *text)
+{
+ assert (c >= 0);
+ assert (r >= 0);
+ assert (c < table->n[H]);
+ assert (r < table->n[V]);
+
+ if (debugging)
+ {
+ if (c < 0 || r < 0 || c >= table->n[H] || r >= table->n[V])
+ {
+ printf ("table_text(): bad cell (%d,%d) in table size (%d,%d)\n",
+ c, r, table->n[H], table->n[V]);
+ return;
+ }
+ }
+
+ table->cc[c + r * table->n[H]] = text;
+ table->ct[c + r * table->n[H]] = opt;
+}
+
+/* Sets cell (C,R) in TABLE, with options OPT, to have text value
+ TEXT. */
+void
+table_text (struct table *table, int c, int r, unsigned opt,
+ const char *text)
+{
+ do_table_text (table, c, r, opt, pool_strdup (table->container, text));
+}
+
+/* Sets cell (C,R) in TABLE, with options OPT, to have text value
+ FORMAT, which is formatted as if passed to printf. */
+void
+table_text_format (struct table *table, int c, int r, unsigned opt,
+ const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ do_table_text (table, c, r, opt,
+ pool_vasprintf (table->container, format, args));
+ va_end (args);
+}
+
+static struct table_cell *
+add_joined_cell (struct table *table, int x1, int y1, int x2, int y2,
+ unsigned opt)
+{
+ assert (x1 >= 0);
+ assert (y1 >= 0);
+ assert (y2 >= y1);
+ assert (x2 >= x1);
+ assert (y2 < table->n[V]);
+ assert (x2 < table->n[H]);
+
+ if (debugging)
+ {
+ if (x1 < 0 || x1 >= table->n[H]
+ || y1 < 0 || y1 >= table->n[V]
+ || x2 < x1 || x2 >= table->n[H]
+ || y2 < y1 || y2 >= table->n[V])
+ {
+ printf ("table_joint_text(): bad cell "
+ "(%d,%d)-(%d,%d) in table size (%d,%d)\n",
+ x1, y1, x2, y2, table->n[H], table->n[V]);
+ return NULL;
+ }
+ }
+
+ table_box (table, -1, -1, TABLE_STROKE_NONE, TABLE_STROKE_NONE,
+ x1, y1, x2, y2);
+
+ struct table_cell *cell = pool_alloc (table->container, sizeof *cell);
+ *cell = (struct table_cell) {
+ .d = { [TABLE_HORZ] = { x1, ++x2 },
+ [TABLE_VERT] = { y1, ++y2 } },
+ .options = opt,
+ };
+
+ void **cc = &table->cc[x1 + y1 * table->n[H]];
+ unsigned short *ct = &table->ct[x1 + y1 * table->n[H]];
+ const int ofs = table->n[H] - (x2 - x1);
+ for (int y = y1; y < y2; y++)
+ {
+ for (int x = x1; x < x2; x++)
+ {
+ *cc++ = cell;
+ *ct++ = opt | TAB_JOIN;
+ }
+
+ cc += ofs;
+ ct += ofs;
+ }
+
+ return cell;
+}
+
+/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
+ options OPT to have text value TEXT. */
+void
+table_joint_text (struct table *table, int x1, int y1, int x2, int y2,
+ unsigned opt, const char *text)
+{
+ char *s = pool_strdup (table->container, text);
+ if (x1 == x2 && y1 == y2)
+ do_table_text (table, x1, y1, opt, s);
+ else
+ add_joined_cell (table, x1, y1, x2, y2, opt)->text = s;
+}
+
+static struct table_cell *
+get_joined_cell (struct table *table, int x, int y)
+{
+ int index = x + y * table->n[H];
+ unsigned short opt = table->ct[index];
+ struct table_cell *cell;
+
+ if (opt & TAB_JOIN)
+ cell = table->cc[index];
+ else
+ {
+ char *text = table->cc[index];
+
+ cell = add_joined_cell (table, x, y, x, y, table->ct[index]);
+ cell->text = text ? text : pool_strdup (table->container, "");
+ }
+ return cell;
+}
+
+/* Sets the subscripts for column X, row Y in TABLE. */
+void
+table_add_subscripts (struct table *table, int x, int y,
+ char **subscripts, size_t n_subscripts)
+{
+ struct table_cell *cell = get_joined_cell (table, x, y);
+
+ cell->n_subscripts = n_subscripts;
+ cell->subscripts = pool_nalloc (table->container, n_subscripts,
+ sizeof *cell->subscripts);
+ for (size_t i = 0; i < n_subscripts; i++)
+ cell->subscripts[i] = pool_strdup (table->container, subscripts[i]);
+}
+
+/* Create a footnote in TABLE with MARKER (e.g. "a") as its marker and CONTENT
+ as its content. The footnote will be styled as STYLE, which is mandatory.
+ IDX must uniquely identify the footnote within TABLE.
+
+ Returns the new footnote. The return value is the only way to get to the
+ footnote later, so it is important for the caller to remember it. */
+struct footnote *
+table_create_footnote (struct table *table, size_t idx, const char *content,
+ const char *marker, struct table_area_style *style)
+{
+ assert (style);
+
+ struct footnote *f = pool_alloc (table->container, sizeof *f);
+ f->idx = idx;
+ f->content = pool_strdup (table->container, content);
+ f->marker = pool_strdup (table->container, marker);
+ f->style = style;
+ return f;
+}
+
+/* Attaches a reference to footnote F to the cell at column X, row Y in
+ TABLE. */
+void
+table_add_footnote (struct table *table, int x, int y,
+ const struct footnote *f)
+{
+ assert (f->style);
+
+ struct table_cell *cell = get_joined_cell (table, x, y);
+
+ cell->footnotes = pool_realloc (
+ table->container, cell->footnotes,
+ (cell->n_footnotes + 1) * sizeof *cell->footnotes);
+
+ cell->footnotes[cell->n_footnotes++] = f;
+}
+
+/* Overrides the style for column X, row Y in TABLE with STYLE.
+ Does not make a copy of STYLE, so it should either be allocated from
+ TABLE->container or have a lifetime that will outlive TABLE. */
+void
+table_add_style (struct table *table, int x, int y,
+ const struct table_area_style *style)
+{
+ get_joined_cell (table, x, y)->style = style;
+}
+
+/* Returns true if column C, row R has no contents, otherwise false. */
+bool
+table_cell_is_empty (const struct table *table, int c, int r)
+{
+ return table->cc[c + r * table->n[H]] == NULL;
+}
+\f
+/* Initializes CELL with the contents of the table cell at column X and row Y
+ within TABLE. When CELL is no longer needed, the caller is responsible for
+ freeing it by calling table_cell_free(CELL).
+
+ The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
+void
+table_get_cell (const struct table *t, int x, int y, struct table_cell *cell)
+{
+ assert (x >= 0 && x < t->n[TABLE_HORZ]);
+ assert (y >= 0 && y < t->n[TABLE_VERT]);
+
+ int index = x + y * t->n[H];
+ unsigned short opt = t->ct[index];
+ const void *cc = t->cc[index];
+
+ const struct table_area_style *style
+ = t->styles[(opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT];
+ if (opt & TAB_JOIN)
+ {
+ const struct table_cell *jc = cc;
+ *cell = *jc;
+ if (!cell->style)
+ cell->style = style;
+ }
+ else
+ *cell = (struct table_cell) {
+ .d = { [TABLE_HORZ] = { x, x + 1 },
+ [TABLE_VERT] = { y, y + 1 } },
+ .options = opt,
+ .text = CONST_CAST (char *, cc ? cc : ""),
+ .style = style,
+ };
+
+ assert (cell->style);
+}
+
+/* Returns one of the TAL_* enumeration constants (declared in output/table.h)
+ representing a rule running alongside one of the cells in TABLE.
+
+ Suppose NC is the number of columns in TABLE and NR is the number of rows.
+ Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR. If (X,Y) =
+ (0,0), the return value is the rule that runs vertically on the left side of
+ cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
+ cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
+ cell (NC-1,0).
+
+ The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
+ within a 7x7 table. The '|' characters at the intersection of the X labels
+ and Y labels show the rule whose style would be returned by calling
+ table_get_rule with those X and Y values:
+
+ 0 1 2 3 4 5 6 7
+ +--+--+--+--+--+--+--+
+ 0 | | | | | | | |
+ +--+--+--+--+--+--+--+
+ 1 | | | | | | | |
+ +--+--+--+--+--+--+--+
+ 2 | | | | | | | |
+ +--+--+--+--+--+--+--+
+ 3 | | | | | | | |
+ +--+--+--+--+--+--+--+
+ 4 | | | | | | | |
+ +--+--+--+--+--+--+--+
+ 5 | | | | | | | |
+ +--+--+--+--+--+--+--+
+ 6 | | | | | | | |
+ +--+--+--+--+--+--+--+
+
+ Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR. If
+ (X,Y) = (0,0), the return value is the rule that runs horizontally above
+ the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
+ between that cell and cell (0,1); and so on, up to (0,NR), which runs
+ horizontally below cell (0,NR-1). */
+int
+table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
+ struct cell_color *color)
+{
+ assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
+ assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
+
+ uint8_t raw = (axis == TABLE_VERT
+ ? table->rh[x + table->n[H] * y]
+ : table->rv[x + (table->n[H] + 1) * y]);
+ struct cell_color *p = table->rule_colors[(raw & TAB_RULE_STYLE_MASK)
+ >> TAB_RULE_STYLE_SHIFT];
+ *color = p ? *p : (struct cell_color) CELL_COLOR_BLACK;
+ return (raw & TAB_RULE_TYPE_MASK) >> TAB_RULE_TYPE_SHIFT;
+}