it works again
[pspp] / src / output / table.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2011, 2014, 2016 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "output/table.h"
20 #include "output/table-provider.h"
21
22 #include <assert.h>
23 #include <inttypes.h>
24 #include <stdlib.h>
25
26 #include "data/format.h"
27 #include "libpspp/assertion.h"
28 #include "libpspp/cast.h"
29 #include "libpspp/compiler.h"
30 #include "libpspp/pool.h"
31 #include "libpspp/str.h"
32 #include "output/table-item.h"
33 #include "output/table.h"
34 #include "output/text-item.h"
35
36 #include "gl/xalloc.h"
37
38 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
39 #define H TABLE_HORZ
40 #define V TABLE_VERT
41
42 /* Increases TABLE's reference count, indicating that it has an additional
43    owner.  An table that is shared among multiple owners must not be
44    modified. */
45 struct table *
46 table_ref (const struct table *table_)
47 {
48   struct table *table = CONST_CAST (struct table *, table_);
49   table->ref_cnt++;
50   return table;
51 }
52
53 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
54    If TABLE no longer has any owners, it is freed. */
55 void
56 table_unref (struct table *table)
57 {
58   if (table != NULL)
59     {
60       assert (table->ref_cnt > 0);
61       if (--table->ref_cnt == 0)
62         pool_destroy (table->container);
63     }
64 }
65
66 /* Returns true if TABLE has more than one owner.  A table item that is shared
67    among multiple owners must not be modified. */
68 bool
69 table_is_shared (const struct table *table)
70 {
71   return table->ref_cnt > 1;
72 }
73 \f
74 struct table_area_style *
75 table_area_style_clone (struct pool *pool, const struct table_area_style *old)
76 {
77   struct table_area_style *new = pool_malloc (pool, sizeof *new);
78   *new = *old;
79   if (new->font_style.typeface)
80     new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
81   return new;
82 }
83
84 void
85 table_area_style_free (struct table_area_style *style)
86 {
87   if (style)
88     {
89       free (style->font_style.typeface);
90       free (style);
91     }
92 }
93 \f
94 const char *
95 table_halign_to_string (enum table_halign halign)
96 {
97   switch (halign)
98     {
99     case TABLE_HALIGN_LEFT: return "left";
100     case TABLE_HALIGN_CENTER: return "center";
101     case TABLE_HALIGN_RIGHT: return "right";
102     case TABLE_HALIGN_DECIMAL: return "decimal";
103     case TABLE_HALIGN_MIXED: return "mixed";
104     default: return "**error**";
105     }
106 }
107
108 const char *
109 table_valign_to_string (enum table_valign valign)
110 {
111   switch (valign)
112     {
113     case TABLE_VALIGN_TOP: return "top";
114     case TABLE_VALIGN_CENTER: return "center";
115     case TABLE_VALIGN_BOTTOM: return "bottom";
116     default: return "**error**";
117     }
118 }
119
120 enum table_halign
121 table_halign_interpret (enum table_halign halign, bool numeric)
122 {
123   switch (halign)
124     {
125     case TABLE_HALIGN_LEFT:
126     case TABLE_HALIGN_CENTER:
127     case TABLE_HALIGN_RIGHT:
128       return halign;
129
130     case TABLE_HALIGN_MIXED:
131       return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT;
132
133     case TABLE_HALIGN_DECIMAL:
134       return TABLE_HALIGN_DECIMAL;
135
136     default:
137       NOT_REACHED ();
138     }
139 }
140
141 void
142 font_style_copy (struct pool *container,
143                  struct font_style *dst, const struct font_style *src)
144 {
145   *dst = *src;
146   if (dst->typeface)
147     dst->typeface = pool_strdup (container, dst->typeface);
148 }
149
150 void
151 font_style_uninit (struct font_style *font)
152 {
153   if (font)
154     free (font->typeface);
155 }
156
157 void
158 table_area_style_copy (struct pool *container, struct table_area_style *dst,
159                        const struct table_area_style *src)
160 {
161   font_style_copy (container, &dst->font_style, &src->font_style);
162   dst->cell_style = src->cell_style;
163 }
164
165 void
166 table_area_style_uninit (struct table_area_style *area)
167 {
168   if (area)
169     font_style_uninit (&area->font_style);
170 }
171
172 const char *
173 table_stroke_to_string (enum table_stroke stroke)
174 {
175   switch (stroke)
176     {
177     case TABLE_STROKE_NONE: return "none";
178     case TABLE_STROKE_SOLID: return "solid";
179     case TABLE_STROKE_DASHED: return "dashed";
180     case TABLE_STROKE_THICK: return "thick";
181     case TABLE_STROKE_THIN: return "thin";
182     case TABLE_STROKE_DOUBLE: return "double";
183     default:
184       return "**error**";
185     }
186 }
187
188 void
189 cell_color_dump (const struct cell_color *c)
190 {
191   if (c->alpha != 255)
192     printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
193   else
194     printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
195 }
196
197 void
198 font_style_dump (const struct font_style *f)
199 {
200   printf ("%s %dpx ", f->typeface, f->size);
201   cell_color_dump (&f->fg[0]);
202   putchar ('/');
203   cell_color_dump (&f->bg[0]);
204   if (!cell_color_equal (&f->fg[0], &f->fg[1])
205       || !cell_color_equal (&f->bg[0], &f->bg[1]))
206     {
207       printf (" alt=");
208       cell_color_dump (&f->fg[1]);
209       putchar ('/');
210       cell_color_dump (&f->bg[1]);
211     }
212   if (f->bold)
213     fputs (" bold", stdout);
214   if (f->italic)
215     fputs (" italic", stdout);
216   if (f->underline)
217     fputs (" underline", stdout);
218 }
219
220 bool
221 font_style_equal (const struct font_style *a, const struct font_style *b)
222 {
223   return (a->bold == b->bold
224           && a->italic == b->italic
225           && a->underline == b->underline
226           && a->markup == b->markup
227           && cell_color_equal (&a->fg[0], &b->fg[0])
228           && cell_color_equal (&a->fg[1], &b->fg[1])
229           && cell_color_equal (&a->bg[0], &b->bg[0])
230           && cell_color_equal (&a->bg[1], &b->bg[1])
231           && !strcmp (a->typeface ? a->typeface : "",
232                       b->typeface ? b->typeface : "")
233           && a->size == b->size);
234 }
235
236 void
237 cell_style_dump (const struct cell_style *c)
238 {
239   fputs (table_halign_to_string (c->halign), stdout);
240   if (c->halign == TABLE_HALIGN_DECIMAL)
241     printf ("(%.2gpx)", c->decimal_offset);
242   printf (" %s", table_valign_to_string (c->valign));
243   printf (" %d,%d,%d,%dpx",
244           c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
245           c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);
246 }
247 \f
248
249 static const bool debugging = true;
250
251 /* Creates and returns a new table with NC columns and NR rows and initially no
252    header rows or columns.
253
254    Sets the number of header rows on each side of TABLE to HL on the
255    left, HR on the right, HT on the top, HB on the bottom.  Header rows
256    are repeated when a table is broken across multiple columns or
257    multiple pages.
258
259    The table's cells are initially empty. */
260 struct table *
261 table_create (int nc, int nr, int hl, int hr, int ht, int hb)
262 {
263   struct table *t;
264
265   t = pool_create_container (struct table, container);
266   t->n[TABLE_HORZ] = nc;
267   t->n[TABLE_VERT] = nr;
268   t->h[TABLE_HORZ][0] = hl;
269   t->h[TABLE_HORZ][1] = hr;
270   t->h[TABLE_VERT][0] = ht;
271   t->h[TABLE_VERT][1] = hb;
272   t->ref_cnt = 1;
273
274   t->cc = pool_calloc (t->container, nr * nc, sizeof *t->cc);
275   t->ct = pool_calloc (t->container, nr * nc, sizeof *t->ct);
276
277   t->rh = pool_nmalloc (t->container, nc, nr + 1);
278   memset (t->rh, TABLE_STROKE_NONE, nc * (nr + 1));
279
280   t->rv = pool_nmalloc (t->container, nr, nc + 1);
281   memset (t->rv, TABLE_STROKE_NONE, nr * (nc + 1));
282
283   memset (t->styles, 0, sizeof t->styles);
284   memset (t->rule_colors, 0, sizeof t->rule_colors);
285
286   return t;
287 }
288 \f
289 /* Rules. */
290
291 /* Draws a vertical line to the left of cells at horizontal position X
292    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
293 void
294 table_vline (struct table *t, int style, int x, int y1, int y2)
295 {
296   if (debugging)
297     {
298       if (x < 0 || x > t->n[H]
299           || y1 < 0 || y1 >= t->n[V]
300           || y2 < 0 || y2 >= t->n[V])
301         {
302           printf ("bad vline: x=%d y=(%d,%d) in table size (%d,%d)\n",
303                   x, y1, y2, t->n[H], t->n[V]);
304           return;
305         }
306     }
307
308   assert (x >= 0);
309   assert (x <= t->n[H]);
310   assert (y1 >= 0);
311   assert (y2 >= y1);
312   assert (y2 <= t->n[V]);
313
314   if (style != -1)
315     {
316       int y;
317       for (y = y1; y <= y2; y++)
318         t->rv[x + (t->n[H] + 1) * y] = style;
319     }
320 }
321
322 /* Draws a horizontal line above cells at vertical position Y from X1
323    to X2 inclusive in style STYLE, if style is not -1. */
324 void
325 table_hline (struct table *t, int style, int x1, int x2, int y)
326 {
327   if (debugging)
328     {
329       if (y < 0 || y > t->n[V]
330           || x1 < 0 || x1 >= t->n[H]
331           || x2 < 0 || x2 >= t->n[H])
332         {
333           printf ("bad hline: x=(%d,%d) y=%d in table size (%d,%d)\n",
334                   x1, x2, y, t->n[H], t->n[V]);
335           return;
336         }
337     }
338
339   assert (y >= 0);
340   assert (y <= t->n[V]);
341   assert (x2 >= x1);
342   assert (x1 >= 0);
343   assert (x2 < t->n[H]);
344
345   if (style != -1)
346     {
347       int x;
348       for (x = x1; x <= x2; x++)
349         t->rh[x + t->n[H] * y] = style;
350     }
351 }
352
353 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
354    lines of style F_H and vertical lines of style F_V.  Fills the
355    interior of the box with horizontal lines of style I_H and vertical
356    lines of style I_V.  Any of the line styles may be -1 to avoid
357    drawing those lines.  This is distinct from 0, which draws a null
358    line. */
359 void
360 table_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
361            int x1, int y1, int x2, int y2)
362 {
363   if (debugging)
364     {
365       if (x1 < 0 || x1 >= t->n[H]
366           || x2 < 0 || x2 >= t->n[H]
367           || y1 < 0 || y1 >= t->n[V]
368           || y2 < 0 || y2 >= t->n[V])
369         {
370           printf ("bad box: (%d,%d)-(%d,%d) in table size (%d,%d)\n",
371                   x1, y1, x2, y2, t->n[H], t->n[V]);
372           NOT_REACHED ();
373         }
374     }
375
376   assert (x2 >= x1);
377   assert (y2 >= y1);
378   assert (x1 >= 0);
379   assert (y1 >= 0);
380   assert (x2 < t->n[H]);
381   assert (y2 < t->n[V]);
382
383   if (f_h != -1)
384     {
385       int x;
386       for (x = x1; x <= x2; x++)
387         {
388           t->rh[x + t->n[H] * y1] = f_h;
389           t->rh[x + t->n[H] * (y2 + 1)] = f_h;
390         }
391     }
392   if (f_v != -1)
393     {
394       int y;
395       for (y = y1; y <= y2; y++)
396         {
397           t->rv[x1 + (t->n[H] + 1) * y] = f_v;
398           t->rv[(x2 + 1) + (t->n[H] + 1) * y] = f_v;
399         }
400     }
401
402   if (i_h != -1)
403     {
404       int y;
405
406       for (y = y1 + 1; y <= y2; y++)
407         {
408           int x;
409
410           for (x = x1; x <= x2; x++)
411             t->rh[x + t->n[H] * y] = i_h;
412         }
413     }
414   if (i_v != -1)
415     {
416       int x;
417
418       for (x = x1 + 1; x <= x2; x++)
419         {
420           int y;
421
422           for (y = y1; y <= y2; y++)
423             t->rv[x + (t->n[H] + 1) * y] = i_v;
424         }
425     }
426 }
427 \f
428 /* Cells. */
429
430 static void
431 do_table_text (struct table *table, int c, int r, unsigned opt, char *text)
432 {
433   assert (c >= 0);
434   assert (r >= 0);
435   assert (c < table->n[H]);
436   assert (r < table->n[V]);
437
438   if (debugging)
439     {
440       if (c < 0 || r < 0 || c >= table->n[H] || r >= table->n[V])
441         {
442           printf ("table_text(): bad cell (%d,%d) in table size (%d,%d)\n",
443                   c, r, table->n[H], table->n[V]);
444           return;
445         }
446     }
447
448   table->cc[c + r * table->n[H]] = text;
449   table->ct[c + r * table->n[H]] = opt;
450 }
451
452 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
453    TEXT. */
454 void
455 table_text (struct table *table, int c, int r, unsigned opt,
456           const char *text)
457 {
458   do_table_text (table, c, r, opt, pool_strdup (table->container, text));
459 }
460
461 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
462    FORMAT, which is formatted as if passed to printf. */
463 void
464 table_text_format (struct table *table, int c, int r, unsigned opt,
465                    const char *format, ...)
466 {
467   va_list args;
468
469   va_start (args, format);
470   do_table_text (table, c, r, opt,
471                  pool_vasprintf (table->container, format, args));
472   va_end (args);
473 }
474
475 static struct table_cell *
476 add_joined_cell (struct table *table, int x1, int y1, int x2, int y2,
477                  unsigned opt)
478 {
479   assert (x1 >= 0);
480   assert (y1 >= 0);
481   assert (y2 >= y1);
482   assert (x2 >= x1);
483   assert (y2 < table->n[V]);
484   assert (x2 < table->n[H]);
485
486   if (debugging)
487     {
488       if (x1 < 0 || x1 >= table->n[H]
489           || y1 < 0 || y1 >= table->n[V]
490           || x2 < x1 || x2 >= table->n[H]
491           || y2 < y1 || y2 >= table->n[V])
492         {
493           printf ("table_joint_text(): bad cell "
494                   "(%d,%d)-(%d,%d) in table size (%d,%d)\n",
495                   x1, y1, x2, y2, table->n[H], table->n[V]);
496           return NULL;
497         }
498     }
499
500   table_box (table, -1, -1, TABLE_STROKE_NONE, TABLE_STROKE_NONE,
501              x1, y1, x2, y2);
502
503   struct table_cell *cell = pool_alloc (table->container, sizeof *cell);
504   *cell = (struct table_cell) {
505     .d = { [TABLE_HORZ] = { x1, ++x2 },
506            [TABLE_VERT] = { y1, ++y2 } },
507     .options = opt,
508   };
509
510   void **cc = &table->cc[x1 + y1 * table->n[H]];
511   unsigned short *ct = &table->ct[x1 + y1 * table->n[H]];
512   const int ofs = table->n[H] - (x2 - x1);
513   for (int y = y1; y < y2; y++)
514     {
515       for (int x = x1; x < x2; x++)
516         {
517           *cc++ = cell;
518           *ct++ = opt | TAB_JOIN;
519         }
520
521       cc += ofs;
522       ct += ofs;
523     }
524
525   return cell;
526 }
527
528 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
529    options OPT to have text value TEXT. */
530 void
531 table_joint_text (struct table *table, int x1, int y1, int x2, int y2,
532                   unsigned opt, const char *text)
533 {
534   char *s = pool_strdup (table->container, text);
535   if (x1 == x2 && y1 == y2)
536     do_table_text (table, x1, y1, opt, s);
537   else
538     add_joined_cell (table, x1, y1, x2, y2, opt)->text = s;
539 }
540
541 static struct table_cell *
542 get_joined_cell (struct table *table, int x, int y)
543 {
544   int index = x + y * table->n[H];
545   unsigned short opt = table->ct[index];
546   struct table_cell *cell;
547
548   if (opt & TAB_JOIN)
549     cell = table->cc[index];
550   else
551     {
552       char *text = table->cc[index];
553
554       cell = add_joined_cell (table, x, y, x, y, table->ct[index]);
555       cell->text = text ? text : pool_strdup (table->container, "");
556     }
557   return cell;
558 }
559
560 /* Sets the subscripts for column X, row Y in TABLE. */
561 void
562 table_add_subscripts (struct table *table, int x, int y,
563                       char **subscripts, size_t n_subscripts)
564 {
565   struct table_cell *cell = get_joined_cell (table, x, y);
566
567   cell->n_subscripts = n_subscripts;
568   cell->subscripts = pool_nalloc (table->container, n_subscripts,
569                                   sizeof *cell->subscripts);
570   for (size_t i = 0; i < n_subscripts; i++)
571     cell->subscripts[i] = pool_strdup (table->container, subscripts[i]);
572 }
573
574 /* Attaches a reference to the NF footnotes at F to the cell at column X, row Y
575    in TABLE. */
576 void
577 table_add_footnotes (struct table *table, int x, int y,
578                      struct pivot_footnote **f, size_t nf)
579 {
580   struct table_cell *cell = get_joined_cell (table, x, y);
581
582   cell->footnotes = pool_realloc (
583     table->container, cell->footnotes,
584     (cell->n_footnotes + nf) * sizeof *cell->footnotes);
585
586   for (size_t i = 0; i < nf; i++)
587     cell->footnotes[cell->n_footnotes++] = f[i];
588 }
589
590 /* Overrides the style for column X, row Y in TABLE with STYLE.
591    Does not make a copy of STYLE, so it should either be allocated from
592    TABLE->container or have a lifetime that will outlive TABLE. */
593 void
594 table_add_style (struct table *table, int x, int y,
595                  struct table_area_style *style)
596 {
597   get_joined_cell (table, x, y)->style = style;
598 }
599
600 /* Returns true if column C, row R has no contents, otherwise false. */
601 bool
602 table_cell_is_empty (const struct table *table, int c, int r)
603 {
604   return table->cc[c + r * table->n[H]] == NULL;
605 }
606 \f
607 /* Initializes CELL with the contents of the table cell at column X and row Y
608    within TABLE. */
609 void
610 table_get_cell (const struct table *t, int x, int y, struct table_cell *cell)
611 {
612   assert (x >= 0 && x < t->n[TABLE_HORZ]);
613   assert (y >= 0 && y < t->n[TABLE_VERT]);
614
615   int index = x + y * t->n[H];
616   unsigned short opt = t->ct[index];
617   const void *cc = t->cc[index];
618
619   struct table_area_style *style
620     = t->styles[(opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT];
621   if (opt & TAB_JOIN)
622     {
623       const struct table_cell *jc = cc;
624       *cell = *jc;
625       if (!cell->style)
626         cell->style = style;
627     }
628   else
629     *cell = (struct table_cell) {
630       .d = { [TABLE_HORZ] = { x, x + 1 },
631              [TABLE_VERT] = { y, y + 1 } },
632       .options = opt,
633       .text = CONST_CAST (char *, cc ? cc : ""),
634       .style = style,
635     };
636
637   assert (cell->style);
638 }
639
640 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
641    representing a rule running alongside one of the cells in TABLE.
642
643    Suppose NC is the number of columns in TABLE and NR is the number of rows.
644    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
645    (0,0), the return value is the rule that runs vertically on the left side of
646    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
647    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
648    cell (NC-1,0).
649
650    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
651    within a 7x7 table.  The '|' characters at the intersection of the X labels
652    and Y labels show the rule whose style would be returned by calling
653    table_get_rule with those X and Y values:
654
655                            0  1  2  3  4  5  6  7
656                            +--+--+--+--+--+--+--+
657                          0 |  |  |  |  |  |  |  |
658                            +--+--+--+--+--+--+--+
659                          1 |  |  |  |  |  |  |  |
660                            +--+--+--+--+--+--+--+
661                          2 |  |  |  |  |  |  |  |
662                            +--+--+--+--+--+--+--+
663                          3 |  |  |  |  |  |  |  |
664                            +--+--+--+--+--+--+--+
665                          4 |  |  |  |  |  |  |  |
666                            +--+--+--+--+--+--+--+
667                          5 |  |  |  |  |  |  |  |
668                            +--+--+--+--+--+--+--+
669                          6 |  |  |  |  |  |  |  |
670                            +--+--+--+--+--+--+--+
671
672    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
673    (X,Y) = (0,0), the return value is the rule that runs horizontally above
674    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
675    between that cell and cell (0,1); and so on, up to (0,NR), which runs
676    horizontally below cell (0,NR-1). */
677 int
678 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
679                 struct cell_color *color)
680 {
681   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
682   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
683
684   uint8_t raw = (axis == TABLE_VERT
685                  ? table->rh[x + table->n[H] * y]
686                  : table->rv[x + (table->n[H] + 1) * y]);
687   struct cell_color *p = table->rule_colors[(raw & TAB_RULE_STYLE_MASK)
688                                             >> TAB_RULE_STYLE_SHIFT];
689   *color = p ? *p : (struct cell_color) CELL_COLOR_BLACK;
690   return (raw & TAB_RULE_TYPE_MASK) >> TAB_RULE_TYPE_SHIFT;
691 }