eabcc85b89f846bd26969a9e85533e924a6191a0
[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/pivot-table.h"
33 #include "output/table-item.h"
34 #include "output/table.h"
35 #include "output/text-item.h"
36
37 #include "gl/xalloc.h"
38
39 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
40 #define H TABLE_HORZ
41 #define V TABLE_VERT
42
43 /* Increases TABLE's reference count, indicating that it has an additional
44    owner.  An table that is shared among multiple owners must not be
45    modified. */
46 struct table *
47 table_ref (const struct table *table_)
48 {
49   struct table *table = CONST_CAST (struct table *, table_);
50   table->ref_cnt++;
51   return table;
52 }
53
54 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
55    If TABLE no longer has any owners, it is freed. */
56 void
57 table_unref (struct table *table)
58 {
59   if (table != NULL)
60     {
61       assert (table->ref_cnt > 0);
62       if (--table->ref_cnt == 0)
63         pool_destroy (table->container);
64     }
65 }
66
67 /* Returns true if TABLE has more than one owner.  A table item that is shared
68    among multiple owners must not be modified. */
69 bool
70 table_is_shared (const struct table *table)
71 {
72   return table->ref_cnt > 1;
73 }
74 \f
75 struct table_area_style *
76 table_area_style_clone (struct pool *pool, const struct table_area_style *old)
77 {
78   struct table_area_style *new = pool_malloc (pool, sizeof *new);
79   *new = *old;
80   if (new->font_style.typeface)
81     new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
82   return new;
83 }
84
85 void
86 table_area_style_free (struct table_area_style *style)
87 {
88   if (style)
89     {
90       free (style->font_style.typeface);
91       free (style);
92     }
93 }
94 \f
95 const char *
96 table_halign_to_string (enum table_halign halign)
97 {
98   switch (halign)
99     {
100     case TABLE_HALIGN_LEFT: return "left";
101     case TABLE_HALIGN_CENTER: return "center";
102     case TABLE_HALIGN_RIGHT: return "right";
103     case TABLE_HALIGN_DECIMAL: return "decimal";
104     case TABLE_HALIGN_MIXED: return "mixed";
105     default: return "**error**";
106     }
107 }
108
109 const char *
110 table_valign_to_string (enum table_valign valign)
111 {
112   switch (valign)
113     {
114     case TABLE_VALIGN_TOP: return "top";
115     case TABLE_VALIGN_CENTER: return "center";
116     case TABLE_VALIGN_BOTTOM: return "bottom";
117     default: return "**error**";
118     }
119 }
120
121 enum table_halign
122 table_halign_interpret (enum table_halign halign, bool numeric)
123 {
124   switch (halign)
125     {
126     case TABLE_HALIGN_LEFT:
127     case TABLE_HALIGN_CENTER:
128     case TABLE_HALIGN_RIGHT:
129       return halign;
130
131     case TABLE_HALIGN_MIXED:
132       return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT;
133
134     case TABLE_HALIGN_DECIMAL:
135       return TABLE_HALIGN_DECIMAL;
136
137     default:
138       NOT_REACHED ();
139     }
140 }
141
142 void
143 font_style_copy (struct pool *container,
144                  struct font_style *dst, const struct font_style *src)
145 {
146   *dst = *src;
147   if (dst->typeface)
148     dst->typeface = pool_strdup (container, dst->typeface);
149 }
150
151 void
152 font_style_uninit (struct font_style *font)
153 {
154   if (font)
155     free (font->typeface);
156 }
157
158 void
159 table_area_style_copy (struct pool *container, struct table_area_style *dst,
160                        const struct table_area_style *src)
161 {
162   font_style_copy (container, &dst->font_style, &src->font_style);
163   dst->cell_style = src->cell_style;
164 }
165
166 void
167 table_area_style_uninit (struct table_area_style *area)
168 {
169   if (area)
170     font_style_uninit (&area->font_style);
171 }
172
173 const char *
174 table_stroke_to_string (enum table_stroke stroke)
175 {
176   switch (stroke)
177     {
178     case TABLE_STROKE_NONE: return "none";
179     case TABLE_STROKE_SOLID: return "solid";
180     case TABLE_STROKE_DASHED: return "dashed";
181     case TABLE_STROKE_THICK: return "thick";
182     case TABLE_STROKE_THIN: return "thin";
183     case TABLE_STROKE_DOUBLE: return "double";
184     default:
185       return "**error**";
186     }
187 }
188
189 void
190 cell_color_dump (const struct cell_color *c)
191 {
192   if (c->alpha != 255)
193     printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
194   else
195     printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
196 }
197
198 void
199 font_style_dump (const struct font_style *f)
200 {
201   printf ("%s %dpx ", f->typeface, f->size);
202   cell_color_dump (&f->fg[0]);
203   putchar ('/');
204   cell_color_dump (&f->bg[0]);
205   if (!cell_color_equal (&f->fg[0], &f->fg[1])
206       || !cell_color_equal (&f->bg[0], &f->bg[1]))
207     {
208       printf (" alt=");
209       cell_color_dump (&f->fg[1]);
210       putchar ('/');
211       cell_color_dump (&f->bg[1]);
212     }
213   if (f->bold)
214     fputs (" bold", stdout);
215   if (f->italic)
216     fputs (" italic", stdout);
217   if (f->underline)
218     fputs (" underline", stdout);
219 }
220
221 bool
222 font_style_equal (const struct font_style *a, const struct font_style *b)
223 {
224   return (a->bold == b->bold
225           && a->italic == b->italic
226           && a->underline == b->underline
227           && a->markup == b->markup
228           && cell_color_equal (&a->fg[0], &b->fg[0])
229           && cell_color_equal (&a->fg[1], &b->fg[1])
230           && cell_color_equal (&a->bg[0], &b->bg[0])
231           && cell_color_equal (&a->bg[1], &b->bg[1])
232           && !strcmp (a->typeface ? a->typeface : "",
233                       b->typeface ? b->typeface : "")
234           && a->size == b->size);
235 }
236
237 void
238 cell_style_dump (const struct cell_style *c)
239 {
240   fputs (table_halign_to_string (c->halign), stdout);
241   if (c->halign == TABLE_HALIGN_DECIMAL)
242     printf ("(%.2gpx)", c->decimal_offset);
243   printf (" %s", table_valign_to_string (c->valign));
244   printf (" %d,%d,%d,%dpx",
245           c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
246           c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);
247 }
248 \f
249
250 static const bool debugging = true;
251
252 /* Creates and returns a new table with NC columns and NR rows and initially no
253    header rows or columns.
254
255    Sets the number of header rows on each side of TABLE to HL on the
256    left, HR on the right, HT on the top, HB on the bottom.  Header rows
257    are repeated when a table is broken across multiple columns or
258    multiple pages.
259
260    The table's cells are initially empty. */
261 struct table *
262 table_create (int nc, int nr, int hl, int hr, int ht, int hb)
263 {
264   struct table *t;
265
266   t = pool_create_container (struct table, container);
267   t->n[TABLE_HORZ] = nc;
268   t->n[TABLE_VERT] = nr;
269   t->h[TABLE_HORZ][0] = hl;
270   t->h[TABLE_HORZ][1] = hr;
271   t->h[TABLE_VERT][0] = ht;
272   t->h[TABLE_VERT][1] = hb;
273   t->ref_cnt = 1;
274
275   t->cc = pool_calloc (t->container, nr * nc, sizeof *t->cc);
276   t->ct = pool_calloc (t->container, nr * nc, sizeof *t->ct);
277
278   t->rh = pool_nmalloc (t->container, nc, nr + 1);
279   memset (t->rh, TABLE_STROKE_NONE, nc * (nr + 1));
280
281   t->rv = pool_nmalloc (t->container, nr, nc + 1);
282   memset (t->rv, TABLE_STROKE_NONE, nr * (nc + 1));
283
284   memset (t->styles, 0, sizeof t->styles);
285   memset (t->rule_colors, 0, sizeof t->rule_colors);
286
287   return t;
288 }
289 \f
290 /* Rules. */
291
292 /* Draws a vertical line to the left of cells at horizontal position X
293    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
294 void
295 table_vline (struct table *t, int style, int x, int y1, int y2)
296 {
297   if (debugging)
298     {
299       if (x < 0 || x > t->n[H]
300           || y1 < 0 || y1 >= t->n[V]
301           || y2 < 0 || y2 >= t->n[V])
302         {
303           printf ("bad vline: x=%d y=(%d,%d) in table size (%d,%d)\n",
304                   x, y1, y2, t->n[H], t->n[V]);
305           return;
306         }
307     }
308
309   assert (x >= 0);
310   assert (x <= t->n[H]);
311   assert (y1 >= 0);
312   assert (y2 >= y1);
313   assert (y2 <= t->n[V]);
314
315   if (style != -1)
316     {
317       int y;
318       for (y = y1; y <= y2; y++)
319         t->rv[x + (t->n[H] + 1) * y] = style;
320     }
321 }
322
323 /* Draws a horizontal line above cells at vertical position Y from X1
324    to X2 inclusive in style STYLE, if style is not -1. */
325 void
326 table_hline (struct table *t, int style, int x1, int x2, int y)
327 {
328   if (debugging)
329     {
330       if (y < 0 || y > t->n[V]
331           || x1 < 0 || x1 >= t->n[H]
332           || x2 < 0 || x2 >= t->n[H])
333         {
334           printf ("bad hline: x=(%d,%d) y=%d in table size (%d,%d)\n",
335                   x1, x2, y, t->n[H], t->n[V]);
336           return;
337         }
338     }
339
340   assert (y >= 0);
341   assert (y <= t->n[V]);
342   assert (x2 >= x1);
343   assert (x1 >= 0);
344   assert (x2 < t->n[H]);
345
346   if (style != -1)
347     {
348       int x;
349       for (x = x1; x <= x2; x++)
350         t->rh[x + t->n[H] * y] = style;
351     }
352 }
353
354 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
355    lines of style F_H and vertical lines of style F_V.  Fills the
356    interior of the box with horizontal lines of style I_H and vertical
357    lines of style I_V.  Any of the line styles may be -1 to avoid
358    drawing those lines.  This is distinct from 0, which draws a null
359    line. */
360 void
361 table_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
362            int x1, int y1, int x2, int y2)
363 {
364   if (debugging)
365     {
366       if (x1 < 0 || x1 >= t->n[H]
367           || x2 < 0 || x2 >= t->n[H]
368           || y1 < 0 || y1 >= t->n[V]
369           || y2 < 0 || y2 >= t->n[V])
370         {
371           printf ("bad box: (%d,%d)-(%d,%d) in table size (%d,%d)\n",
372                   x1, y1, x2, y2, t->n[H], t->n[V]);
373           NOT_REACHED ();
374         }
375     }
376
377   assert (x2 >= x1);
378   assert (y2 >= y1);
379   assert (x1 >= 0);
380   assert (y1 >= 0);
381   assert (x2 < t->n[H]);
382   assert (y2 < t->n[V]);
383
384   if (f_h != -1)
385     {
386       int x;
387       for (x = x1; x <= x2; x++)
388         {
389           t->rh[x + t->n[H] * y1] = f_h;
390           t->rh[x + t->n[H] * (y2 + 1)] = f_h;
391         }
392     }
393   if (f_v != -1)
394     {
395       int y;
396       for (y = y1; y <= y2; y++)
397         {
398           t->rv[x1 + (t->n[H] + 1) * y] = f_v;
399           t->rv[(x2 + 1) + (t->n[H] + 1) * y] = f_v;
400         }
401     }
402
403   if (i_h != -1)
404     {
405       int y;
406
407       for (y = y1 + 1; y <= y2; y++)
408         {
409           int x;
410
411           for (x = x1; x <= x2; x++)
412             t->rh[x + t->n[H] * y] = i_h;
413         }
414     }
415   if (i_v != -1)
416     {
417       int x;
418
419       for (x = x1 + 1; x <= x2; x++)
420         {
421           int y;
422
423           for (y = y1; y <= y2; y++)
424             t->rv[x + (t->n[H] + 1) * y] = i_v;
425         }
426     }
427 }
428 \f
429 /* Cells. */
430
431 /* Fill TABLE cells (X1,X2)-(Y1,Y2), inclusive, with VALUE and OPT. */
432 void
433 table_put (struct table *table, int x1, int y1, int x2, int y2,
434            unsigned opt, const struct pivot_value *value)
435 {
436   assert (0 <= x1 && x1 <= x2 && x2 < table->n[H]);
437   assert (0 <= y1 && y1 <= y2 && y2 < table->n[V]);
438
439   if (x1 == x2 && y1 == y2)
440     {
441       table->cc[x1 + y1 * table->n[H]] = CONST_CAST (struct pivot_value *, value);
442       table->ct[x1 + y1 * table->n[H]] = opt;
443     }
444   else
445     {
446       table_box (table, -1, -1, TABLE_STROKE_NONE, TABLE_STROKE_NONE,
447                  x1, y1, x2, y2);
448
449       struct table_cell *cell = pool_alloc (table->container, sizeof *cell);
450       *cell = (struct table_cell) {
451         .d = { [H] = { x1, x2 + 1 }, [V] = { y1, y2 + 1 } },
452         .options = opt,
453         .value = value,
454       };
455
456       for (int y = y1; y <= y2; y++)
457         {
458           size_t ofs = x1 + y * table->n[H];
459           void **cc = &table->cc[ofs];
460           unsigned short *ct = &table->ct[ofs];
461           for (int x = x1; x <= x2; x++)
462             {
463               *cc++ = cell;
464               *ct++ = opt | TAB_JOIN;
465             }
466         }
467     }
468 }
469
470 static void
471 free_value (void *value_)
472 {
473   struct pivot_value *value = value_;
474   pivot_value_destroy (value);
475 }
476
477 void
478 table_put_owned (struct table *table, int x1, int y1, int x2, int y2,
479                  unsigned opt, struct pivot_value *value)
480 {
481   table_put (table, x1, y1, x2, y2, opt, value);
482   pool_register (table->container, free_value, value);
483 }
484
485 /* Returns true if column C, row R has no contents, otherwise false. */
486 bool
487 table_cell_is_empty (const struct table *table, int c, int r)
488 {
489   return table->cc[c + r * table->n[H]] == NULL;
490 }
491 \f
492 /* Initializes CELL with the contents of the table cell at column X and row Y
493    within TABLE. */
494 void
495 table_get_cell (const struct table *t, int x, int y, struct table_cell *cell)
496 {
497   assert (x >= 0 && x < t->n[TABLE_HORZ]);
498   assert (y >= 0 && y < t->n[TABLE_VERT]);
499
500   int index = x + y * t->n[H];
501   unsigned short opt = t->ct[index];
502   const void *cc = t->cc[index];
503
504   struct table_area_style *style
505     = t->styles[(opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT];
506
507   static const struct pivot_value empty_value = {
508     .type = PIVOT_VALUE_TEXT,
509     .text = {
510       .local = (char *) "",
511       .c = (char *) "",
512       .id = (char *) "",
513       .user_provided = true,
514     },
515   };
516
517   if (opt & TAB_JOIN)
518     {
519       const struct table_cell *jc = cc;
520       *cell = *jc;
521       if (!cell->value)
522         cell->value = &empty_value;
523       if (!cell->font_style)
524         cell->font_style = &style->font_style;
525       if (!cell->cell_style)
526         cell->cell_style = &style->cell_style;
527     }
528   else
529     {
530       const struct pivot_value *v = cc ? cc : &empty_value;
531       *cell = (struct table_cell) {
532         .d = { [H] = { x, x + 1 }, [V] = { y, y + 1 } },
533         .options = opt,
534         .value = v,
535         .font_style = v->font_style ? v->font_style : &style->font_style,
536         .cell_style = v->cell_style ? v->cell_style : &style->cell_style,
537       };
538     }
539
540   assert (cell->font_style);
541   assert (cell->cell_style);
542 }
543
544 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
545    representing a rule running alongside one of the cells in TABLE.
546
547    Suppose NC is the number of columns in TABLE and NR is the number of rows.
548    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
549    (0,0), the return value is the rule that runs vertically on the left side of
550    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
551    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
552    cell (NC-1,0).
553
554    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
555    within a 7x7 table.  The '|' characters at the intersection of the X labels
556    and Y labels show the rule whose style would be returned by calling
557    table_get_rule with those X and Y values:
558
559                            0  1  2  3  4  5  6  7
560                            +--+--+--+--+--+--+--+
561                          0 |  |  |  |  |  |  |  |
562                            +--+--+--+--+--+--+--+
563                          1 |  |  |  |  |  |  |  |
564                            +--+--+--+--+--+--+--+
565                          2 |  |  |  |  |  |  |  |
566                            +--+--+--+--+--+--+--+
567                          3 |  |  |  |  |  |  |  |
568                            +--+--+--+--+--+--+--+
569                          4 |  |  |  |  |  |  |  |
570                            +--+--+--+--+--+--+--+
571                          5 |  |  |  |  |  |  |  |
572                            +--+--+--+--+--+--+--+
573                          6 |  |  |  |  |  |  |  |
574                            +--+--+--+--+--+--+--+
575
576    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
577    (X,Y) = (0,0), the return value is the rule that runs horizontally above
578    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
579    between that cell and cell (0,1); and so on, up to (0,NR), which runs
580    horizontally below cell (0,NR-1). */
581 int
582 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
583                 struct cell_color *color)
584 {
585   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
586   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
587
588   uint8_t raw = (axis == TABLE_VERT
589                  ? table->rh[x + table->n[H] * y]
590                  : table->rv[x + (table->n[H] + 1) * y]);
591   struct cell_color *p = table->rule_colors[(raw & TAB_RULE_STYLE_MASK)
592                                             >> TAB_RULE_STYLE_SHIFT];
593   *color = p ? *p : (struct cell_color) CELL_COLOR_BLACK;
594   return (raw & TAB_RULE_TYPE_MASK) >> TAB_RULE_TYPE_SHIFT;
595 }