-/* PSPP - computes sample statistics.
- Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
- Written by Ben Pfaff <blp@gnu.org>.
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009, 2011, 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 the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
+ 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
-#include "table.h"
+#include "output/table.h"
+#include "output/table-provider.h"
-#include <ctype.h>
-#include <stdarg.h>
-#include <limits.h>
+#include <assert.h>
+#include <inttypes.h>
#include <stdlib.h>
-#include "output.h"
-#include "manager.h"
+#include "data/format.h"
+#include "libpspp/assertion.h"
+#include "libpspp/cast.h"
+#include "libpspp/compiler.h"
+#include "libpspp/pool.h"
+#include "libpspp/str.h"
+#include "output/table-item.h"
+#include "output/table.h"
+#include "output/text-item.h"
-#include <data/data-out.h>
-#include <data/format.h>
-#include <data/variable.h>
-#include <libpspp/alloc.h>
-#include <libpspp/assertion.h>
-#include <libpspp/compiler.h>
-#include <libpspp/magic.h>
-#include <libpspp/misc.h>
-#include <libpspp/pool.h>
+#include "gl/xalloc.h"
-#include "minmax.h"
+/* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
+#define H TABLE_HORZ
+#define V TABLE_VERT
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
+/* Increases TABLE's reference count, indicating that it has an additional
+ owner. An table that is shared among multiple owners must not be
+ modified. */
+struct table *
+table_ref (const struct table *table_)
+{
+ struct table *table = CONST_CAST (struct table *, table_);
+ table->ref_cnt++;
+ return table;
+}
+
+/* Decreases TABLE's reference count, indicating that it has one fewer owner.
+ If TABLE no longer has any owners, it is freed. */
+void
+table_unref (struct table *table)
+{
+ if (table != NULL)
+ {
+ assert (table->ref_cnt > 0);
+ if (--table->ref_cnt == 0)
+ pool_destroy (table->container);
+ }
+}
+
+/* Returns true if TABLE has more than one owner. A table item that is shared
+ among multiple owners must not be modified. */
+bool
+table_is_shared (const struct table *table)
+{
+ return table->ref_cnt > 1;
+}
\f
-const struct som_table_class tab_table_class;
-static char *command_name;
+struct table_area_style *
+table_area_style_clone (struct pool *pool, const struct table_area_style *old)
+{
+ 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);
+ return new;
+}
-/* Returns the font to use for a cell with the given OPTIONS. */
-static enum outp_font
-options_to_font (unsigned options)
+void
+table_area_style_free (struct table_area_style *style)
{
- return (options & TAB_FIX ? OUTP_FIXED
- : options & TAB_EMPH ? OUTP_EMPHASIS
- : OUTP_PROPORTIONAL);
+ if (style)
+ {
+ free (style->font_style.typeface);
+ free (style);
+ }
}
-/* Creates a table with NC columns and NR rows. */
-struct tab_table *
-tab_create (int nc, int nr, int reallocable UNUSED)
+struct footnote *
+footnote_clone (const struct footnote *old)
{
- struct tab_table *t;
+ struct footnote *new = xmalloc (sizeof *new);
+ *new = (struct footnote) {
+ .idx = old->idx,
+ .content = old->content ? xstrdup (old->content) : NULL,
+ .marker = old->marker ? xstrdup (old->marker) : NULL,
+ .style = old->style ? table_area_style_clone (NULL, old->style) : NULL,
+ };
+ return new;
+}
- t = pool_create_container (struct tab_table, container);
- t->col_style = TAB_COL_NONE;
- t->col_group = 0;
- t->title = NULL;
- t->flags = SOMF_NONE;
- t->nr = nr;
- t->nc = t->cf = nc;
- t->l = t->r = t->t = t->b = 0;
+void
+footnote_destroy (struct footnote *f)
+{
+ if (f)
+ {
+ free (f->content);
+ free (f->marker);
+ if (f->style)
+ {
+ table_area_style_uninit (f->style);
+ free (f->style);
+ }
+ free (f);
+ }
+}
- t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
- t->ct = pool_malloc (t->container, nr * nc);
- memset (t->ct, TAB_EMPTY, nc * nr);
+struct table_cell *
+table_cell_clone (const struct table_cell *old)
+{
+ struct table_cell *new = xmalloc (sizeof *new);
+ *new = *old;
+ new->text = xstrdup (new->text);
- t->rh = pool_nmalloc (t->container, nc, nr + 1);
- memset (t->rh, 0, nc * (nr + 1));
+ if (old->n_subscripts)
+ {
+ new->subscripts = xnmalloc (old->n_subscripts, sizeof *new->subscripts);
+ for (size_t i = 0; i < old->n_subscripts; i++)
+ new->subscripts[i] = xstrdup (old->subscripts[i]);
+ }
+ else
+ new->subscripts = NULL;
- t->rv = pool_nmalloc (t->container, nr, nc + 1);
- memset (t->rv, UCHAR_MAX, nr * (nc + 1));
+ if (old->n_footnotes)
+ {
+ new->footnotes = xnmalloc (old->n_footnotes, sizeof *new->footnotes);
+ for (size_t i = 0; i < old->n_footnotes; i++)
+ new->footnotes[i] = footnote_clone (old->footnotes[i]);
+ }
+ else
+ new->footnotes = NULL;
- t->dim = NULL;
- t->w = t->h = NULL;
- t->col_ofs = t->row_ofs = 0;
+ if (old->style)
+ new->style = table_area_style_clone (NULL, old->style);
- return t;
+ return new;
}
-/* Destroys table T. */
void
-tab_destroy (struct tab_table *t)
+table_cell_destroy (struct table_cell *cell)
{
- assert (t != NULL);
- free (t->title);
- pool_destroy (t->container);
+ if (!cell)
+ return;
+
+ free (cell->text);
+ for (size_t i = 0; i < cell->n_subscripts; i++)
+ free (cell->subscripts[i]);
+ free (cell->subscripts);
+ for (size_t i = 0; i < cell->n_footnotes; i++)
+ footnote_destroy (cell->footnotes[i]);
+ free (cell->footnotes);
+ if (cell->style)
+ {
+ table_area_style_uninit (cell->style);
+ free (cell->style);
+ }
+ free (cell);
}
-/* Sets the width and height of a table, in columns and rows,
- respectively. Use only to reduce the size of a table, since it
- does not change the amount of allocated memory. */
void
-tab_resize (struct tab_table *t, int nc, int nr)
+table_cell_format_footnote_markers (const struct table_cell *cell,
+ struct string *s)
{
- assert (t != NULL);
- if (nc != -1)
+ for (size_t i = 0; i < cell->n_footnotes; i++)
{
- assert (nc + t->col_ofs <= t->cf);
- t->nc = nc + t->col_ofs;
+ if (i)
+ ds_put_byte (s, ',');
+ ds_put_cstr (s, cell->footnotes[i]->marker);
}
- if (nr != -1)
+}
+
+static struct footnote **
+add_footnotes (struct footnote **refs, size_t n_refs,
+ struct footnote **footnotes, size_t *allocated, size_t *n)
+{
+ for (size_t i = 0; i < n_refs; i++)
{
- assert (nr + t->row_ofs <= t->nr);
- t->nr = nr + t->row_ofs;
+ struct footnote *f = refs[i];
+ if (f->idx >= *allocated)
+ {
+ size_t new_allocated = (f->idx + 1) * 2;
+ footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
+ while (*allocated < new_allocated)
+ footnotes[(*allocated)++] = NULL;
+ }
+ footnotes[f->idx] = f;
+ if (f->idx >= *n)
+ *n = f->idx + 1;
}
+ return footnotes;
}
-/* Changes either or both dimensions of a table. Consider using the
- above routine instead if it won't waste a lot of space.
+size_t
+table_collect_footnotes (const struct table_item *item,
+ struct footnote ***footnotesp)
+{
+ struct footnote **footnotes = NULL;
+ size_t allocated = 0;
+ size_t n = 0;
- Changing the number of columns in a table is particularly expensive
- in space and time. Avoid doing such. FIXME: In fact, transferring
- of rules isn't even implemented yet. */
-void
-tab_realloc (struct tab_table *t, int nc, int nr)
+ struct table *t = item->table;
+ for (int y = 0; y < t->n[V]; y++)
+ {
+ struct table_cell cell;
+ 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);
+ }
+ }
+
+ const struct table_cell *title = table_item_get_title (item);
+ if (title)
+ 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_cell *caption = table_item_get_caption (item);
+ if (caption)
+ footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
+ footnotes, &allocated, &n);
+
+ size_t n_nonnull = 0;
+ for (size_t i = 0; i < n; i++)
+ if (footnotes[i])
+ footnotes[n_nonnull++] = footnotes[i];
+
+ *footnotesp = footnotes;
+ return n_nonnull;
+}
+\f
+const char *
+table_halign_to_string (enum table_halign halign)
{
- int ro, co;
-
- assert (t != NULL);
- ro = t->row_ofs;
- co = t->col_ofs;
- if (ro || co)
- tab_offset (t, 0, 0);
-
- if (nc == -1)
- nc = t->nc;
- if (nr == -1)
- nr = t->nr;
-
- assert (nc == t->nc);
-
- if (nc > t->cf)
+ switch (halign)
{
- int mr1 = MIN (nr, t->nr);
- int mc1 = MIN (nc, t->nc);
-
- struct substring *new_cc;
- unsigned char *new_ct;
- int r;
-
- new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
- new_ct = pool_malloc (t->container, nr * nc);
- for (r = 0; r < mr1; r++)
- {
- memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
- memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
- memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
- }
- pool_free (t->container, t->cc);
- pool_free (t->container, t->ct);
- t->cc = new_cc;
- t->ct = new_ct;
- t->cf = nc;
+ case TABLE_HALIGN_LEFT: return "left";
+ case TABLE_HALIGN_CENTER: return "center";
+ case TABLE_HALIGN_RIGHT: return "right";
+ case TABLE_HALIGN_DECIMAL: return "decimal";
+ case TABLE_HALIGN_MIXED: return "mixed";
+ default: return "**error**";
}
- else if (nr != t->nr)
+}
+
+const char *
+table_valign_to_string (enum table_valign valign)
+{
+ switch (valign)
{
- t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
- t->ct = pool_realloc (t->container, t->ct, nr * nc);
-
- t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
- t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
-
- if (nr > t->nr)
- {
- memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
- memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
- (nr - t->nr) * (nc + 1));
- }
+ case TABLE_VALIGN_TOP: return "top";
+ case TABLE_VALIGN_CENTER: return "center";
+ case TABLE_VALIGN_BOTTOM: return "bottom";
+ default: return "**error**";
}
+}
- memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
-
- t->nr = nr;
- t->nc = nc;
+enum table_halign
+table_halign_interpret (enum table_halign halign, bool numeric)
+{
+ switch (halign)
+ {
+ case TABLE_HALIGN_LEFT:
+ case TABLE_HALIGN_CENTER:
+ case TABLE_HALIGN_RIGHT:
+ return halign;
+
+ case TABLE_HALIGN_MIXED:
+ return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT;
- if (ro || co)
- tab_offset (t, co, ro);
+ case TABLE_HALIGN_DECIMAL:
+ return TABLE_HALIGN_DECIMAL;
+
+ default:
+ NOT_REACHED ();
+ }
}
-/* Sets the number of header rows on each side of TABLE to L on the
- left, R on the right, T on the top, B on the bottom. Header rows
- are repeated when a table is broken across multiple columns or
- multiple pages. */
void
-tab_headers (struct tab_table *table, int l, int r, int t, int b)
+font_style_copy (struct pool *container,
+ struct font_style *dst, const struct font_style *src)
{
- assert (table != NULL);
- assert (l < table->nc);
- assert (r < table->nc);
- assert (t < table->nr);
- assert (b < table->nr);
+ *dst = *src;
+ if (dst->typeface)
+ dst->typeface = pool_strdup (container, dst->typeface);
+}
+void
+font_style_uninit (struct font_style *font)
+{
+ if (font)
+ free (font->typeface);
+}
+
+void
+table_area_style_copy (struct pool *container, struct table_area_style *dst,
+ const struct table_area_style *src)
+{
+ font_style_copy (container, &dst->font_style, &src->font_style);
+ dst->cell_style = src->cell_style;
+}
- table->l = l;
- table->r = r;
- table->t = t;
- table->b = b;
+void
+table_area_style_uninit (struct table_area_style *area)
+{
+ if (area)
+ font_style_uninit (&area->font_style);
}
-/* Set up table T so that, when it is an appropriate size, it will be
- displayed across the page in columns.
+const char *
+table_stroke_to_string (enum table_stroke stroke)
+{
+ switch (stroke)
+ {
+ case TABLE_STROKE_NONE: return "none";
+ case TABLE_STROKE_SOLID: return "solid";
+ case TABLE_STROKE_DASHED: return "dashed";
+ case TABLE_STROKE_THICK: return "thick";
+ case TABLE_STROKE_THIN: return "thin";
+ case TABLE_STROKE_DOUBLE: return "double";
+ default:
+ return "**error**";
+ }
+}
- STYLE is a TAB_COL_* constant. GROUP is the number of rows to take
- as a unit. */
void
-tab_columns (struct tab_table *t, int style, int group)
+cell_color_dump (const struct cell_color *c)
{
- assert (t != NULL);
- t->col_style = style;
- t->col_group = group;
+ if (c->alpha != 255)
+ printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
+ else
+ printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
+}
+
+void
+font_style_dump (const struct font_style *f)
+{
+ printf ("%s %dpx ", f->typeface, f->size);
+ cell_color_dump (&f->fg[0]);
+ putchar ('/');
+ cell_color_dump (&f->bg[0]);
+ if (!cell_color_equal (&f->fg[0], &f->fg[1])
+ || !cell_color_equal (&f->bg[0], &f->bg[1]))
+ {
+ printf (" alt=");
+ cell_color_dump (&f->fg[1]);
+ putchar ('/');
+ cell_color_dump (&f->bg[1]);
+ }
+ if (f->bold)
+ fputs (" bold", stdout);
+ if (f->italic)
+ fputs (" italic", stdout);
+ if (f->underline)
+ fputs (" underline", stdout);
+}
+
+bool
+font_style_equal (const struct font_style *a, const struct font_style *b)
+{
+ return (a->bold == b->bold
+ && a->italic == b->italic
+ && a->underline == b->underline
+ && a->markup == b->markup
+ && cell_color_equal (&a->fg[0], &b->fg[0])
+ && cell_color_equal (&a->fg[1], &b->fg[1])
+ && cell_color_equal (&a->bg[0], &b->bg[0])
+ && cell_color_equal (&a->bg[1], &b->bg[1])
+ && !strcmp (a->typeface ? a->typeface : "",
+ b->typeface ? b->typeface : "")
+ && a->size == b->size);
+}
+
+void
+cell_style_dump (const struct cell_style *c)
+{
+ fputs (table_halign_to_string (c->halign), stdout);
+ if (c->halign == TABLE_HALIGN_DECIMAL)
+ printf ("(%.2gpx)", c->decimal_offset);
+ printf (" %s", table_valign_to_string (c->valign));
+ printf (" %d,%d,%d,%dpx",
+ 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
-tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
+table_vline (struct table *t, int style, int x, int y1, int y2)
{
- assert (t != NULL);
-
-#if DEBUGGING
- if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
- || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
- || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
+ if (debugging)
{
- printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
- "table size (%d,%d)\n"),
- x, t->col_ofs, x + t->col_ofs,
- y1, t->row_ofs, y1 + t->row_ofs,
- y2, t->row_ofs, y2 + t->row_ofs,
- t->nc, t->nr);
- return;
+ 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;
+ }
}
-#endif
-
- x += t->col_ofs;
- y1 += t->row_ofs;
- y2 += t->row_ofs;
- assert (x > 0);
- assert (x < t->nc);
+ assert (x >= 0);
+ assert (x <= t->n[H]);
assert (y1 >= 0);
assert (y2 >= y1);
- assert (y2 <= t->nr);
+ assert (y2 <= t->n[V]);
if (style != -1)
{
int y;
for (y = y1; y <= y2; y++)
- t->rv[x + (t->cf + 1) * y] = style;
+ 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
-tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
+table_hline (struct table *t, int style, int x1, int x2, int y)
{
- assert (t != NULL);
-
- x1 += t->col_ofs;
- x2 += t->col_ofs;
- y += t->row_ofs;
+ 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->nr);
- assert (x2 >= x1 );
- assert (x1 >= 0 );
- assert (x2 < t->nc);
+ 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->cf * y] = style;
+ t->rh[x + t->n[H] * y] = style;
}
}
drawing those lines. This is distinct from 0, which draws a null
line. */
void
-tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
- int x1, int y1, int x2, int y2)
+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)
{
- assert (t != NULL);
-
-#if DEBUGGING
- if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
- || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
- || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
- || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
+ if (debugging)
{
- printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
- "in table size (%d,%d)\n"),
- x1, t->col_ofs, x1 + t->col_ofs,
- y1, t->row_ofs, y1 + t->row_ofs,
- x2, t->col_ofs, x2 + t->col_ofs,
- y2, t->row_ofs, y2 + t->row_ofs,
- t->nc, t->nr);
- NOT_REACHED ();
+ 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 ();
+ }
}
-#endif
-
- x1 += t->col_ofs;
- x2 += t->col_ofs;
- y1 += t->row_ofs;
- y2 += t->row_ofs;
assert (x2 >= x1);
assert (y2 >= y1);
assert (x1 >= 0);
assert (y1 >= 0);
- assert (x2 < t->nc);
- assert (y2 < t->nr);
+ 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->cf * y1] = f_h;
- t->rh[x + t->cf * (y2 + 1)] = f_h;
+ 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->cf + 1) * y] = f_v;
- t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
+ 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;
+ {
+ int x;
for (x = x1; x <= x2; x++)
- t->rh[x + t->cf * y] = i_h;
- }
+ 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->cf + 1) * y] = i_v;
- }
- }
-}
-
-/* Formats text TEXT and arguments ARGS as indicated in OPT in
- TABLE's pool and returns the resultant string. */
-static struct substring
-text_format (struct tab_table *table, int opt, const char *text, va_list args)
-{
- assert (table != NULL && text != NULL);
-
- return ss_cstr (opt & TAT_PRINTF
- ? pool_vasprintf (table->container, text, args)
- : pool_strdup (table->container, text));
-}
-
-/* Set the title of table T to TITLE, which is formatted as if
- passed to printf(). */
-void
-tab_title (struct tab_table *t, const char *title, ...)
-{
- va_list args;
-
- assert (t != NULL && title != NULL);
- va_start (args, title);
- t->title = xvasprintf (title, args);
- va_end (args);
-}
-/* Set DIM_FUNC as the dimension function for table T. */
-void
-tab_dim (struct tab_table *t, tab_dim_func *dim_func)
-{
- assert (t != NULL && t->dim == NULL);
- t->dim = dim_func;
-}
+ for (x = x1 + 1; x <= x2; x++)
+ {
+ int y;
-/* Returns the natural width of column C in table T for driver D, that
- is, the smallest width necessary to display all its cells without
- wrapping. The width will be no larger than the page width minus
- left and right rule widths. */
-int
-tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
-{
- int width;
-
- assert (t != NULL && c >= 0 && c < t->nc);
- {
- int r;
-
- for (width = r = 0; r < t->nr; r++)
- {
- struct outp_text text;
- unsigned char opt = t->ct[c + r * t->cf];
- int w;
-
- if (opt & (TAB_JOIN | TAB_EMPTY))
- continue;
-
- text.string = t->cc[c + r * t->cf];
- text.justification = OUTP_LEFT;
- text.font = options_to_font (opt);
- text.h = text.v = INT_MAX;
-
- d->class->text_metrics (d, &text, &w, NULL);
- if (w > width)
- width = w;
- }
- }
-
- if (width == 0)
- {
- /* FIXME: This is an ugly kluge to compensate for the fact
- that we don't let joined cells contribute to column
- widths. */
- width = d->prop_em_width * 8;
+ for (y = y1; y <= y2; y++)
+ t->rv[x + (t->n[H] + 1) * y] = i_v;
+ }
}
-
- {
- const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
-
- if (width > clamp)
- width = clamp;
- }
-
- return width;
}
-
-/* Returns the natural height of row R in table T for driver D, that
- is, the minimum height necessary to display the information in the
- cell at the widths set for each column. */
-int
-tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
-{
- int height;
-
- assert (t != NULL && r >= 0 && r < t->nr);
-
- {
- int c;
-
- for (height = d->font_height, c = 0; c < t->nc; c++)
- {
- struct outp_text text;
- unsigned char opt = t->ct[c + r * t->cf];
- int h;
-
- assert (t->w[c] != NOT_INT);
- if (opt & (TAB_JOIN | TAB_EMPTY))
- continue;
-
- text.string = t->cc[c + r * t->cf];
- text.justification = OUTP_LEFT;
- text.font = options_to_font (opt);
- text.h = t->w[c];
- text.v = INT_MAX;
- d->class->text_metrics (d, &text, NULL, &h);
-
- if (h > height)
- height = h;
- }
- }
-
- return height;
-}
-
-/* Callback function to set all columns and rows to their natural
- dimensions. Not really meant to be called directly. */
-void
-tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
-{
- int i;
-
- assert (t != NULL);
-
- for (i = 0; i < t->nc; i++)
- t->w[i] = tab_natural_width (t, d, i);
-
- for (i = 0; i < t->nr; i++)
- t->h[i] = tab_natural_height (t, d, i);
-}
-
\f
/* Cells. */
-/* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
- from V, displayed with format spec F. */
-void
-tab_value (struct tab_table *table, int c, int r, unsigned char opt,
- const union value *v, const struct fmt_spec *f)
+static void
+do_table_text (struct table *table, int c, int r, unsigned opt, char *text)
{
- char *contents;
+ assert (c >= 0);
+ assert (r >= 0);
+ assert (c < table->n[H]);
+ assert (r < table->n[V]);
- assert (table != NULL && v != NULL && f != NULL);
-#if DEBUGGING
- if (c + table->col_ofs < 0 || r + table->row_ofs < 0
- || c + table->col_ofs >= table->nc
- || r + table->row_ofs >= table->nr)
+ if (debugging)
{
- printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
- "(%d,%d)\n",
- c, table->col_ofs, c + table->col_ofs,
- r, table->row_ofs, r + table->row_ofs,
- table->nc, table->nr);
- return;
+ 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;
+ }
}
-#endif
- contents = pool_alloc (table->container, f->w);
- table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
- table->ct[c + r * table->cf] = opt;
-
- data_out (v, f, contents);
+ 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 value VAL
- with NDEC decimal places. */
+/* Sets cell (C,R) in TABLE, with options OPT, to have text value
+ TEXT. */
void
-tab_float (struct tab_table *table, int c, int r, unsigned char opt,
- double val, int w, int d)
+table_text (struct table *table, int c, int r, unsigned opt,
+ const char *text)
{
- char *contents;
- char buf[40], *cp;
-
- struct fmt_spec f;
- union value double_value;
-
- assert (table != NULL && w <= 40);
-
- assert (c >= 0);
- assert (c < table->nc);
- assert (r >= 0);
- assert (r < table->nr);
-
- f = fmt_for_output (FMT_F, w, d);
-
-#if DEBUGGING
- if (c + table->col_ofs < 0 || r + table->row_ofs < 0
- || c + table->col_ofs >= table->nc
- || r + table->row_ofs >= table->nr)
- {
- printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
- "(%d,%d)\n",
- c, table->col_ofs, c + table->col_ofs,
- r, table->row_ofs, r + table->row_ofs,
- table->nc, table->nr);
- return;
- }
-#endif
-
- double_value.f = val;
- data_out (&double_value, &f, buf);
-
- cp = buf;
- while (isspace ((unsigned char) *cp) && cp < &buf[w])
- cp++;
- f.w = w - (cp - buf);
-
- contents = pool_alloc (table->container, f.w);
- table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
- table->ct[c + r * table->cf] = opt;
- memcpy (contents, cp, f.w);
+ 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
- TEXT. */
+ FORMAT, which is formatted as if passed to printf. */
void
-tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
+table_text_format (struct table *table, int c, int r, unsigned opt,
+ const char *format, ...)
{
va_list args;
- assert (table != NULL && text != NULL);
-
- assert (c >= 0 );
- assert (r >= 0 );
- assert (c < table->nc);
- assert (r < table->nr);
-
-
-#if DEBUGGING
- if (c + table->col_ofs < 0 || r + table->row_ofs < 0
- || c + table->col_ofs >= table->nc
- || r + table->row_ofs >= table->nr)
- {
- printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
- "(%d,%d)\n",
- c, table->col_ofs, c + table->col_ofs,
- r, table->row_ofs, r + table->row_ofs,
- table->nc, table->nr);
- return;
- }
-#endif
-
- va_start (args, text);
- table->cc[c + r * table->cf] = text_format (table, opt, text, args);
- table->ct[c + r * table->cf] = opt;
+ va_start (args, format);
+ do_table_text (table, c, r, opt,
+ pool_vasprintf (table->container, format, args));
va_end (args);
}
-/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
- options OPT to have text value TEXT. */
-void
-tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
- unsigned opt, const char *text, ...)
+static struct table_cell *
+add_joined_cell (struct table *table, int x1, int y1, int x2, int y2,
+ unsigned opt)
{
- struct tab_joined_cell *j;
-
- assert (table != NULL && text != NULL);
-
- assert (x1 + table->col_ofs >= 0);
- assert (y1 + table->row_ofs >= 0);
+ assert (x1 >= 0);
+ assert (y1 >= 0);
assert (y2 >= y1);
assert (x2 >= x1);
- assert (y2 + table->row_ofs < table->nr);
- assert (x2 + table->col_ofs < table->nc);
-
-#if DEBUGGING
- if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
- || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
- || x2 < x1 || x2 + table->col_ofs >= table->nc
- || y2 < y2 || y2 + table->row_ofs >= table->nr)
- {
- printf ("tab_joint_text(): bad cell "
- "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
- x1, table->col_ofs, x1 + table->col_ofs,
- y1, table->row_ofs, y1 + table->row_ofs,
- x2, table->col_ofs, x2 + table->col_ofs,
- y2, table->row_ofs, y2 + table->row_ofs,
- table->nc, table->nr);
- return;
- }
-#endif
-
- tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
-
- j = pool_alloc (table->container, sizeof *j);
- j->hit = 0;
- j->x1 = x1 + table->col_ofs;
- j->y1 = y1 + table->row_ofs;
- j->x2 = ++x2 + table->col_ofs;
- j->y2 = ++y2 + table->row_ofs;
-
- {
- va_list args;
-
- va_start (args, text);
- j->contents = text_format (table, opt, text, args);
- va_end (args);
- }
-
- opt |= TAB_JOIN;
-
- {
- struct substring *cc = &table->cc[x1 + y1 * table->cf];
- unsigned char *ct = &table->ct[x1 + y1 * table->cf];
- const int ofs = table->cf - (x2 - x1);
-
- int y;
-
- for (y = y1; y < y2; y++)
- {
- int x;
-
- for (x = x1; x < x2; x++)
- {
- *cc++ = ss_buffer ((char *) j, 0);
- *ct++ = opt;
- }
-
- cc += ofs;
- ct += ofs;
- }
- }
-}
-
-/* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
-void
-tab_raw (struct tab_table *table, int c, int r, unsigned opt,
- struct substring *string)
-{
- assert (table != NULL && string != NULL);
-
-#if DEBUGGING
- if (c + table->col_ofs < 0 || r + table->row_ofs < 0
- || c + table->col_ofs >= table->nc
- || r + table->row_ofs >= table->nr)
- {
- printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
- "(%d,%d)\n",
- c, table->col_ofs, c + table->col_ofs,
- r, table->row_ofs, r + table->row_ofs,
- table->nc, table->nr);
- return;
- }
-#endif
-
- table->cc[c + r * table->cf] = *string;
- table->ct[c + r * table->cf] = opt;
-}
-\f
-/* Miscellaneous. */
-
-/* Sets the widths of all the columns and heights of all the rows in
- table T for driver D. */
-static void
-nowrap_dim (struct tab_table *t, struct outp_driver *d)
-{
- t->w[0] = tab_natural_width (t, d, 0);
- t->h[0] = d->font_height;
-}
-
-/* Sets the widths of all the columns and heights of all the rows in
- table T for driver D. */
-static void
-wrap_dim (struct tab_table *t, struct outp_driver *d)
-{
- t->w[0] = tab_natural_width (t, d, 0);
- t->h[0] = tab_natural_height (t, d, 0);
-}
-
-/* Outputs text BUF as a table with a single cell having cell options
- OPTIONS, which is a combination of the TAB_* and TAT_*
- constants. */
-void
-tab_output_text (int options, const char *buf, ...)
-{
- struct tab_table *t = tab_create (1, 1, 0);
- char *tmp_buf = NULL;
-
- if (options & TAT_PRINTF)
- {
- va_list args;
-
- va_start (args, buf);
- buf = tmp_buf = xvasprintf (buf, args);
- va_end (args);
- }
-
- tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
- tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
- tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
- tab_submit (t);
-
- free (tmp_buf);
-}
-
-/* Set table flags to FLAGS. */
-void
-tab_flags (struct tab_table *t, unsigned flags)
-{
- assert (t != NULL);
- t->flags = flags;
-}
-
-/* Easy, type-safe way to submit a tab table to som. */
-void
-tab_submit (struct tab_table *t)
-{
- struct som_entity s;
-
- assert (t != NULL);
- s.class = &tab_table_class;
- s.ext = t;
- s.type = SOM_TABLE;
- som_submit (&s);
- tab_destroy (t);
-}
-\f
-/* Editing. */
-
-/* Set table row and column offsets for all functions that affect
- cells or rules. */
-void
-tab_offset (struct tab_table *t, int col, int row)
-{
- int diff = 0;
+ assert (y2 < table->n[V]);
+ assert (x2 < table->n[H]);
- assert (t != NULL);
-#if DEBUGGING
- if (row < -1 || row > t->nr)
- {
- printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
- NOT_REACHED ();
- }
- if (col < -1 || col > t->nc)
+ if (debugging)
{
- printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
- NOT_REACHED ();
+ 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;
+ }
}
-#endif
- if (row != -1)
- diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
- if (col != -1)
- diff += (col - t->col_ofs), t->col_ofs = col;
+ table_box (table, -1, -1, TABLE_STROKE_NONE, TABLE_STROKE_NONE,
+ x1, y1, x2, y2);
- t->cc += diff;
- t->ct += diff;
-}
-
-/* Increment the row offset by one. If the table is too small,
- increase its size. */
-void
-tab_next_row (struct tab_table *t)
-{
- assert (t != NULL);
- t->cc += t->cf;
- t->ct += t->cf;
- if (++t->row_ofs >= t->nr)
- tab_realloc (t, -1, t->nr * 4 / 3);
-}
-\f
-static struct tab_table *t;
-static struct outp_driver *d;
-static int tab_hit;
-
-/* Set the current table to TABLE. */
-static void
-tabi_table (struct som_entity *table)
-{
- assert (table != NULL);
- assert (table->type == SOM_TABLE);
-
- t = table->ext;
- tab_offset (t, 0, 0);
-
- assert (t->w == NULL && t->h == NULL);
- t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
- t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
- t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
- t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
-}
-
-/* Returns the line style to use for spacing purposes for a rule
- of the given TYPE. */
-static enum outp_line_style
-rule_to_spacing_type (unsigned char type)
-{
- switch (type)
- {
- case TAL_0:
- return OUTP_L_NONE;
- case TAL_GAP:
- case TAL_1:
- return OUTP_L_SINGLE;
- case TAL_2:
- return OUTP_L_DOUBLE;
- default:
- NOT_REACHED ();
- }
-}
+ 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,
+ };
-/* Set the current output device to DRIVER. */
-static void
-tabi_driver (struct outp_driver *driver)
-{
- int c, r;
- int i;
-
- assert (driver != NULL);
- d = driver;
-
- /* Figure out sizes of rules. */
- for (r = 0; r <= t->nr; r++)
+ 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++)
{
- int width = 0;
- for (c = 0; c < t->nc; c++)
+ for (int x = x1; x < x2; x++)
{
- unsigned char rh = t->rh[c + r * t->cf];
- int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
- if (w > width)
- width = w;
+ *cc++ = cell;
+ *ct++ = opt | TAB_JOIN;
}
- t->hrh[r] = width;
- }
- for (c = 0; c <= t->nc; c++)
- {
- int width = 0;
- for (r = 0; r < t->nr; r++)
- {
- unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
- int w;
- if (*rv == UCHAR_MAX)
- *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
- w = driver->vert_line_width[rule_to_spacing_type (*rv)];
- if (w > width)
- width = w;
- }
- t->wrv[c] = width;
+ cc += ofs;
+ ct += ofs;
}
-#if DEBUGGING
- for (i = 0; i < t->nr; i++)
- t->h[i] = -1;
- for (i = 0; i < t->nc; i++)
- t->w[i] = -1;
-#endif
-
- assert (t->dim != NULL);
- t->dim (t, d);
-
-#if DEBUGGING
- {
- int error = 0;
-
- for (i = 0; i < t->nr; i++)
- {
- if (t->h[i] == -1)
- {
- printf ("Table row %d height not initialized.\n", i);
- error = 1;
- }
- assert (t->h[i] > 0);
- }
-
- for (i = 0; i < t->nc; i++)
- {
- if (t->w[i] == -1)
- {
- printf ("Table column %d width not initialized.\n", i);
- error = 1;
- }
- assert (t->w[i] > 0);
- }
- }
-#endif
-
- /* Add up header sizes. */
- for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
- t->wl += t->w[i] + t->wrv[i + 1];
- for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
- t->ht += t->h[i] + t->hrh[i + 1];
- for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
- t->wr += t->w[i] + t->wrv[i + 1];
- for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
- t->hb += t->h[i] + t->hrh[i + 1];
-
- /* Title. */
- if (!(t->flags & SOMF_NO_TITLE))
- t->ht += d->font_height;
-}
-
-/* Return the number of columns and rows in the table into N_COLUMNS
- and N_ROWS, respectively. */
-static void
-tabi_count (int *n_columns, int *n_rows)
-{
- assert (n_columns != NULL && n_rows != NULL);
- *n_columns = t->nc;
- *n_rows = t->nr;
+ return cell;
}
-static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
-
-/* Return the horizontal and vertical size of the entire table,
- including headers, for the current output device, into HORIZ and
- VERT. */
-static void
-tabi_area (int *horiz, int *vert)
-{
- assert (horiz != NULL && vert != NULL);
-
- {
- int w, c;
-
- for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
- c < t->nc - t->r; c++)
- w += t->w[c] + t->wrv[c];
- *horiz = w;
- }
-
- {
- int h, r;
- for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
- r < t->nr - t->b; r++)
- h += t->h[r] + t->hrh[r];
- *vert = h;
- }
-}
-
-/* Return the column style for this table into STYLE. */
-static void
-tabi_columns (int *style)
+/* 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)
{
- assert (style != NULL);
- *style = t->col_style;
+ 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;
}
-/* Return the number of header rows/columns on the left, right, top,
- and bottom sides into HL, HR, HT, and HB, respectively. */
-static void
-tabi_headers (int *hl, int *hr, int *ht, int *hb)
+static struct table_cell *
+get_joined_cell (struct table *table, int x, int y)
{
- assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
- *hl = t->l;
- *hr = t->r;
- *ht = t->t;
- *hb = t->b;
-}
+ int index = x + y * table->n[H];
+ unsigned short opt = table->ct[index];
+ struct table_cell *cell;
-/* Determines the number of rows or columns (including appropriate
- headers), depending on CUMTYPE, that will fit into the space
- specified. Takes rows/columns starting at index START and attempts
- to fill up available space MAX. Returns in END the index of the
- last row/column plus one; returns in ACTUAL the actual amount of
- space the selected rows/columns (including appropriate headers)
- filled. */
-static void
-tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
-{
- int n;
- int *d;
- int *r;
- int total;
-
- assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
- if (cumtype == SOM_ROWS)
- {
- assert (start >= 0 && start < t->nr);
- n = t->nr - t->b;
- d = &t->h[start];
- r = &t->hrh[start + 1];
- total = t->ht + t->hb;
- }
+ if (opt & TAB_JOIN)
+ cell = table->cc[index];
else
{
- assert (start >= 0 && start < t->nc);
- n = t->nc - t->r;
- d = &t->w[start];
- r = &t->wrv[start + 1];
- total = t->wl + t->wr;
- }
-
- total += *d++;
- if (total > max)
- {
- if (end)
- *end = start;
- if (actual)
- *actual = 0;
- return;
- }
-
- {
- int x;
-
- for (x = start + 1; x < n; x++)
- {
- int amt = *d++ + *r++;
-
- total += amt;
- if (total > max)
- {
- total -= amt;
- break;
- }
- }
-
- if (end)
- *end = x;
-
- if (actual)
- *actual = total;
- }
-}
-
-/* Return flags set for the current table into FLAGS. */
-static void
-tabi_flags (unsigned *flags)
-{
- assert (flags != NULL);
- *flags = t->flags;
-}
-
-/* Returns true if the table will fit in the given page WIDTH,
- false otherwise. */
-static bool
-tabi_fits_width (int width)
-{
- int i;
-
- for (i = t->l; i < t->nc - t->r; i++)
- if (t->wl + t->wr + t->w[i] > width)
- return false;
-
- return true;
-}
+ char *text = table->cc[index];
-/* Returns true if the table will fit in the given page LENGTH,
- false otherwise. */
-static bool
-tabi_fits_length (int length)
-{
- int i;
-
- for (i = t->t; i < t->nr - t->b; i++)
- if (t->ht + t->hb + t->h[i] > length)
- return false;
-
- return true;
-}
-
-/* Sets the number of header rows/columns on the left, right, top,
- and bottom sides to HL, HR, HT, and HB, respectively. */
-static void
-tabi_set_headers (int hl, int hr, int ht, int hb)
-{
- t->l = hl;
- t->r = hr;
- t->t = ht;
- t->b = hb;
+ cell = add_joined_cell (table, x, y, x, y, table->ct[index]);
+ cell->text = text ? text : pool_strdup (table->container, "");
+ }
+ return cell;
}
-/* Render title for current table, with major index X and minor index
- Y. Y may be zero, or X and Y may be zero, but X should be nonzero
- if Y is nonzero. */
-static void
-tabi_title (int x, int y)
+/* 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)
{
- char buf[1024];
- char *cp;
+ struct table_cell *cell = get_joined_cell (table, x, y);
- if (t->flags & SOMF_NO_TITLE)
- return;
-
- cp = spprintf (buf, "%d.%d", table_num, subtable_num);
- if (x && y)
- cp = spprintf (cp, "(%d:%d)", x, y);
- else if (x)
- cp = spprintf (cp, "(%d)", x);
- if (command_name != NULL)
- cp = spprintf (cp, " %s", command_name);
- cp = stpcpy (cp, ". ");
- if (t->title != NULL)
- {
- size_t length = strlen (t->title);
- memcpy (cp, t->title, length);
- cp += length;
- }
- *cp = 0;
-
- {
- struct outp_text text;
-
- text.font = OUTP_PROPORTIONAL;
- text.justification = OUTP_LEFT;
- text.string = ss_buffer (buf, cp - buf);
- text.h = d->width;
- text.v = d->font_height;
- text.x = 0;
- text.y = d->cp_y;
- d->class->text_draw (d, &text);
- }
+ 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]);
}
-static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
+/* 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.
-/* Renders columns C0...C1, plus headers, of rows R0...R1,
- at the given vertical position Y.
- C0 and C1 count vertical rules as columns,
- but R0 and R1 do not count horizontal rules as rows.
- Returns the vertical position after rendering. */
-static int
-render_rows (int y, int c0, int c1, int r0, int r1)
+ 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)
{
- int r;
- for (r = r0; r < r1; r++)
- {
- int x = d->cp_x;
- x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
- x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
- x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
- y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2];
- }
- return y;
-}
-
-/* Draws table region (C0,R0)-(C1,R1), plus headers, at the
- current position on the current output device. */
-static void
-tabi_render (int c0, int r0, int c1, int r1)
-{
- int y;
-
- tab_hit++;
-
- y = d->cp_y;
- if (!(t->flags & SOMF_NO_TITLE))
- y += d->font_height;
-
- y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
- y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
- y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
-}
+ assert (style);
-const struct som_table_class tab_table_class =
- {
- tabi_table,
- tabi_driver,
-
- tabi_count,
- tabi_area,
- NULL,
- NULL,
- tabi_columns,
- NULL,
- tabi_headers,
- NULL,
- tabi_cumulate,
- tabi_flags,
- tabi_fits_width,
- tabi_fits_length,
-
- NULL,
- NULL,
- tabi_set_headers,
-
- tabi_title,
- tabi_render,
- };
-\f
-static enum outp_justification
-translate_justification (unsigned int opt)
-{
- switch (opt & TAB_ALIGN_MASK)
- {
- case TAB_RIGHT:
- return OUTP_RIGHT;
- case TAB_LEFT:
- return OUTP_LEFT;
- case TAB_CENTER:
- return OUTP_CENTER;
- default:
- NOT_REACHED ();
- }
+ 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;
}
-/* Returns the line style to use for drawing a rule of the given
- TYPE. */
-static enum outp_line_style
-rule_to_draw_type (unsigned char type)
+/* 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, struct footnote *f)
{
- switch (type)
- {
- case TAL_0:
- case TAL_GAP:
- return OUTP_L_NONE;
- case TAL_1:
- return OUTP_L_SINGLE;
- case TAL_2:
- return OUTP_L_DOUBLE;
- default:
- NOT_REACHED ();
- }
-}
+ assert (f->style);
-/* Returns the horizontal rule at the given column and row. */
-static int
-get_hrule (int c, int r)
-{
- return t->rh[c + r * t->cf];
-}
+ struct table_cell *cell = get_joined_cell (table, x, y);
-/* Returns the vertical rule at the given column and row. */
-static int
-get_vrule (int c, int r)
-{
- return t->rv[c + r * (t->cf + 1)];
-}
+ cell->footnotes = pool_realloc (
+ table->container, cell->footnotes,
+ (cell->n_footnotes + 1) * sizeof *cell->footnotes);
-/* Renders the horizontal rule at the given column and row
- at (X,Y) on the page. */
-static void
-render_horz_rule (int x, int y, int c, int r)
-{
- enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
- if (style != OUTP_L_NONE)
- d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
- OUTP_L_NONE, style, OUTP_L_NONE, style);
+ cell->footnotes[cell->n_footnotes++] = f;
}
-/* Renders the vertical rule at the given column and row
- at (X,Y) on the page. */
-static void
-render_vert_rule (int x, int y, int c, int r)
+/* 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,
+ struct table_area_style *style)
{
- enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
- if (style != OUTP_L_NONE)
- d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
- style, OUTP_L_NONE, style, OUTP_L_NONE);
+ get_joined_cell (table, x, y)->style = style;
}
-/* Renders the rule intersection at the given column and row
- at (X,Y) on the page. */
-static void
-render_rule_intersection (int x, int y, int c, int r)
+/* 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)
{
- /* Bounds of intersection. */
- int x0 = x;
- int y0 = y;
- int x1 = x + t->wrv[c];
- int y1 = y + t->hrh[r];
-
- /* Lines on each side of intersection. */
- int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
- int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
- int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
- int right = c < t->nc ? get_hrule (c, r) : TAL_0;
-
- /* Output style for each line. */
- enum outp_line_style o_top = rule_to_draw_type (top);
- enum outp_line_style o_left = rule_to_draw_type (left);
- enum outp_line_style o_bottom = rule_to_draw_type (bottom);
- enum outp_line_style o_right = rule_to_draw_type (right);
-
- if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
- || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
- d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
+ return table->cc[c + r * table->n[H]] == NULL;
}
-
-/* Returns the width of columns C1...C2 exclusive,
- including interior but not exterior rules. */
-static int
-strip_width (int c1, int c2)
+\f
+/* Initializes CELL with the contents of the table cell at column X and row Y
+ within TABLE. */
+void
+table_get_cell (const struct table *t, int x, int y, struct table_cell *cell)
{
- int width = 0;
- int c;
-
- for (c = c1; c < c2; c++)
- width += t->w[c] + t->wrv[c + 1];
- if (c1 < c2)
- width -= t->wrv[c2];
- return width;
-}
+ assert (x >= 0 && x < t->n[TABLE_HORZ]);
+ assert (y >= 0 && y < t->n[TABLE_VERT]);
-/* Returns the height of rows R1...R2 exclusive,
- including interior but not exterior rules. */
-static int
-strip_height (int r1, int r2)
-{
- int height = 0;
- int r;
-
- for (r = r1; r < r2; r++)
- height += t->h[r] + t->hrh[r + 1];
- if (r1 < r2)
- height -= t->hrh[r2];
- return height;
-}
+ int index = x + y * t->n[H];
+ unsigned short opt = t->ct[index];
+ const void *cc = t->cc[index];
-/* Renders the cell at the given column and row at (X,Y) on the
- page. Also renders joined cells that extend as far to the
- right as C1 and as far down as R1. */
-static void
-render_cell (int x, int y, int c, int r, int c1, int r1)
-{
- const int index = c + (r * t->cf);
- unsigned char type = t->ct[index];
- struct substring *content = &t->cc[index];
-
- if (!(type & TAB_JOIN))
+ struct table_area_style *style
+ = t->styles[(opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT];
+ if (opt & TAB_JOIN)
{
- if (!(type & TAB_EMPTY))
- {
- struct outp_text text;
- text.font = options_to_font (type);
- text.justification = translate_justification (type);
- text.string = *content;
- text.h = t->w[c];
- text.v = t->h[r];
- text.x = x;
- text.y = y;
- d->class->text_draw (d, &text);
- }
+ const struct table_cell *jc = cc;
+ *cell = *jc;
+ if (!cell->style)
+ cell->style = style;
}
else
- {
- struct tab_joined_cell *j
- = (struct tab_joined_cell *) ss_data (*content);
-
- if (j->hit != tab_hit)
- {
- j->hit = tab_hit;
-
- if (j->x1 == c && j->y1 == r)
- {
- struct outp_text text;
- text.font = options_to_font (type);
- text.justification = translate_justification (type);
- text.string = j->contents;
- text.x = x;
- text.y = y;
- text.h = strip_width (j->x1, MIN (j->x2, c1));
- text.v = strip_height (j->y1, MIN (j->y2, r1));
- d->class->text_draw (d, &text);
- }
- }
- }
-}
-
-/* Render contiguous strip consisting of columns C0...C1, exclusive,
- on row R, at (X,Y). Returns X position after rendering.
- Also renders joined cells that extend beyond that strip,
- cropping them to lie within rendering region (C0,R0)-(C1,R1).
- C0 and C1 count vertical rules as columns.
- R counts horizontal rules as rows, but R0 and R1 do not. */
-static int
-render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
-{
- int c;
-
- for (c = c0; c < c1; c++)
- if (c & 1)
- {
- if (r & 1)
- render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
- else
- render_horz_rule (x, y, c / 2, r / 2);
- x += t->w[c / 2];
- }
- else
- {
- if (r & 1)
- render_vert_rule (x, y, c / 2, r / 2);
- else
- render_rule_intersection (x, y, c / 2, r / 2);
- x += t->wrv[c / 2];
- }
-
- return x;
-}
-
-/* Sets COMMAND_NAME as the name of the current command,
- for embedding in output. */
-void
-tab_set_command_name (const char *command_name_)
-{
- free (command_name);
- command_name = command_name_ ? xstrdup (command_name_) : NULL;
+ *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;
}