+
+ add_joined_cell (table, x1, y1, x2, y2, opt)->u.text = s;
+}
+
+void
+tab_footnote (struct tab_table *table, int x, int y, const char *format, ...)
+{
+ int index = x + y * table->cf;
+ unsigned char opt = table->ct[index];
+ struct tab_joined_cell *j;
+ va_list args;
+
+ if (opt & TAB_JOIN)
+ j = table->cc[index];
+ else
+ {
+ char *text = table->cc[index];
+
+ j = add_joined_cell (table, x, y, x, y, table->ct[index]);
+ j->u.text = text ? text : xstrdup ("");
+ }
+
+ j->footnotes = xrealloc (j->footnotes,
+ (j->n_footnotes + 1) * sizeof *j->footnotes);
+
+ va_start (args, format);
+ j->footnotes[j->n_footnotes++] = pool_vasprintf (table->container, format, args);
+ va_end (args);
+}
+
+static void
+subtable_unref (void *subtable)
+{
+ table_item_unref (subtable);
+}
+
+/* Places SUBTABLE as the content for cells (X1,X2)-(Y1,Y2) inclusive in TABLE
+ with options OPT. */
+void
+tab_subtable (struct tab_table *table, int x1, int y1, int x2, int y2,
+ unsigned opt, struct table_item *subtable)
+{
+ add_joined_cell (table, x1, y1, x2, y2, opt | TAB_SUBTABLE)->u.subtable
+ = subtable;
+ pool_register (table->container, subtable_unref, subtable);
+}
+
+/* Places the contents of SUBTABLE as the content for cells (X1,X2)-(Y1,Y2)
+ inclusive in TABLE with options OPT.
+
+ SUBTABLE must have exactly one row and column. The contents of its single
+ cell are used as the contents of TABLE's cell; that is, SUBTABLE is not used
+ as a nested table but its contents become part of TABLE. */
+void
+tab_subtable_bare (struct tab_table *table, int x1, int y1, int x2, int y2,
+ unsigned opt, struct table_item *subtable)
+{
+ const struct table *t UNUSED = table_item_get_table (subtable);
+ assert (table_nc (t) == 1);
+ assert (table_nr (t) == 1);
+ tab_subtable (table, x1, y1, x2, y2, opt | TAB_BARE, subtable);