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 /* Increases TABLE's reference count, indicating that it has an additional
39 owner. An table that is shared among multiple owners must not be
40 modified. */
41 struct table *
table_ref(const struct table * table_)42 table_ref (const struct table *table_)
43 {
44 struct table *table = CONST_CAST (struct table *, table_);
45 table->ref_cnt++;
46 return table;
47 }
48
49 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
50 If TABLE no longer has any owners, it is freed. */
51 void
table_unref(struct table * table)52 table_unref (struct table *table)
53 {
54 if (table != NULL)
55 {
56 assert (table->ref_cnt > 0);
57 if (--table->ref_cnt == 0)
58 pool_destroy (table->container);
59 }
60 }
61
62 /* Returns true if TABLE has more than one owner. A table item that is shared
63 among multiple owners must not be modified. */
64 bool
table_is_shared(const struct table * table)65 table_is_shared (const struct table *table)
66 {
67 return table->ref_cnt > 1;
68 }
69
70 struct area_style *
area_style_clone(struct pool * pool,const struct area_style * old)71 area_style_clone (struct pool *pool, const struct area_style *old)
72 {
73 struct area_style *new = pool_malloc (pool, sizeof *new);
74 *new = *old;
75 if (new->font_style.typeface)
76 new->font_style.typeface = pool_strdup (pool, new->font_style.typeface);
77 return new;
78 }
79
80 void
area_style_free(struct area_style * style)81 area_style_free (struct area_style *style)
82 {
83 if (style)
84 {
85 free (style->font_style.typeface);
86 free (style);
87 }
88 }
89
90 void
table_cell_format_footnote_markers(const struct table_cell * cell,struct string * s)91 table_cell_format_footnote_markers (const struct table_cell *cell,
92 struct string *s)
93 {
94 for (size_t i = 0; i < cell->n_footnotes; i++)
95 {
96 if (i)
97 ds_put_byte (s, ',');
98 ds_put_cstr (s, cell->footnotes[i]->marker);
99 }
100 }
101
102 static const struct footnote **
add_footnotes(const struct footnote ** refs,size_t n_refs,const struct footnote ** footnotes,size_t * allocated,size_t * n)103 add_footnotes (const struct footnote **refs, size_t n_refs,
104 const struct footnote **footnotes, size_t *allocated, size_t *n)
105 {
106 for (size_t i = 0; i < n_refs; i++)
107 {
108 const struct footnote *f = refs[i];
109 if (f->idx >= *allocated)
110 {
111 size_t new_allocated = (f->idx + 1) * 2;
112 footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
113 while (*allocated < new_allocated)
114 footnotes[(*allocated)++] = NULL;
115 }
116 footnotes[f->idx] = f;
117 if (f->idx >= *n)
118 *n = f->idx + 1;
119 }
120 return footnotes;
121 }
122
123 size_t
table_collect_footnotes(const struct table_item * item,const struct footnote *** footnotesp)124 table_collect_footnotes (const struct table_item *item,
125 const struct footnote ***footnotesp)
126 {
127 const struct footnote **footnotes = NULL;
128 size_t allocated = 0;
129 size_t n = 0;
130
131 struct table *t = item->table;
132 for (int y = 0; y < table_nr (t); y++)
133 {
134 struct table_cell cell;
135 for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
136 {
137 table_get_cell (t, x, y, &cell);
138
139 if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
140 footnotes = add_footnotes (cell.footnotes, cell.n_footnotes,
141 footnotes, &allocated, &n);
142 }
143 }
144
145 const struct table_item_text *title = table_item_get_title (item);
146 if (title)
147 footnotes = add_footnotes (title->footnotes, title->n_footnotes,
148 footnotes, &allocated, &n);
149
150 const struct table_item_layers *layers = table_item_get_layers (item);
151 if (layers)
152 {
153 for (size_t i = 0; i < layers->n_layers; i++)
154 footnotes = add_footnotes (layers->layers[i].footnotes,
155 layers->layers[i].n_footnotes,
156 footnotes, &allocated, &n);
157 }
158
159 const struct table_item_text *caption = table_item_get_caption (item);
160 if (caption)
161 footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
162 footnotes, &allocated, &n);
163
164 size_t n_nonnull = 0;
165 for (size_t i = 0; i < n; i++)
166 if (footnotes[i])
167 footnotes[n_nonnull++] = footnotes[i];
168
169 *footnotesp = footnotes;
170 return n_nonnull;
171 }
172
173 /* Returns a table that contains a single cell, whose contents are the
174 left-aligned TEXT. */
175 struct table *
table_from_string(const char * text)176 table_from_string (const char *text)
177 {
178 struct table *t = table_create (1, 1, 0, 0, 0, 0);
179 t->styles[0] = pool_alloc (t->container, sizeof *t->styles[0]);
180 *t->styles[0] = (struct area_style) {
181 AREA_STYLE_INITIALIZER__,
182 .cell_style.halign = TABLE_HALIGN_LEFT,
183 .cell_style.valign = TABLE_VALIGN_TOP
184 };
185 table_text (t, 0, 0, 0 << TAB_STYLE_SHIFT, text);
186 return t;
187 }
188
189 const char *
table_halign_to_string(enum table_halign halign)190 table_halign_to_string (enum table_halign halign)
191 {
192 switch (halign)
193 {
194 case TABLE_HALIGN_LEFT: return "left";
195 case TABLE_HALIGN_CENTER: return "center";
196 case TABLE_HALIGN_RIGHT: return "right";
197 case TABLE_HALIGN_DECIMAL: return "decimal";
198 case TABLE_HALIGN_MIXED: return "mixed";
199 default: return "**error**";
200 }
201 }
202
203 const char *
table_valign_to_string(enum table_valign valign)204 table_valign_to_string (enum table_valign valign)
205 {
206 switch (valign)
207 {
208 case TABLE_VALIGN_TOP: return "top";
209 case TABLE_VALIGN_CENTER: return "center";
210 case TABLE_VALIGN_BOTTOM: return "bottom";
211 default: return "**error**";
212 }
213 }
214
215 enum table_halign
table_halign_interpret(enum table_halign halign,bool numeric)216 table_halign_interpret (enum table_halign halign, bool numeric)
217 {
218 switch (halign)
219 {
220 case TABLE_HALIGN_LEFT:
221 case TABLE_HALIGN_CENTER:
222 case TABLE_HALIGN_RIGHT:
223 return halign;
224
225 case TABLE_HALIGN_MIXED:
226 return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT;
227
228 case TABLE_HALIGN_DECIMAL:
229 return TABLE_HALIGN_DECIMAL;
230
231 default:
232 NOT_REACHED ();
233 }
234 }
235
236 void
font_style_copy(struct pool * container,struct font_style * dst,const struct font_style * src)237 font_style_copy (struct pool *container,
238 struct font_style *dst, const struct font_style *src)
239 {
240 *dst = *src;
241 if (dst->typeface)
242 dst->typeface = pool_strdup (container, dst->typeface);
243 }
244
245 void
font_style_uninit(struct font_style * font)246 font_style_uninit (struct font_style *font)
247 {
248 if (font)
249 free (font->typeface);
250 }
251
252 void
area_style_copy(struct pool * container,struct area_style * dst,const struct area_style * src)253 area_style_copy (struct pool *container,
254 struct area_style *dst, const struct area_style *src)
255 {
256 font_style_copy (container, &dst->font_style, &src->font_style);
257 dst->cell_style = src->cell_style;
258 }
259
260 void
area_style_uninit(struct area_style * area)261 area_style_uninit (struct area_style *area)
262 {
263 if (area)
264 font_style_uninit (&area->font_style);
265 }
266
267 const char *
table_stroke_to_string(enum table_stroke stroke)268 table_stroke_to_string (enum table_stroke stroke)
269 {
270 switch (stroke)
271 {
272 case TABLE_STROKE_NONE: return "none";
273 case TABLE_STROKE_SOLID: return "solid";
274 case TABLE_STROKE_DASHED: return "dashed";
275 case TABLE_STROKE_THICK: return "thick";
276 case TABLE_STROKE_THIN: return "thin";
277 case TABLE_STROKE_DOUBLE: return "double";
278 default:
279 return "**error**";
280 }
281 }
282
283 void
cell_color_dump(const struct cell_color * c)284 cell_color_dump (const struct cell_color *c)
285 {
286 if (c->alpha != 255)
287 printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha);
288 else
289 printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b);
290 }
291
292 void
font_style_dump(const struct font_style * f)293 font_style_dump (const struct font_style *f)
294 {
295 printf ("%s %dpx ", f->typeface, f->size);
296 cell_color_dump (&f->fg[0]);
297 putchar ('/');
298 cell_color_dump (&f->bg[0]);
299 if (!cell_color_equal (&f->fg[0], &f->fg[1])
300 || !cell_color_equal (&f->bg[0], &f->bg[1]))
301 {
302 printf (" alt=");
303 cell_color_dump (&f->fg[1]);
304 putchar ('/');
305 cell_color_dump (&f->bg[1]);
306 }
307 if (f->bold)
308 fputs (" bold", stdout);
309 if (f->italic)
310 fputs (" italic", stdout);
311 if (f->underline)
312 fputs (" underline", stdout);
313 }
314
315 void
cell_style_dump(const struct cell_style * c)316 cell_style_dump (const struct cell_style *c)
317 {
318 fputs (table_halign_to_string (c->halign), stdout);
319 if (c->halign == TABLE_HALIGN_DECIMAL)
320 printf ("(%.2gpx)", c->decimal_offset);
321 printf (" %s", table_valign_to_string (c->valign));
322 printf (" %d,%d,%d,%dpx",
323 c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1],
324 c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]);
325 }
326
327
328 static const bool debugging = true;
329
330 /* Creates and returns a new table with NC columns and NR rows and initially no
331 header rows or columns.
332
333 Sets the number of header rows on each side of TABLE to HL on the
334 left, HR on the right, HT on the top, HB on the bottom. Header rows
335 are repeated when a table is broken across multiple columns or
336 multiple pages.
337
338 The table's cells are initially empty. */
339 struct table *
table_create(int nc,int nr,int hl,int hr,int ht,int hb)340 table_create (int nc, int nr, int hl, int hr, int ht, int hb)
341 {
342 struct table *t;
343
344 t = pool_create_container (struct table, container);
345 t->n[TABLE_HORZ] = nc;
346 t->n[TABLE_VERT] = nr;
347 t->h[TABLE_HORZ][0] = hl;
348 t->h[TABLE_HORZ][1] = hr;
349 t->h[TABLE_VERT][0] = ht;
350 t->h[TABLE_VERT][1] = hb;
351 t->ref_cnt = 1;
352
353 t->cc = pool_calloc (t->container, nr * nc, sizeof *t->cc);
354 t->ct = pool_calloc (t->container, nr * nc, sizeof *t->ct);
355
356 t->rh = pool_nmalloc (t->container, nc, nr + 1);
357 memset (t->rh, TABLE_STROKE_NONE, nc * (nr + 1));
358
359 t->rv = pool_nmalloc (t->container, nr, nc + 1);
360 memset (t->rv, TABLE_STROKE_NONE, nr * (nc + 1));
361
362 memset (t->styles, 0, sizeof t->styles);
363 memset (t->rule_colors, 0, sizeof t->rule_colors);
364
365 return t;
366 }
367
368 /* Rules. */
369
370 /* Draws a vertical line to the left of cells at horizontal position X
371 from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
372 void
table_vline(struct table * t,int style,int x,int y1,int y2)373 table_vline (struct table *t, int style, int x, int y1, int y2)
374 {
375 if (debugging)
376 {
377 if (x < 0 || x > table_nc (t)
378 || y1 < 0 || y1 >= table_nr (t)
379 || y2 < 0 || y2 >= table_nr (t))
380 {
381 printf ("bad vline: x=%d y=(%d,%d) in table size (%d,%d)\n",
382 x, y1, y2, table_nc (t), table_nr (t));
383 return;
384 }
385 }
386
387 assert (x >= 0);
388 assert (x <= table_nc (t));
389 assert (y1 >= 0);
390 assert (y2 >= y1);
391 assert (y2 <= table_nr (t));
392
393 if (style != -1)
394 {
395 int y;
396 for (y = y1; y <= y2; y++)
397 t->rv[x + (table_nc (t) + 1) * y] = style;
398 }
399 }
400
401 /* Draws a horizontal line above cells at vertical position Y from X1
402 to X2 inclusive in style STYLE, if style is not -1. */
403 void
table_hline(struct table * t,int style,int x1,int x2,int y)404 table_hline (struct table *t, int style, int x1, int x2, int y)
405 {
406 if (debugging)
407 {
408 if (y < 0 || y > table_nr (t)
409 || x1 < 0 || x1 >= table_nc (t)
410 || x2 < 0 || x2 >= table_nc (t))
411 {
412 printf ("bad hline: x=(%d,%d) y=%d in table size (%d,%d)\n",
413 x1, x2, y, table_nc (t), table_nr (t));
414 return;
415 }
416 }
417
418 assert (y >= 0);
419 assert (y <= table_nr (t));
420 assert (x2 >= x1);
421 assert (x1 >= 0);
422 assert (x2 < table_nc (t));
423
424 if (style != -1)
425 {
426 int x;
427 for (x = x1; x <= x2; x++)
428 t->rh[x + table_nc (t) * y] = style;
429 }
430 }
431
432 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
433 lines of style F_H and vertical lines of style F_V. Fills the
434 interior of the box with horizontal lines of style I_H and vertical
435 lines of style I_V. Any of the line styles may be -1 to avoid
436 drawing those lines. This is distinct from 0, which draws a null
437 line. */
438 void
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)439 table_box (struct table *t, int f_h, int f_v, int i_h, int i_v,
440 int x1, int y1, int x2, int y2)
441 {
442 if (debugging)
443 {
444 if (x1 < 0 || x1 >= table_nc (t)
445 || x2 < 0 || x2 >= table_nc (t)
446 || y1 < 0 || y1 >= table_nr (t)
447 || y2 < 0 || y2 >= table_nr (t))
448 {
449 printf ("bad box: (%d,%d)-(%d,%d) in table size (%d,%d)\n",
450 x1, y1, x2, y2, table_nc (t), table_nr (t));
451 NOT_REACHED ();
452 }
453 }
454
455 assert (x2 >= x1);
456 assert (y2 >= y1);
457 assert (x1 >= 0);
458 assert (y1 >= 0);
459 assert (x2 < table_nc (t));
460 assert (y2 < table_nr (t));
461
462 if (f_h != -1)
463 {
464 int x;
465 for (x = x1; x <= x2; x++)
466 {
467 t->rh[x + table_nc (t) * y1] = f_h;
468 t->rh[x + table_nc (t) * (y2 + 1)] = f_h;
469 }
470 }
471 if (f_v != -1)
472 {
473 int y;
474 for (y = y1; y <= y2; y++)
475 {
476 t->rv[x1 + (table_nc (t) + 1) * y] = f_v;
477 t->rv[(x2 + 1) + (table_nc (t) + 1) * y] = f_v;
478 }
479 }
480
481 if (i_h != -1)
482 {
483 int y;
484
485 for (y = y1 + 1; y <= y2; y++)
486 {
487 int x;
488
489 for (x = x1; x <= x2; x++)
490 t->rh[x + table_nc (t) * y] = i_h;
491 }
492 }
493 if (i_v != -1)
494 {
495 int x;
496
497 for (x = x1 + 1; x <= x2; x++)
498 {
499 int y;
500
501 for (y = y1; y <= y2; y++)
502 t->rv[x + (table_nc (t) + 1) * y] = i_v;
503 }
504 }
505 }
506
507 /* Cells. */
508
509 static void
do_table_text(struct table * table,int c,int r,unsigned opt,char * text)510 do_table_text (struct table *table, int c, int r, unsigned opt, char *text)
511 {
512 assert (c >= 0);
513 assert (r >= 0);
514 assert (c < table_nc (table));
515 assert (r < table_nr (table));
516
517 if (debugging)
518 {
519 if (c < 0 || r < 0 || c >= table_nc (table) || r >= table_nr (table))
520 {
521 printf ("table_text(): bad cell (%d,%d) in table size (%d,%d)\n",
522 c, r, table_nc (table), table_nr (table));
523 return;
524 }
525 }
526
527 table->cc[c + r * table_nc (table)] = text;
528 table->ct[c + r * table_nc (table)] = opt;
529 }
530
531 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
532 TEXT. */
533 void
table_text(struct table * table,int c,int r,unsigned opt,const char * text)534 table_text (struct table *table, int c, int r, unsigned opt,
535 const char *text)
536 {
537 do_table_text (table, c, r, opt, pool_strdup (table->container, text));
538 }
539
540 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
541 FORMAT, which is formatted as if passed to printf. */
542 void
table_text_format(struct table * table,int c,int r,unsigned opt,const char * format,...)543 table_text_format (struct table *table, int c, int r, unsigned opt,
544 const char *format, ...)
545 {
546 va_list args;
547
548 va_start (args, format);
549 do_table_text (table, c, r, opt,
550 pool_vasprintf (table->container, format, args));
551 va_end (args);
552 }
553
554 static struct table_cell *
add_joined_cell(struct table * table,int x1,int y1,int x2,int y2,unsigned opt)555 add_joined_cell (struct table *table, int x1, int y1, int x2, int y2,
556 unsigned opt)
557 {
558 assert (x1 >= 0);
559 assert (y1 >= 0);
560 assert (y2 >= y1);
561 assert (x2 >= x1);
562 assert (y2 < table_nr (table));
563 assert (x2 < table_nc (table));
564
565 if (debugging)
566 {
567 if (x1 < 0 || x1 >= table_nc (table)
568 || y1 < 0 || y1 >= table_nr (table)
569 || x2 < x1 || x2 >= table_nc (table)
570 || y2 < y1 || y2 >= table_nr (table))
571 {
572 printf ("table_joint_text(): bad cell "
573 "(%d,%d)-(%d,%d) in table size (%d,%d)\n",
574 x1, y1, x2, y2, table_nc (table), table_nr (table));
575 return NULL;
576 }
577 }
578
579 table_box (table, -1, -1, TABLE_STROKE_NONE, TABLE_STROKE_NONE,
580 x1, y1, x2, y2);
581
582 struct table_cell *cell = pool_alloc (table->container, sizeof *cell);
583 *cell = (struct table_cell) {
584 .d = { [TABLE_HORZ] = { x1, ++x2 },
585 [TABLE_VERT] = { y1, ++y2 } },
586 .options = opt,
587 };
588
589 void **cc = &table->cc[x1 + y1 * table_nc (table)];
590 unsigned short *ct = &table->ct[x1 + y1 * table_nc (table)];
591 const int ofs = table_nc (table) - (x2 - x1);
592 for (int y = y1; y < y2; y++)
593 {
594 for (int x = x1; x < x2; x++)
595 {
596 *cc++ = cell;
597 *ct++ = opt | TAB_JOIN;
598 }
599
600 cc += ofs;
601 ct += ofs;
602 }
603
604 return cell;
605 }
606
607 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
608 options OPT to have text value TEXT. */
609 void
table_joint_text(struct table * table,int x1,int y1,int x2,int y2,unsigned opt,const char * text)610 table_joint_text (struct table *table, int x1, int y1, int x2, int y2,
611 unsigned opt, const char *text)
612 {
613 char *s = pool_strdup (table->container, text);
614 if (x1 == x2 && y1 == y2)
615 do_table_text (table, x1, y1, opt, s);
616 else
617 add_joined_cell (table, x1, y1, x2, y2, opt)->text = s;
618 }
619
620 static struct table_cell *
get_joined_cell(struct table * table,int x,int y)621 get_joined_cell (struct table *table, int x, int y)
622 {
623 int index = x + y * table_nc (table);
624 unsigned short opt = table->ct[index];
625 struct table_cell *cell;
626
627 if (opt & TAB_JOIN)
628 cell = table->cc[index];
629 else
630 {
631 char *text = table->cc[index];
632
633 cell = add_joined_cell (table, x, y, x, y, table->ct[index]);
634 cell->text = text ? text : pool_strdup (table->container, "");
635 }
636 return cell;
637 }
638
639 /* Sets the subscripts for column X, row Y in TABLE. */
640 void
table_add_subscripts(struct table * table,int x,int y,char ** subscripts,size_t n_subscripts)641 table_add_subscripts (struct table *table, int x, int y,
642 char **subscripts, size_t n_subscripts)
643 {
644 struct table_cell *cell = get_joined_cell (table, x, y);
645
646 cell->n_subscripts = n_subscripts;
647 cell->subscripts = pool_nalloc (table->container, n_subscripts,
648 sizeof *cell->subscripts);
649 for (size_t i = 0; i < n_subscripts; i++)
650 cell->subscripts[i] = pool_strdup (table->container, subscripts[i]);
651 }
652
653 /* Sets the superscript for column X, row Y in TABLE. */
654 void
table_add_superscript(struct table * table,int x,int y,const char * superscript)655 table_add_superscript (struct table *table, int x, int y,
656 const char *superscript)
657 {
658 get_joined_cell (table, x, y)->superscript
659 = pool_strdup (table->container, superscript);
660 }
661
662 /* Create a footnote in TABLE with MARKER (e.g. "a") as its marker and CONTENT
663 as its content. The footnote will be styled as STYLE, which is mandatory.
664 IDX must uniquely identify the footnote within TABLE.
665
666 Returns the new footnote. The return value is the only way to get to the
667 footnote later, so it is important for the caller to remember it. */
668 struct footnote *
table_create_footnote(struct table * table,size_t idx,const char * content,const char * marker,struct area_style * style)669 table_create_footnote (struct table *table, size_t idx, const char *content,
670 const char *marker, struct area_style *style)
671 {
672 assert (style);
673
674 struct footnote *f = pool_alloc (table->container, sizeof *f);
675 f->idx = idx;
676 f->content = pool_strdup (table->container, content);
677 f->marker = pool_strdup (table->container, marker);
678 f->style = style;
679 return f;
680 }
681
682 /* Attaches a reference to footnote F to the cell at column X, row Y in
683 TABLE. */
684 void
table_add_footnote(struct table * table,int x,int y,const struct footnote * f)685 table_add_footnote (struct table *table, int x, int y,
686 const struct footnote *f)
687 {
688 assert (f->style);
689
690 struct table_cell *cell = get_joined_cell (table, x, y);
691
692 cell->footnotes = pool_realloc (
693 table->container, cell->footnotes,
694 (cell->n_footnotes + 1) * sizeof *cell->footnotes);
695
696 cell->footnotes[cell->n_footnotes++] = f;
697 }
698
699 /* Overrides the style for column X, row Y in TABLE with STYLE.
700 Does not make a copy of STYLE, so it should either be allocated from
701 TABLE->container or have a lifetime that will outlive TABLE. */
702 void
table_add_style(struct table * table,int x,int y,const struct area_style * style)703 table_add_style (struct table *table, int x, int y,
704 const struct area_style *style)
705 {
706 get_joined_cell (table, x, y)->style = style;
707 }
708
709 /* Returns true if column C, row R has no contents, otherwise false. */
710 bool
table_cell_is_empty(const struct table * table,int c,int r)711 table_cell_is_empty (const struct table *table, int c, int r)
712 {
713 return table->cc[c + r * table_nc (table)] == NULL;
714 }
715
716 /* Editing. */
717
718 /* Writes STRING to the output. OPTIONS may be any valid combination of TAB_*
719 bits.
720
721 This function is obsolete. Please do not add new uses of it. Instead, use
722 a text_item (see output/text-item.h). */
723 void
table_output_text(int options UNUSED,const char * string)724 table_output_text (int options UNUSED, const char *string)
725 {
726 text_item_submit (text_item_create (TEXT_ITEM_LOG, string));
727 }
728
729 /* Same as table_output_text(), but FORMAT is passed through printf-like
730 formatting before output. */
731 void
table_output_text_format(int options,const char * format,...)732 table_output_text_format (int options, const char *format, ...)
733 {
734 va_list args;
735 char *text;
736
737 va_start (args, format);
738 text = xvasprintf (format, args);
739 va_end (args);
740
741 table_output_text (options, text);
742
743 free (text);
744 }
745
746 /* Initializes CELL with the contents of the table cell at column X and row Y
747 within TABLE. When CELL is no longer needed, the caller is responsible for
748 freeing it by calling table_cell_free(CELL).
749
750 The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
751 void
table_get_cell(const struct table * t,int x,int y,struct table_cell * cell)752 table_get_cell (const struct table *t, int x, int y, struct table_cell *cell)
753 {
754 assert (x >= 0 && x < t->n[TABLE_HORZ]);
755 assert (y >= 0 && y < t->n[TABLE_VERT]);
756
757 int index = x + y * table_nc (t);
758 unsigned short opt = t->ct[index];
759 const void *cc = t->cc[index];
760
761 const struct area_style *style
762 = t->styles[(opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT];
763 if (opt & TAB_JOIN)
764 {
765 const struct table_cell *jc = cc;
766 *cell = *jc;
767 if (!cell->style)
768 cell->style = style;
769 }
770 else
771 *cell = (struct table_cell) {
772 .d = { [TABLE_HORZ] = { x, x + 1 },
773 [TABLE_VERT] = { y, y + 1 } },
774 .options = opt,
775 .text = CONST_CAST (char *, cc ? cc : ""),
776 .style = style,
777 };
778
779 assert (cell->style);
780 }
781
782 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
783 representing a rule running alongside one of the cells in TABLE.
784
785 Suppose NC is the number of columns in TABLE and NR is the number of rows.
786 Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR. If (X,Y) =
787 (0,0), the return value is the rule that runs vertically on the left side of
788 cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
789 cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
790 cell (NC-1,0).
791
792 The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
793 within a 7x7 table. The '|' characters at the intersection of the X labels
794 and Y labels show the rule whose style would be returned by calling
795 table_get_rule with those X and Y values:
796
797 0 1 2 3 4 5 6 7
798 +--+--+--+--+--+--+--+
799 0 | | | | | | | |
800 +--+--+--+--+--+--+--+
801 1 | | | | | | | |
802 +--+--+--+--+--+--+--+
803 2 | | | | | | | |
804 +--+--+--+--+--+--+--+
805 3 | | | | | | | |
806 +--+--+--+--+--+--+--+
807 4 | | | | | | | |
808 +--+--+--+--+--+--+--+
809 5 | | | | | | | |
810 +--+--+--+--+--+--+--+
811 6 | | | | | | | |
812 +--+--+--+--+--+--+--+
813
814 Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR. If
815 (X,Y) = (0,0), the return value is the rule that runs horizontally above
816 the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
817 between that cell and cell (0,1); and so on, up to (0,NR), which runs
818 horizontally below cell (0,NR-1). */
819 int
table_get_rule(const struct table * table,enum table_axis axis,int x,int y,struct cell_color * color)820 table_get_rule (const struct table *table, enum table_axis axis, int x, int y,
821 struct cell_color *color)
822 {
823 assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
824 assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
825
826 uint8_t raw = (axis == TABLE_VERT
827 ? table->rh[x + table_nc (table) * y]
828 : table->rv[x + (table_nc (table) + 1) * y]);
829 struct cell_color *p = table->rule_colors[(raw & TAB_RULE_STYLE_MASK)
830 >> TAB_RULE_STYLE_SHIFT];
831 *color = p ? *p : (struct cell_color) CELL_COLOR_BLACK;
832 return (raw & TAB_RULE_TYPE_MASK) >> TAB_RULE_TYPE_SHIFT;
833 }
834