X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Ftable.c;h=09ed509e35bfac726be760d9e2210db17f89144e;hb=5ce9c6ba1502e623ec6723a8273da77e5858b8d4;hp=04181e44f709b9ca1d2fcb9cab3abcce207b9595;hpb=89306c3c6b335e23a09bc2c3442d8b08e60cb328;p=pspp diff --git a/src/output/table.c b/src/output/table.c index 04181e44f7..09ed509e35 100644 --- a/src/output/table.c +++ b/src/output/table.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2009 Free Software Foundation, Inc. + 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 @@ -16,208 +16,274 @@ #include -#include "table.h" +#include "output/table.h" +#include "output/table-provider.h" -#include -#include -#include +#include +#include #include -#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/output-item.h" +#include "output/pivot-table.h" +#include "output/table.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include "gl/xalloc.h" -#include +/* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */ +#define H TABLE_HORZ +#define V TABLE_VERT -#include "minmax.h" -#include "xalloc.h" +/* 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; +} -#include "gettext.h" -#define _(msgid) gettext (msgid) +/* 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; +} -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); + } +} + +const char * +table_halign_to_string (enum table_halign halign) +{ + switch (halign) + { + 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**"; + } } -/* Creates a table with NC columns and NR rows. */ -struct tab_table * -tab_create (int nc, int nr, int reallocable UNUSED) +const char * +table_valign_to_string (enum table_valign valign) { - struct tab_table *t; + switch (valign) + { + case TABLE_VALIGN_TOP: return "top"; + case TABLE_VALIGN_CENTER: return "center"; + case TABLE_VALIGN_BOTTOM: return "bottom"; + default: return "**error**"; + } +} - 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; +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; - 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); + case TABLE_HALIGN_MIXED: + return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT; - t->rh = pool_nmalloc (t->container, nc, nr + 1); - memset (t->rh, 0, nc * (nr + 1)); + case TABLE_HALIGN_DECIMAL: + return TABLE_HALIGN_DECIMAL; - t->rv = pool_nmalloc (t->container, nr, nc + 1); - memset (t->rv, UCHAR_MAX, nr * (nc + 1)); + default: + NOT_REACHED (); + } +} - t->dim = NULL; - t->w = t->h = NULL; - t->col_ofs = t->row_ofs = 0; +void +font_style_copy (struct pool *container, + struct font_style *dst, const struct font_style *src) +{ + *dst = *src; + if (dst->typeface) + dst->typeface = pool_strdup (container, dst->typeface); +} - return t; +void +font_style_uninit (struct font_style *font) +{ + if (font) + free (font->typeface); } -/* Destroys table T. */ void -tab_destroy (struct tab_table *t) +table_area_style_copy (struct pool *container, struct table_area_style *dst, + const struct table_area_style *src) { - assert (t != NULL); - free (t->title); - pool_destroy (t->container); + font_style_copy (container, &dst->font_style, &src->font_style); + dst->cell_style = src->cell_style; } -/* 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_area_style_uninit (struct table_area_style *area) { - assert (t != NULL); - if (nc != -1) - { - assert (nc + t->col_ofs <= t->cf); - t->nc = nc + t->col_ofs; - } - if (nr != -1) + if (area) + font_style_uninit (&area->font_style); +} + +const char * +table_stroke_to_string (enum table_stroke stroke) +{ + switch (stroke) { - assert (nr + t->row_ofs <= t->nr); - t->nr = nr + t->row_ofs; + 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**"; } } -/* Changes either or both dimensions of a table. Consider using the - above routine instead if it won't waste a lot of space. - - 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) +cell_color_dump (const struct cell_color *c) { - int ro, co; + 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); +} - assert (t != NULL); - ro = t->row_ofs; - co = t->col_ofs; - if (ro || co) - tab_offset (t, 0, 0); +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); +} - if (nc == -1) - nc = t->nc; - if (nr == -1) - nr = t->nr; +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]); +} + - assert (nc == t->nc); +static const bool debugging = true; - if (nc > t->cf) - { - 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; - } - else if (nr != t->nr) - { - 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)); - } - } +/* Creates and returns a new table with NC columns and NR rows and initially no + header rows or columns. - memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr)); + 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. - t->nr = nr; - t->nc = nc; + 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; - if (ro || co) - tab_offset (t, co, ro); -} + 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; -/* 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) -{ - assert (table != NULL); - assert (l < table->nc); - assert (r < table->nc); - assert (t < table->nr); - assert (b < table->nr); + 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)); - table->l = l; - table->r = r; - table->t = t; - table->b = b; -} + t->rv = pool_nmalloc (t->container, nr, nc + 1); + memset (t->rv, TABLE_STROKE_NONE, nr * (nc + 1)); -/* Set up table T so that, when it is an appropriate size, it will be - displayed across the page in columns. + memset (t->styles, 0, sizeof t->styles); + memset (t->rule_colors, 0, sizeof t->rule_colors); - 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) -{ - assert (t != NULL); - t->col_style = style; - t->col_group = group; + return t; } /* Rules. */ @@ -225,65 +291,62 @@ tab_columns (struct tab_table *t, int style, int group) /* 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; } } @@ -294,47 +357,36 @@ tab_hline (struct tab_table * t, int style, int x1, int x2, int y) 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) @@ -342,8 +394,8 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v, 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; } } @@ -352,1166 +404,192 @@ tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v, 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; + { + 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, void *aux) -{ - assert (t != NULL && t->dim == NULL); - t->dim = dim_func; - t->dim_aux = aux; -} - -/* 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; + 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; - - 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, - void *aux UNUSED) -{ - 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); -} - /* 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 dictionary *dict, - const struct fmt_spec *f) -{ - char *contents; - - 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) - { - 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; - } -#endif - - contents = data_out_pool (v, dict_get_encoding (dict), f, table->container); - - table->cc[c + r * table->cf] = ss_cstr (contents); - table->ct[c + r * table->cf] = opt; -} - -/* Sets cell (C,R) in TABLE, with options OPT, to have value VAL - with NDEC decimal places. */ -void -tab_fixed (struct tab_table *table, int c, int r, unsigned char opt, - double val, int w, int d) -{ - char *s, *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_fixed(): 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; - s = data_out_pool (&double_value, LEGACY_NATIVE, &f, table->container); - - cp = s; - while (isspace ((unsigned char) *cp) && cp < &s[w]) - cp++; - f.w = w - (cp - s); - - table->cc[c + r * table->cf] = ss_buffer (cp, f.w); - table->ct[c + r * table->cf] = opt; -} - -/* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as - formatted by FMT. - If FMT is null, then the default print format will be used. -*/ +/* Fill TABLE cells (X1,X2)-(Y1,Y2), inclusive, with VALUE and OPT. */ void -tab_double (struct tab_table *table, int c, int r, unsigned char opt, - double val, const struct fmt_spec *fmt) +table_put (struct table *table, int x1, int y1, int x2, int y2, + unsigned opt, const struct pivot_value *value) { - struct substring ss; - union value double_value ; - - assert (table != NULL); - - assert (c >= 0); - assert (c < table->nc); - assert (r >= 0); - assert (r < table->nr); - - if ( fmt == NULL) - fmt = settings_get_format (); - - fmt_check_output (fmt); - -#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_double(): 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; - ss = ss_cstr (data_out_pool (&double_value, LEGACY_NATIVE, fmt, table->container)); - - ss_ltrim (&ss, ss_cstr (" ")); - - table->cc[c + r * table->cf] = ss; - table->ct[c + r * table->cf] = opt; -} - - -/* Sets cell (C,R) in TABLE, with options OPT, to have text value - TEXT. */ -void -tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...) -{ - va_list args; - - assert (table != NULL && text != NULL); - - assert (c >= 0 ); - assert (r >= 0 ); - assert (c < table->nc); - assert (r < table->nr); + assert (0 <= x1 && x1 <= x2 && x2 < table->n[H]); + assert (0 <= y1 && y1 <= y2 && y2 < table->n[V]); - -#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 (x1 == x2 && y1 == y2) { - 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; + table->cc[x1 + y1 * table->n[H]] = CONST_CAST (struct pivot_value *, value); + table->ct[x1 + y1 * table->n[H]] = opt; } -#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_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, ...) -{ - struct tab_joined_cell *j; - - assert (table != NULL && text != NULL); - - assert (x1 + table->col_ofs >= 0); - assert (y1 + table->row_ofs >= 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) + else { - 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); + table_box (table, -1, -1, TABLE_STROKE_NONE, TABLE_STROKE_NONE, + 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); + struct table_cell *cell = pool_alloc (table->container, sizeof *cell); + *cell = (struct table_cell) { + .d = { [H] = { x1, x2 + 1 }, [V] = { y1, y2 + 1 } }, + .options = opt, + .value = value, + }; -#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_raw(): 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; + for (int y = y1; y <= y2; y++) + { + size_t ofs = x1 + y * table->n[H]; + void **cc = &table->cc[ofs]; + unsigned short *ct = &table->ct[ofs]; + for (int x = x1; x <= x2; x++) + { + *cc++ = cell; + *ct++ = opt | TAB_JOIN; + } + } } -#endif - - table->cc[c + r * table->cf] = *string; - table->ct[c + r * table->cf] = opt; } - -/* 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, void *aux UNUSED) +free_value (void *value_) { - t->w[0] = tab_natural_width (t, d, 0); - t->h[0] = d->font_height; + struct pivot_value *value = value_; + pivot_value_destroy (value); } -/* 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, void *aux UNUSED) -{ - 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, ...) +table_put_owned (struct table *table, int x1, int y1, int x2, int y2, + unsigned opt, struct pivot_value *value) { - 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, NULL); - 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; + table_put (table, x1, y1, x2, y2, opt, value); + pool_register (table->container, free_value, value); } -/* Easy, type-safe way to submit a tab table to som. */ -void -tab_submit (struct tab_table *t) +/* 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) { - 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); + return table->cc[c + r * table->n[H]] == NULL; } -/* 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 (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) - { - printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc); - NOT_REACHED (); - } -#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; - - t->cc += diff; - t->ct += diff; -} - -/* Increment the row offset by one. If the table is too small, - increase its size. */ +/* Initializes CELL with the contents of the table cell at column X and row Y + within TABLE. */ 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); -} - -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 (); - } -} - -/* 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++) - { - int width = 0; - for (c = 0; c < t->nc; c++) - { - 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; - } - 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; - } - -#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, t->dim_aux); - -#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; -} - -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) -{ - assert (style != NULL); - *style = t->col_style; -} - -/* 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) -{ - assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL); - *hl = t->l; - *hr = t->r; - *ht = t->t; - *hb = t->b; -} - -/* 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; - } - 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; -} - -/* 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; -} - -/* 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) -{ - char buf[1024]; - char *cp; - - 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); - } -} - -static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2); - -/* 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) -{ - 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); -} - -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, +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]; + + struct table_area_style *style + = t->styles[(opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT]; + + static const struct pivot_value empty_value = { + .text = { + .type = PIVOT_VALUE_TEXT, + .local = (char *) "", + .c = (char *) "", + .id = (char *) "", + .user_provided = true, + }, }; - -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 (); - } -} -/* 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) -{ - switch (type) + if (opt & TAB_JOIN) { - 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 (); - } -} - -/* 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]; -} - -/* 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)]; -} - -/* 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); -} - -/* 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) -{ - 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); -} - -/* 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) -{ - /* 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); -} - -/* Returns the width of columns C1...C2 exclusive, - including interior but not exterior rules. */ -static int -strip_width (int c1, int c2) -{ - 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; -} - -/* 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; -} - -/* 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)) - { - 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->value) + cell->value = &empty_value; + if (!cell->font_style) + cell->font_style = &style->font_style; + if (!cell->cell_style) + cell->cell_style = &style->cell_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; + const struct pivot_value *v = cc ? cc : &empty_value; + const struct pivot_value_ex *ex = pivot_value_ex (v); + *cell = (struct table_cell) { + .d = { [H] = { x, x + 1 }, [V] = { y, y + 1 } }, + .options = opt, + .value = v, + .font_style = ex->font_style ? ex->font_style : &style->font_style, + .cell_style = ex->cell_style ? ex->cell_style : &style->cell_style, + }; + } + + assert (cell->font_style); + assert (cell->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; }