output: Expand footnote support.
authorBen Pfaff <blp@cs.stanford.edu>
Sat, 17 Nov 2018 04:42:13 +0000 (20:42 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Tue, 25 Dec 2018 19:45:37 +0000 (11:45 -0800)
This adds support for multiple footnotes per table cell and the ability
to add footnotes to titles and captions.

15 files changed:
src/output/ascii.c
src/output/cairo.c
src/output/csv.c
src/output/html.c
src/output/odt.c
src/output/render.c
src/output/render.h
src/output/tab.c
src/output/tab.h
src/output/table-item.c
src/output/table-item.h
src/output/table-provider.h
src/output/table.c
src/ui/gui/psppire-output-view.c
tests/output/render-test.c

index bb776d0c2a0b3b9ef10b5aa2296cccbfed216222..507e758e3fd727316f4f147d9162fd4f15218c02 100644 (file)
@@ -191,11 +191,11 @@ static bool ascii_open_page (struct ascii_driver *);
 static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
                              enum render_line_style styles[TABLE_N_AXES][2]);
 static void ascii_measure_cell_width (void *, const struct table_cell *,
-                                      int footnote_idx, int *min, int *max);
+                                      int *min, int *max);
 static int ascii_measure_cell_height (void *, const struct table_cell *,
-                                      int footnote_idx, int width);
+                                      int width);
 static void ascii_draw_cell (void *, const struct table_cell *,
-                             int footnote_idx, int bb[TABLE_N_AXES][2],
+                             int bb[TABLE_N_AXES][2],
                              int clip[TABLE_N_AXES][2]);
 
 static struct ascii_driver *
@@ -505,7 +505,6 @@ static char *ascii_reserve (struct ascii_driver *, int y, int x0, int x1,
                             int n);
 static void ascii_layout_cell (struct ascii_driver *,
                                const struct table_cell *,
-                               int footnote_idx,
                                int bb[TABLE_N_AXES][2],
                                int clip[TABLE_N_AXES][2],
                                int *width, int *height);
@@ -546,7 +545,7 @@ ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
 
 static void
 ascii_measure_cell_width (void *a_, const struct table_cell *cell,
-                          int footnote_idx, int *min_width, int *max_width)
+                          int *min_width, int *max_width)
 {
   struct ascii_driver *a = a_;
   int bb[TABLE_N_AXES][2];
@@ -558,22 +557,21 @@ ascii_measure_cell_width (void *a_, const struct table_cell *cell,
   bb[V][0] = 0;
   bb[V][1] = INT_MAX;
   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
-  ascii_layout_cell (a, cell, footnote_idx, bb, clip, max_width, &h);
+  ascii_layout_cell (a, cell, bb, clip, max_width, &h);
 
   if (cell->n_contents != 1
       || cell->contents[0].n_footnotes
       || strchr (cell->contents[0].text, ' '))
     {
       bb[H][1] = 1;
-      ascii_layout_cell (a, cell, footnote_idx, bb, clip, min_width, &h);
+      ascii_layout_cell (a, cell, bb, clip, min_width, &h);
     }
   else
     *min_width = *max_width;
 }
 
 static int
-ascii_measure_cell_height (void *a_, const struct table_cell *cell,
-                           int footnote_idx, int width)
+ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
 {
   struct ascii_driver *a = a_;
   int bb[TABLE_N_AXES][2];
@@ -585,18 +583,18 @@ ascii_measure_cell_height (void *a_, const struct table_cell *cell,
   bb[V][0] = 0;
   bb[V][1] = INT_MAX;
   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
-  ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
+  ascii_layout_cell (a, cell, bb, clip, &w, &h);
   return h;
 }
 
 static void
-ascii_draw_cell (void *a_, const struct table_cell *cell, int footnote_idx,
+ascii_draw_cell (void *a_, const struct table_cell *cell,
                  int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
 {
   struct ascii_driver *a = a_;
   int w, h;
 
-  ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
+  ascii_layout_cell (a, cell, bb, clip, &w, &h);
 }
 
 static char *
@@ -744,7 +742,7 @@ text_draw (struct ascii_driver *a, unsigned int options,
 
 static int
 ascii_layout_cell_text (struct ascii_driver *a,
-                        const struct cell_contents *contents, int *footnote_idx,
+                        const struct cell_contents *contents,
                         int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
                         int *widthp)
 {
@@ -766,12 +764,7 @@ ascii_layout_cell_text (struct ascii_driver *a,
       ds_extend (&s, length + contents->n_footnotes * 4);
       ds_put_cstr (&s, contents->text);
       for (i = 0; i < contents->n_footnotes; i++)
-        {
-          char marker[10];
-
-          str_format_26adic (++*footnote_idx, false, marker, sizeof marker);
-          ds_put_format (&s, "[%s]", marker);
-        }
+        ds_put_format (&s, "[%s]", contents->footnotes[i]->marker);
 
       length = ds_length (&s);
       text = ds_steal_cstr (&s);
@@ -868,7 +861,6 @@ ascii_layout_cell_text (struct ascii_driver *a,
 
 static void
 ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
-                   int footnote_idx,
                    int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
                    int *widthp, int *heightp)
 {
@@ -891,8 +883,7 @@ ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
             break;
         }
 
-      bb[V][0] = ascii_layout_cell_text (a, contents, &footnote_idx,
-                                         bb, clip, widthp);
+      bb[V][0] = ascii_layout_cell_text (a, contents, bb, clip, widthp);
     }
   *heightp = bb[V][0] - bb_[V][0];
 }
@@ -902,28 +893,28 @@ ascii_test_write (struct output_driver *driver,
                   const char *s, int x, int y, unsigned int options)
 {
   struct ascii_driver *a = ascii_driver_cast (driver);
-  struct cell_contents contents;
-  struct table_cell cell;
   int bb[TABLE_N_AXES][2];
   int width, height;
 
   if (a->file == NULL && !ascii_open_page (a))
     return;
 
-  contents.options = options | TAB_LEFT;
-  contents.text = CONST_CAST (char *, s);
-  contents.n_footnotes = 0;
+  struct cell_contents contents = {
+    .options = options | TAB_LEFT,
+    .text = CONST_CAST (char *, s),
+  };
 
-  memset (&cell, 0, sizeof cell);
-  cell.contents = &contents;
-  cell.n_contents = 1;
+  struct table_cell cell = {
+    .contents = &contents,
+    .n_contents = 1,
+  };
 
   bb[TABLE_HORZ][0] = x;
   bb[TABLE_HORZ][1] = a->width;
   bb[TABLE_VERT][0] = y;
   bb[TABLE_VERT][1] = INT_MAX;
 
-  ascii_layout_cell (a, &cell, 0, bb, bb, &width, &height);
+  ascii_layout_cell (a, &cell, bb, bb, &width, &height);
 }
 
 void
index 745c16e1eb0f4df915736d4f857dfe09e127c5e7..c0a2b5ee0ff8375e407df101c0403aff23c55312 100644 (file)
@@ -163,13 +163,13 @@ static void xr_driver_run_fsm (struct xr_driver *);
 static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
                           enum render_line_style styles[TABLE_N_AXES][2]);
 static void xr_measure_cell_width (void *, const struct table_cell *,
-                                   int footnote_idx, int *min, int *max);
+                                   int *min, int *max);
 static int xr_measure_cell_height (void *, const struct table_cell *,
-                                   int footnote_idx, int width);
-static void xr_draw_cell (void *, const struct table_cell *, int footnote_idx,
+                                   int width);
+static void xr_draw_cell (void *, const struct table_cell *,
                           int bb[TABLE_N_AXES][2],
                           int clip[TABLE_N_AXES][2]);
-static int xr_adjust_break (void *, const struct table_cell *, int footnote_idx,
+static int xr_adjust_break (void *, const struct table_cell *,
                             int width, int height);
 
 static struct xr_render_fsm *xr_render_output_item (
@@ -635,7 +635,7 @@ xr_driver_run_fsm (struct xr_driver *xr)
 }
 \f
 static void
-xr_layout_cell (struct xr_driver *, const struct table_cell *, int footnote_idx,
+xr_layout_cell (struct xr_driver *, const struct table_cell *,
                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
                 int *width, int *height, int *brk);
 
@@ -813,7 +813,7 @@ xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
 
 static void
 xr_measure_cell_width (void *xr_, const struct table_cell *cell,
-                       int footnote_idx, int *min_width, int *max_width)
+                       int *min_width, int *max_width)
 {
   struct xr_driver *xr = xr_;
   int bb[TABLE_N_AXES][2];
@@ -825,10 +825,10 @@ xr_measure_cell_width (void *xr_, const struct table_cell *cell,
   bb[V][0] = 0;
   bb[V][1] = INT_MAX;
   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
-  xr_layout_cell (xr, cell, footnote_idx, bb, clip, max_width, &h, NULL);
+  xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
 
   bb[H][1] = 1;
-  xr_layout_cell (xr, cell, footnote_idx, bb, clip, min_width, &h, NULL);
+  xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
 
   if (*min_width > 0)
     *min_width += xr->cell_margin * 2;
@@ -837,8 +837,7 @@ xr_measure_cell_width (void *xr_, const struct table_cell *cell,
 }
 
 static int
-xr_measure_cell_height (void *xr_, const struct table_cell *cell,
-                        int footnote_idx, int width)
+xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
 {
   struct xr_driver *xr = xr_;
   int bb[TABLE_N_AXES][2];
@@ -850,12 +849,12 @@ xr_measure_cell_height (void *xr_, const struct table_cell *cell,
   bb[V][0] = 0;
   bb[V][1] = INT_MAX;
   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
-  xr_layout_cell (xr, cell, footnote_idx, bb, clip, &w, &h, NULL);
+  xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL);
   return h;
 }
 
 static void
-xr_draw_cell (void *xr_, const struct table_cell *cell, int footnote_idx,
+xr_draw_cell (void *xr_, const struct table_cell *cell,
               int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
 {
   struct xr_driver *xr = xr_;
@@ -865,11 +864,11 @@ xr_draw_cell (void *xr_, const struct table_cell *cell, int footnote_idx,
   bb[H][1] -= xr->cell_margin;
   if (bb[H][0] >= bb[H][1])
     return;
-  xr_layout_cell (xr, cell, footnote_idx, bb, clip, &w, &h, &brk);
+  xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
 }
 
 static int
-xr_adjust_break (void *xr_, const struct table_cell *cell, int footnote_idx,
+xr_adjust_break (void *xr_, const struct table_cell *cell,
                  int width, int height)
 {
   struct xr_driver *xr = xr_;
@@ -877,7 +876,7 @@ xr_adjust_break (void *xr_, const struct table_cell *cell, int footnote_idx,
   int clip[TABLE_N_AXES][2];
   int w, h, brk;
 
-  if (xr_measure_cell_height (xr_, cell, footnote_idx, width) < height)
+  if (xr_measure_cell_height (xr_, cell, width) < height)
     return -1;
 
   bb[H][0] = 0;
@@ -887,7 +886,7 @@ xr_adjust_break (void *xr_, const struct table_cell *cell, int footnote_idx,
   bb[V][0] = 0;
   bb[V][1] = height;
   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
-  xr_layout_cell (xr, cell, footnote_idx, bb, clip, &w, &h, &brk);
+  xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
   return brk;
 }
 \f
@@ -915,7 +914,7 @@ add_attr_with_start (PangoAttrList *list, PangoAttribute *attr, guint start_inde
 
 static int
 xr_layout_cell_text (struct xr_driver *xr,
-                     const struct cell_contents *contents, int footnote_idx,
+                     const struct cell_contents *contents,
                      int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
                      int *widthp, int *brk)
 {
@@ -930,11 +929,10 @@ xr_layout_cell_text (struct xr_driver *xr,
   else if (contents->n_footnotes == 1 && (options & TAB_ALIGNMENT) == TAB_RIGHT)
     {
       PangoAttrList *attrs;
-      char marker[16];
 
       font = &xr->fonts[XR_FONT_MARKER];
 
-      str_format_26adic (footnote_idx + 1, false, marker, sizeof marker);
+      const char *marker = contents->footnotes[0]->marker;
       pango_layout_set_text (font->layout, marker, strlen (marker));
 
       attrs = pango_attr_list_new ();
@@ -971,22 +969,13 @@ xr_layout_cell_text (struct xr_driver *xr,
     {
       PangoAttrList *attrs;
       struct string s;
-      size_t i;
 
       bb[H][1] += xr->cell_margin;
 
       ds_init_empty (&s);
       ds_extend (&s, length + contents->n_footnotes * 10);
       ds_put_cstr (&s, contents->text);
-      for (i = 0; i < contents->n_footnotes; i++)
-        {
-          char marker[16];
-
-          if (i > 0)
-            ds_put_byte (&s, ',');
-          str_format_26adic (footnote_idx + i + 1, false, marker, sizeof marker);
-          ds_put_cstr (&s, marker);
-        }
+      cell_contents_format_footnote_markers (contents, &s);
       pango_layout_set_text (font->layout, ds_cstr (&s), ds_length (&s));
       ds_destroy (&s);
 
@@ -1105,7 +1094,6 @@ xr_layout_cell_text (struct xr_driver *xr,
 
 static void
 xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
-                int footnote_idx,
                 int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
                 int *width, int *height, int *brk)
 {
@@ -1151,9 +1139,7 @@ xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
             *brk = bb[V][0];
         }
 
-      bb[V][0] = xr_layout_cell_text (xr, contents, footnote_idx, bb, clip,
-                                      width, brk);
-      footnote_idx += contents->n_footnotes;
+      bb[V][0] = xr_layout_cell_text (xr, contents, bb, clip, width, brk);
     }
   *height = bb[V][0] - bb_[V][0];
 }
index ef49759566d9b27829a2d8011d271c149e9ef6f9..763d19137219c0aaa38a77ad0e41879e818a646d 100644 (file)
@@ -33,6 +33,7 @@
 #include "output/table-item.h"
 #include "output/table-provider.h"
 
+#include "gl/minmax.h"
 #include "gl/xalloc.h"
 #include "gl/xvasprintf.h"
 
@@ -163,6 +164,29 @@ csv_output_field_format (struct csv_driver *csv, const char *format, ...)
   free (s);
 }
 
+static void
+csv_format_footnotes (const struct footnote **f, size_t n, struct string *s)
+{
+  for (size_t i = 0; i < n; i++)
+    ds_put_format (s, "[%s]", f[i]->marker);
+}
+
+static void
+csv_output_table_item_text (struct csv_driver *csv,
+                            const struct table_item_text *text,
+                            const char *leader)
+{
+  if (!text)
+    return;
+
+  struct string s = DS_EMPTY_INITIALIZER;
+  ds_put_format (&s, "%s: %s", leader, text->content);
+  csv_format_footnotes (text->footnotes, text->n_footnotes, &s);
+  csv_output_field (csv, ds_cstr (&s));
+  ds_destroy (&s);
+  putc ('\n', csv->file);
+}
+
 static void
 csv_put_separator (struct csv_driver *csv)
 {
@@ -179,21 +203,15 @@ csv_submit (struct output_driver *driver,
   if (is_table_item (output_item))
     {
       struct table_item *table_item = to_table_item (output_item);
-      const char *title = table_item_get_title (table_item);
-      const char *caption = table_item_get_caption (table_item);
       const struct table *t = table_item_get_table (table_item);
-      int footnote_idx;
       int x, y;
 
       csv_put_separator (csv);
 
-      if (csv->titles && title != NULL)
-        {
-          csv_output_field_format (csv, "Table: %s", title);
-          putc ('\n', csv->file);
-        }
+      if (csv->titles)
+        csv_output_table_item_text (csv, table_item_get_title (table_item),
+                                    "Table");
 
-      footnote_idx = 0;
       for (y = 0; y < table_nr (t); y++)
         {
           for (x = 0; x < table_nc (t); x++)
@@ -220,21 +238,12 @@ csv_submit (struct output_driver *driver,
                   for (i = 0; i < cell.n_contents; i++)
                     {
                       const struct cell_contents *c = &cell.contents[i];
-                      int j;
 
                       if (i > 0)
                         ds_put_cstr (&s, "\n\n");
 
                       ds_put_cstr (&s, c->text);
-
-                      for (j = 0; j < c->n_footnotes; j++)
-                        {
-                          char marker[16];
-
-                          str_format_26adic (++footnote_idx, false,
-                                             marker, sizeof marker);
-                          ds_put_format (&s, "[%s]", marker);
-                        }
+                      csv_format_footnotes (c->footnotes, c->n_footnotes, &s);
                     }
                   csv_output_field (csv, ds_cstr (&s));
                   ds_destroy (&s);
@@ -245,47 +254,26 @@ csv_submit (struct output_driver *driver,
           putc ('\n', csv->file);
         }
 
-      if (csv->captions && caption != NULL)
-        {
-          csv_output_field_format (csv, "Caption: %s", caption);
-          putc ('\n', csv->file);
-        }
+      if (csv->captions)
+        csv_output_table_item_text (csv, table_item_get_caption (table_item),
+                                    "Caption");
 
-      if (footnote_idx)
+      const struct footnote **f;
+      size_t n_footnotes = table_collect_footnotes (table_item, &f);
+      if (n_footnotes)
         {
-          size_t i;
-
           fputs ("\nFootnotes:\n", csv->file);
 
-          footnote_idx = 0;
-          for (y = 0; y < table_nr (t); y++)
-            {
-              struct table_cell cell;
-              for (x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
-                {
-                  table_get_cell (t, x, y, &cell);
-
-                  if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
-                    for (i = 0; i < cell.n_contents; i++)
-                      {
-                        const struct cell_contents *c = &cell.contents[i];
-                        int j;
-
-                        for (j = 0; j < c->n_footnotes; j++)
-                          {
-                            char marker[16];
-
-                            str_format_26adic (++footnote_idx, false,
-                                               marker, sizeof marker);
-                            csv_output_field (csv, marker);
-                            fputs (csv->separator, csv->file);
-                            csv_output_field (csv, c->footnotes[j]);
-                            putc ('\n', csv->file);
-                          }
-                      }
-                  table_cell_free (&cell);
-                }
-            }
+          for (size_t i = 0; i < n_footnotes; i++)
+            if (f[i])
+              {
+                csv_output_field (csv, f[i]->marker);
+                fputs (csv->separator, csv->file);
+                csv_output_field (csv, f[i]->content);
+                putc ('\n', csv->file);
+              }
+
+          free (f);
         }
     }
   else if (is_text_item (output_item))
index e79cd8afea34180dfa279dc2ff6adfc0f98aa38f..55bcb6daa09d95064e87a6d600c3e83047f8d644 100644 (file)
@@ -382,67 +382,76 @@ put_tfoot (struct html_driver *html, const struct table *t, bool *tfoot)
     fputs ("\n<BR>", html->file);
 }
 
+static void
+html_put_footnote_markers (struct html_driver *html,
+                           const struct footnote **footnotes,
+                           size_t n_footnotes)
+{
+  if (n_footnotes > 0)
+    {
+      fputs ("<SUP>", html->file);
+      for (size_t i = 0; i < n_footnotes; i++)
+        {
+          const struct footnote *f = footnotes[i];
+
+          if (i > 0)
+            putc (',', html->file);
+          escape_string (html->file, f->marker,
+                         strlen (f->marker), " ", "<BR>");
+        }
+      fputs ("</SUP>", html->file);
+    }
+}
+
+static void
+html_put_table_item_text (struct html_driver *html,
+                          const struct table_item_text *text)
+{
+  escape_string (html->file, text->content, strlen (text->content),
+                 " ", "<BR>");
+  html_put_footnote_markers (html, text->footnotes, text->n_footnotes);
+}
+
 static void
 html_output_table (struct html_driver *html, const struct table_item *item)
 {
   const struct table *t = table_item_get_table (item);
-  const char *title = table_item_get_title (item);
-  const char *caption = table_item_get_caption (item);
-  int footnote_idx = 0;
   bool tfoot = false;
   int y;
 
   fputs ("<TABLE>", html->file);
 
+  const struct table_item_text *caption = table_item_get_caption (item);
   if (caption)
     {
       put_tfoot (html, t, &tfoot);
-      escape_string (html->file, caption, strlen (caption), " ", "<BR>");
-    }
-  footnote_idx = 0;
-  for (y = 0; y < table_nr (t); y++)
-    {
-      int x;
-      for (x = 0; x < table_nc (t); )
-        {
-          const struct cell_contents *c;
-          struct table_cell cell;
-
-          table_get_cell (t, x, y, &cell);
-          if (y != cell.d[TABLE_VERT][0])
-            goto next_0;
-
-          for (c = cell.contents; c < &cell.contents[cell.n_contents]; c++)
-            {
-              int i;
-
-              for (i = 0; i < c->n_footnotes; i++)
-                {
-                  char marker[16];
-
-                  put_tfoot (html, t, &tfoot);
-                  str_format_26adic (++footnote_idx, false, marker, sizeof marker);
-                  fprintf (html->file, "<SUP>%s</SUP> ", marker);
-                  escape_string (html->file, c->footnotes[i],
-                                 strlen (c->footnotes[i]), " ", "<BR>");
-                }
-            }
-
-       next_0:
-          x = cell.d[TABLE_HORZ][1];
-          table_cell_free (&cell);
-        }
+      html_put_table_item_text (html, caption);
     }
+  const struct footnote **f;
+  size_t n_footnotes = table_collect_footnotes (item, &f);
+
+  for (size_t i = 0; i < n_footnotes; i++)
+    if (f[i])
+      {
+        put_tfoot (html, t, &tfoot);
+        fputs ("<SUP>", html->file);
+        escape_string (html->file, f[i]->marker, strlen (f[i]->marker),
+                       " ", "<BR>");
+        fputs ("</SUP> ", html->file);
+        escape_string (html->file, f[i]->content, strlen (f[i]->content),
+                       " ", "<BR>");
+      }
+  free (f);
   if (tfoot)
     fputs ("</TD></TR></TFOOT>\n", html->file);
-  footnote_idx = 0;
 
   fputs ("<TBODY VALIGN=\"TOP\">\n", html->file);
 
-  if (title != NULL)
+  const struct table_item_text *title = table_item_get_title (item);
+  if (title)
     {
       fputs ("  <CAPTION>", html->file);
-      escape_string (html->file, title, strlen (title), " ", "<BR>");
+      html_put_table_item_text (html, title);
       fputs ("</CAPTION>\n", html->file);
     }
 
@@ -524,7 +533,6 @@ html_output_table (struct html_driver *html, const struct table_item *item)
           for (c = cell.contents; c < &cell.contents[cell.n_contents]; c++)
             {
               const char *s = c->text;
-              int i;
 
               if (c->options & TAB_EMPH)
                 fputs ("<EM>", html->file);
@@ -542,21 +550,7 @@ html_output_table (struct html_driver *html, const struct table_item *item)
               if (c->options & TAB_EMPH)
                 fputs ("</EM>", html->file);
 
-              if (c->n_footnotes > 0)
-                {
-                  fputs ("<SUP>", html->file);
-                  for (i = 0; i < c->n_footnotes; i++)
-                    {
-                      char marker[16];
-
-                      if (i > 0)
-                        putc (',', html->file);
-                      str_format_26adic (++footnote_idx, false,
-                                         marker, sizeof marker);
-                      fputs (marker, html->file);
-                    }
-                  fputs ("</SUP>", html->file);
-                }
+              html_put_footnote_markers (html, c->footnotes, c->n_footnotes);
             }
 
           /* Output </TH> or </TD>. */
index 1d11594a152aeadcbace51f18855b1da02cdb373..153cd393c116f357f615e3d113ea9e336d8292e5 100644 (file)
@@ -69,9 +69,6 @@ struct odt_driver
 
   /* Number of tables so far. */
   int table_num;
-
-  /* Number of footnotes so far. */
-  int n_footnotes;
 };
 
 static const struct output_driver_class odt_driver_class;
@@ -422,30 +419,43 @@ write_xml_with_line_breaks (struct odt_driver *odt, const char *line_)
 }
 
 static void
-write_footnote (struct odt_driver *odt, const char *footnote)
+write_footnote (struct odt_driver *odt, const struct footnote *f)
 {
-  char marker[16];
-
   xmlTextWriterStartElement (odt->content_wtr, _xml("text:note"));
   xmlTextWriterWriteAttribute (odt->content_wtr, _xml("text:note-class"),
                                _xml("footnote"));
 
   xmlTextWriterStartElement (odt->content_wtr, _xml("text:note-citation"));
-  str_format_26adic (++odt->n_footnotes, false, marker, sizeof marker);
-  if (strlen (marker) > 1)
+  if (strlen (f->marker) > 1)
     xmlTextWriterWriteFormatAttribute (odt->content_wtr, _xml("text:label"),
-                                       "(%s)", marker);
+                                       "(%s)", f->marker);
   else
     xmlTextWriterWriteAttribute (odt->content_wtr, _xml("text:label"),
-                                 _xml(marker));
+                                 _xml(f->marker));
   xmlTextWriterEndElement (odt->content_wtr);
 
   xmlTextWriterStartElement (odt->content_wtr, _xml("text:note-body"));
   xmlTextWriterStartElement (odt->content_wtr, _xml("text:p"));
-  write_xml_with_line_breaks (odt, footnote);
+  write_xml_with_line_breaks (odt, f->content);
+  xmlTextWriterEndElement (odt->content_wtr);
   xmlTextWriterEndElement (odt->content_wtr);
+
   xmlTextWriterEndElement (odt->content_wtr);
+}
 
+static void
+write_table_item_text (struct odt_driver *odt,
+                       const struct table_item_text *text)
+{
+  if (!text)
+    return;
+
+  xmlTextWriterStartElement (odt->content_wtr, _xml("text:h"));
+  xmlTextWriterWriteFormatAttribute (odt->content_wtr,
+                                     _xml("text:outline-level"), "%d", 2);
+  xmlTextWriterWriteString (odt->content_wtr, _xml (text->content) );
+  for (size_t i = 0; i < text->n_footnotes; i++)
+    write_footnote (odt, text->footnotes[i]);
   xmlTextWriterEndElement (odt->content_wtr);
 }
 
@@ -453,20 +463,10 @@ static void
 write_table (struct odt_driver *odt, const struct table_item *item)
 {
   const struct table *tab = table_item_get_table (item);
-  const char *caption = table_item_get_caption (item);
-  const char *title = table_item_get_title (item);
   int r, c;
 
   /* Write a heading for the table */
-  if (title != NULL)
-    {
-      xmlTextWriterStartElement (odt->content_wtr, _xml("text:h"));
-      xmlTextWriterWriteFormatAttribute (odt->content_wtr,
-                                         _xml("text:outline-level"), "%d", 2);
-      xmlTextWriterWriteString (odt->content_wtr,
-                                _xml (table_item_get_title (item)) );
-      xmlTextWriterEndElement (odt->content_wtr);
-    }
+  write_table_item_text (odt, table_item_get_title (item));
 
   /* Start table */
   xmlTextWriterStartElement (odt->content_wtr, _xml("table:table"));
@@ -556,16 +556,7 @@ write_table (struct odt_driver *odt, const struct table_item *item)
   xmlTextWriterEndElement (odt->content_wtr); /* table */
 
   /* Write a caption for the table */
-  if (caption != NULL)
-    {
-      xmlTextWriterStartElement (odt->content_wtr, _xml("text:h"));
-      xmlTextWriterWriteFormatAttribute (odt->content_wtr,
-                                         _xml("text:outline-level"), "%d", 2);
-      xmlTextWriterWriteString (odt->content_wtr,
-                                _xml (table_item_get_caption (item)) );
-      xmlTextWriterEndElement (odt->content_wtr);
-    }
-
+  write_table_item_text (odt, table_item_get_caption (item));
 }
 
 static void
index 6c7f3bca51e907239bd544db6a8897037190aac1..84ef8c1d352597097ae0f5ecbae65f3404ac6f85 100644 (file)
@@ -94,15 +94,6 @@ struct render_page
        entire page can overflow on all four sides!) */
     struct hmap overflows;
 
-    /* Contains "struct render_footnote"s, one for each cell with one or more
-       footnotes.
-
-       'n_footnotes' is the number of footnotes in the table.  There might be
-       more than hmap_count(&page->footnotes) because there can be more than
-       one footnote in a cell. */
-    struct hmap footnotes;
-    size_t n_footnotes;
-
     /* If a single column (or row) is too wide (or tall) to fit on a page
        reasonably, then render_break_next() will split a single row or column
        across multiple render_pages.  This member indicates when this has
@@ -339,55 +330,6 @@ find_overflow (const struct render_page *page, int x, int y)
   return NULL;
 }
 \f
-/* A footnote. */
-struct render_footnote
-  {
-    struct hmap_node node;
-
-    /* The area of the table covered by the cell that has the footnote.
-
-       d[H][0] is the leftmost column.
-       d[H][1] is the rightmost column, plus 1.
-       d[V][0] is the top row.
-       d[V][1] is the bottom row, plus 1.
-
-       The cell in its original table might occupy a larger region.  This
-       member reflects the size of the cell in the current render_page, after
-       trimming off any rows or columns due to page-breaking. */
-    int d[TABLE_N_AXES][2];
-
-    /* The index of the first footnote in the cell. */
-    int idx;
-  };
-
-static int
-count_footnotes (const struct table_cell *cell)
-{
-  size_t i;
-  int n;
-
-  n = 0;
-  for (i = 0; i < cell->n_contents; i++)
-    n += cell->contents[i].n_footnotes;
-  return n;
-}
-
-static int
-find_footnote_idx (const struct table_cell *cell, const struct hmap *footnotes)
-{
-  const struct render_footnote *f;
-
-  if (!count_footnotes (cell))
-    return 0;
-
-  HMAP_FOR_EACH_WITH_HASH (f, struct render_footnote, node,
-                           hash_cell (cell->d[H][0], cell->d[V][0]), footnotes)
-    if (f->d[H][0] == cell->d[H][0] && f->d[V][0] == cell->d[V][0])
-      return f->idx;
-
-  NOT_REACHED ();
-}
-\f
 /* Row or column dimensions.  Used to figure the size of a table in
    render_page_create() and discarded after that. */
 struct render_row
@@ -592,8 +534,6 @@ render_page_allocate (const struct render_params *params,
     }
 
   hmap_init (&page->overflows);
-  hmap_init (&page->footnotes);
-  page->n_footnotes = 0;
   memset (page->is_edge_cutoff, 0, sizeof page->is_edge_cutoff);
 
   return page;
@@ -683,8 +623,6 @@ render_page_create (const struct render_params *params, struct table *table)
   struct render_row *rows;
   int table_widths[2];
   int *rules[TABLE_N_AXES];
-  struct hmap footnotes;
-  int footnote_idx;
   int nr, nc;
   int x, y;
   int i;
@@ -705,9 +643,7 @@ render_page_create (const struct render_params *params, struct table *table)
     }
 
   /* Calculate minimum and maximum widths of cells that do not
-     span multiple columns.  Assign footnote markers. */
-  hmap_init (&footnotes);
-  footnote_idx = 0;
+     span multiple columns. */
   for (i = 0; i < 2; i++)
     columns[i] = xzalloc (nc * sizeof *columns[i]);
   for (y = 0; y < nr; y++)
@@ -718,33 +654,17 @@ render_page_create (const struct render_params *params, struct table *table)
         table_get_cell (table, x, y, &cell);
         if (y == cell.d[V][0])
           {
-            int n;
-
             if (table_cell_colspan (&cell) == 1)
               {
                 int w[2];
                 int i;
 
-                params->measure_cell_width (params->aux, &cell, footnote_idx,
+                params->measure_cell_width (params->aux, &cell,
                                             &w[MIN], &w[MAX]);
                 for (i = 0; i < 2; i++)
                   if (columns[i][x].unspanned < w[i])
                     columns[i][x].unspanned = w[i];
               }
-
-            n = count_footnotes (&cell);
-            if (n > 0)
-              {
-                struct render_footnote *f = xmalloc (sizeof *f);
-                f->d[H][0] = cell.d[H][0];
-                f->d[H][1] = cell.d[H][1];
-                f->d[V][0] = cell.d[V][0];
-                f->d[V][1] = cell.d[V][1];
-                f->idx = footnote_idx;
-                hmap_insert (&footnotes, &f->node, hash_cell (x, y));
-
-                footnote_idx += n;
-              }
           }
         x = cell.d[H][1];
         table_cell_free (&cell);
@@ -764,9 +684,7 @@ render_page_create (const struct render_params *params, struct table *table)
           {
             int w[2];
 
-            params->measure_cell_width (params->aux, &cell,
-                                        find_footnote_idx (&cell, &footnotes),
-                                        &w[MIN], &w[MAX]);
+            params->measure_cell_width (params->aux, &cell, &w[MIN], &w[MAX]);
             for (i = 0; i < 2; i++)
               distribute_spanned_width (w[i], &columns[i][cell.d[H][0]],
                                         rules[H], table_cell_colspan (&cell));
@@ -822,8 +740,7 @@ render_page_create (const struct render_params *params, struct table *table)
               if (table_cell_rowspan (&cell) == 1)
                 {
                   int w = joined_width (page, H, cell.d[H][0], cell.d[H][1]);
-                  int h = params->measure_cell_height (
-                    params->aux, &cell, find_footnote_idx (&cell, &footnotes), w);
+                  int h = params->measure_cell_height (params->aux, &cell, w);
                   if (h > r->unspanned)
                     r->unspanned = r->width = h;
                 }
@@ -850,8 +767,7 @@ render_page_create (const struct render_params *params, struct table *table)
         if (y == cell.d[V][0] && table_cell_rowspan (&cell) > 1)
           {
             int w = joined_width (page, H, cell.d[H][0], cell.d[H][1]);
-            int h = params->measure_cell_height (
-              params->aux, &cell, find_footnote_idx (&cell, &footnotes), w);
+            int h = params->measure_cell_height (params->aux, &cell, w);
             distribute_spanned_width (h, &rows[cell.d[V][0]], rules[V],
                                       table_cell_rowspan (&cell));
           }
@@ -876,10 +792,6 @@ render_page_create (const struct render_params *params, struct table *table)
         }
     }
 
-  hmap_swap (&page->footnotes, &footnotes);
-  hmap_destroy (&footnotes);
-  page->n_footnotes = footnote_idx;
-
   free (rules[H]);
   free (rules[V]);
 
@@ -1077,8 +989,7 @@ render_cell (const struct render_page *page, const int ofs[TABLE_N_AXES],
         }
     }
 
-  page->params->draw_cell (page->params->aux, cell,
-                           find_footnote_idx (cell, &page->footnotes), bb, clip);
+  page->params->draw_cell (page->params->aux, cell, bb, clip);
 }
 
 /* Draws the cells of PAGE indicated in BB. */
@@ -1338,8 +1249,7 @@ render_break_next (struct render_break *b, int size)
                       table_get_cell (page->table, x, z, &cell);
                       w = joined_width (page, H, cell.d[H][0], cell.d[H][1]);
                       better_pixel = page->params->adjust_break (
-                        page->params->aux, &cell,
-                        find_footnote_idx (&cell, &page->footnotes), w, pixel);
+                        page->params->aux, &cell, w, pixel);
                       x = cell.d[H][1];
                       table_cell_free (&cell);
 
@@ -1464,49 +1374,38 @@ render_pager_start_page (struct render_pager *p)
 }
 
 static void
-add_footnote_page (struct render_pager *p, const struct render_page *body)
+add_footnote_page (struct render_pager *p, const struct table_item *item)
 {
-  const struct table *table = body->table;
-  int nc = table_nc (table);
-  int nr = table_nr (table);
-  int footnote_idx = 0;
-  struct tab_table *t;
-  int x, y;
-
-  if (!body->n_footnotes)
+  const struct footnote **f;
+  size_t n_footnotes = table_collect_footnotes (item, &f);
+  if (!n_footnotes)
     return;
 
-  t = tab_create (2, body->n_footnotes);
-  for (y = 0; y < nr; y++)
-    for (x = 0; x < nc; )
-      {
-        struct table_cell cell;
+  struct tab_table *t = tab_create (2, n_footnotes);
 
-        table_get_cell (table, x, y, &cell);
-        if (y == cell.d[V][0])
-          {
-            size_t i;
-
-            for (i = 0; i < cell.n_contents; i++)
-              {
-                const struct cell_contents *cc = &cell.contents[i];
-                size_t j;
-
-                for (j = 0; j < cc->n_footnotes; j++)
-                  {
-                    const char *f = cc->footnotes[j];
-
-                    tab_text (t, 0, footnote_idx, TAB_LEFT, "");
-                    tab_footnote (t, 0, footnote_idx, "(none)");
-                    tab_text (t, 1, footnote_idx, TAB_LEFT, f);
-                    footnote_idx++;
-                  }
-              }
-          }
-        x = cell.d[H][1];
-        table_cell_free (&cell);
+  for (size_t i = 0; i < n_footnotes; i++)
+    if (f[i])
+      {
+        tab_text (t, 0, i, TAB_LEFT, "");
+        tab_add_footnote (t, 0, i, f[i]);
+        tab_text (t, 1, i, TAB_LEFT, f[i]->content);
       }
   render_pager_add_table (p, &t->table);
+
+  free (f);
+}
+
+static void
+add_text_page (struct render_pager *p, const struct table_item_text *t)
+{
+  if (!t)
+    return;
+
+  struct tab_table *tab = tab_create (1, 1);
+  tab_text (tab, 0, 0, TAB_LEFT, t->content);
+  for (size_t i = 0; i < t->n_footnotes; i++)
+    tab_add_footnote (tab, 0, 0, t->footnotes[i]);
+  render_pager_add_table (p, &tab->table);
 }
 
 /* Creates and returns a new render_pager for rendering TABLE_ITEM on the
@@ -1515,28 +1414,22 @@ struct render_pager *
 render_pager_create (const struct render_params *params,
                      const struct table_item *table_item)
 {
-  const char *caption = table_item_get_caption (table_item);
-  const char *title = table_item_get_title (table_item);
-  const struct render_page *body_page;
   struct render_pager *p;
 
   p = xzalloc (sizeof *p);
   p->params = params;
 
   /* Title. */
-  if (title)
-    render_pager_add_table (p, table_from_string (TAB_LEFT, title));
+  add_text_page (p, table_item_get_title (table_item));
 
   /* Body. */
-  body_page = render_pager_add_table (p, table_ref (table_item_get_table (
-                                                      table_item)));
+  render_pager_add_table (p, table_ref (table_item_get_table (table_item)));
 
   /* Caption. */
-  if (caption)
-    render_pager_add_table (p, table_from_string (TAB_LEFT, caption));
+  add_text_page (p, table_item_get_caption (table_item));
 
   /* Footnotes. */
-  add_footnote_page (p, body_page);
+  add_footnote_page (p, table_item);
 
   render_pager_start_page (p);
 
@@ -1727,7 +1620,6 @@ static struct render_page *
 render_page_select (const struct render_page *page, enum table_axis axis,
                     int z0, int p0, int z1, int p1)
 {
-  const struct render_footnote *f;
   struct render_page_selection s;
   enum table_axis a = axis;
   enum table_axis b = !a;
@@ -1894,21 +1786,6 @@ render_page_select (const struct render_page *page, enum table_axis axis,
       table_cell_free (&cell);
     }
 
-  /* Copy footnotes from PAGE into subpage. */
-  HMAP_FOR_EACH (f, struct render_footnote, node, &page->footnotes)
-    if ((f->d[a][0] >= z0 && f->d[a][0] < z1)
-        || (f->d[a][1] - 1 >= z0 && f->d[a][1] - 1 < z1))
-      {
-        struct render_footnote *nf = xmalloc (sizeof *nf);
-        nf->d[a][0] = MAX (z0, f->d[a][0]) - z0 + page->h[a][0];
-        nf->d[a][1] = MIN (z1, f->d[a][1]) - z0 + page->h[a][0];
-        nf->d[b][0] = f->d[b][0];
-        nf->d[b][1] = f->d[b][1];
-        nf->idx = f->idx;
-        hmap_insert (&subpage->footnotes, &nf->node,
-                     hash_cell (nf->d[H][0], nf->d[V][0]));
-      }
-
   return subpage;
 }
 
index d1f085fb2c3b5232d60c780b001973a7549dcfd4..365c8036d43ae260faf36282b09cc9e720ce6006 100644 (file)
@@ -51,14 +51,6 @@ enum render_line_style
 
    For each of the callback functions, AUX is passed as the 'aux' member of the
    render_params structure.
-
-   The device is expected to transform numerical footnote index numbers into
-   footnote markers.  The existing drivers use str_format_26adic() to transform
-   index 0 to "a", index 1 to "b", and so on.  The FOOTNOTE_IDX supplied to
-   each function is the footnote index number for the first footnote in the
-   cell.  If a cell contains more than one footnote, then the additional
-   footnote indexes increase sequentially, e.g. the second footnote has index
-   FOOTNOTE_IDX + 1.
 */
 struct render_params
   {
@@ -68,12 +60,11 @@ struct render_params
        minimum width required to avoid line breaks other than at new-lines.
        */
     void (*measure_cell_width) (void *aux, const struct table_cell *cell,
-                                int footnote_idx,
                                 int *min_width, int *max_width);
 
     /* Returns the height required to render CELL given a width of WIDTH. */
     int (*measure_cell_height) (void *aux, const struct table_cell *cell,
-                                int footnote_idx, int width);
+                                int width);
 
     /* Given that there is space measuring WIDTH by HEIGHT to render CELL,
        where HEIGHT is insufficient to render the entire height of the cell,
@@ -86,7 +77,7 @@ struct render_params
        Optional.  If NULL, the rendering engine assumes that all breakpoints
        are acceptable. */
     int (*adjust_break) (void *aux, const struct table_cell *cell,
-                         int footnote_idx, int width, int height);
+                         int width, int height);
 
     /* Draws a generalized intersection of lines in the rectangle whose
        top-left corner is (BB[TABLE_HORZ][0], BB[TABLE_VERT][0]) and whose
@@ -106,7 +97,6 @@ struct render_params
        of the cell that lies within CLIP should actually be drawn, although BB
        should used to determine the layout of the cell. */
     void (*draw_cell) (void *aux, const struct table_cell *cell,
-                       int footnote_idx,
                        int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2]);
 
     /* Auxiliary data passed to each of the above functions. */
index af43d6c2bcdb59a3e2a7d4b27c391742ec3ac8eb..dcd6fe29a2cc5590457dd937d67e73df84062892 100644 (file)
@@ -66,7 +66,7 @@ struct tab_joined_cell
     u;
 
   size_t n_footnotes;
-  char **footnotes;
+  const struct footnote **footnotes;
 };
 
 static const struct table_class tab_table_class;
@@ -593,7 +593,10 @@ tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
                 unsigned opt, const char *text)
 {
   char *s = pool_strdup (table->container, text);
-  add_joined_cell (table, x1, y1, x2, y2, opt)->u.text = s;
+  if (x1 == x2 && y1 == y2)
+    do_tab_text (table, x1, y1, opt, s);
+  else
+    add_joined_cell (table, x1, y1, x2, y2, opt)->u.text = s;
 }
 
 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
@@ -613,13 +616,24 @@ tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2,
   add_joined_cell (table, x1, y1, x2, y2, opt)->u.text = s;
 }
 
+struct footnote *
+tab_create_footnote (struct tab_table *table, size_t idx, const char *content,
+                     const char *marker)
+{
+  struct footnote *f = pool_alloc (table->container, sizeof *f);
+  f->idx = idx;
+  f->content = pool_strdup (table->container, content);
+  f->marker = pool_strdup (table->container, marker);
+  return f;
+}
+
 void
-tab_footnote (struct tab_table *table, int x, int y, const char *format, ...)
+tab_add_footnote (struct tab_table *table, int x, int y,
+                  const struct footnote *f)
 {
   int index = x + y * table->cf;
   unsigned char opt = table->ct[index];
   struct tab_joined_cell *j;
-  va_list args;
 
   if (opt & TAB_JOIN)
     j = table->cc[index];
@@ -631,13 +645,10 @@ tab_footnote (struct tab_table *table, int x, int y, const char *format, ...)
       j->u.text = text ? text : xstrdup ("");
     }
 
-  j->footnotes = xrealloc (j->footnotes,
-                           (j->n_footnotes + 1) * sizeof *j->footnotes);
+  j->footnotes = pool_realloc (table->container, j->footnotes,
+                               (j->n_footnotes + 1) * sizeof *j->footnotes);
 
-  va_start (args, format);
-  j->footnotes[j->n_footnotes++] =
-    pool_vasprintf (table->container, format, args);
-  va_end (args);
+  j->footnotes[j->n_footnotes++] = f;
 }
 
 bool
index 3109e39439d165f0c94a4dd3e7cf8117d5f86b27..00d8fab93cacc2cee3fe97db959cb092796c6d05 100644 (file)
@@ -143,8 +143,10 @@ void tab_joint_text_format (struct tab_table *, int x1, int y1, int x2, int y2,
                             unsigned opt, const char *, ...)
      PRINTF_FORMAT (7, 8);
 
-void tab_footnote (struct tab_table *, int x, int y, const char *format, ...)
-  PRINTF_FORMAT (4, 5);
+struct footnote *tab_create_footnote (struct tab_table *, size_t idx,
+                                      const char *content, const char *marker);
+void tab_add_footnote (struct tab_table *, int x, int y,
+                       const struct footnote *);
 
 bool tab_cell_is_empty (const struct tab_table *, int c, int r);
 
index c9fd0a62a75f8c275de8b46d13ad68389419b5e1..645d8ca45f71c8387e675c6f518dce71937d8cd7 100644 (file)
 
 #include "gl/xalloc.h"
 
+struct table_item_text *
+table_item_text_create (const char *content)
+{
+  if (!content)
+    return NULL;
+
+  struct table_item_text *text = xmalloc (sizeof *text);
+  *text = (struct table_item_text) { .content = xstrdup (content) };
+  return text;
+}
+
+struct table_item_text *
+table_item_text_clone (const struct table_item_text *old)
+{
+  if (!old)
+    return NULL;
+
+  struct table_item_text *new = xmalloc (sizeof *new);
+  *new = (struct table_item_text) {
+    .content = xstrdup (old->content),
+    .footnotes = xmemdup (old->footnotes,
+                          old->n_footnotes * sizeof *old->footnotes),
+    .n_footnotes = old->n_footnotes,
+  };
+  return new;
+}
+
+void
+table_item_text_destroy (struct table_item_text *text)
+{
+  if (text)
+    {
+      free (text->content);
+      free (text->footnotes);
+      free (text);
+    }
+}
+
 /* Initializes ITEM as a table item for rendering TABLE.  The new table item
    initially has the specified TITLE and CAPTION, which may each be NULL.  The
    caller retains ownership of TITLE and CAPTION. */
@@ -37,8 +75,8 @@ table_item_create (struct table *table, const char *title, const char *caption)
   struct table_item *item = xmalloc (sizeof *item);
   output_item_init (&item->output_item, &table_item_class);
   item->table = table;
-  item->title = title != NULL ? xstrdup (title) : NULL;
-  item->caption = caption != NULL ? xstrdup (caption) : NULL;
+  item->title = table_item_text_create (title);
+  item->caption = table_item_text_create (caption);
   return item;
 }
 
@@ -52,7 +90,7 @@ table_item_get_table (const struct table_item *table_item)
 
 /* Returns ITEM's title, which is a null pointer if no title has been
    set. */
-const char *
+const struct table_item_text *
 table_item_get_title (const struct table_item *item)
 {
   return item->title;
@@ -63,16 +101,17 @@ table_item_get_title (const struct table_item *item)
 
    This function may only be used on a table_item that is unshared. */
 void
-table_item_set_title (struct table_item *item, const char *title)
+table_item_set_title (struct table_item *item,
+                      const struct table_item_text *title)
 {
   assert (!table_item_is_shared (item));
-  free (item->title);
-  item->title = title != NULL ? xstrdup (title) : NULL;
+  table_item_text_destroy (item->title);
+  item->title = table_item_text_clone (title);
 }
 
 /* Returns ITEM's caption, which is a null pointer if no caption has been
    set. */
-const char *
+const struct table_item_text *
 table_item_get_caption (const struct table_item *item)
 {
   return item->caption;
@@ -84,11 +123,12 @@ table_item_get_caption (const struct table_item *item)
 
    This function may only be used on a table_item that is unshared. */
 void
-table_item_set_caption (struct table_item *item, const char *caption)
+table_item_set_caption (struct table_item *item,
+                        const struct table_item_text *caption)
 {
   assert (!table_item_is_shared (item));
-  free (item->caption);
-  item->caption = caption != NULL ? xstrdup (caption) : NULL;
+  table_item_text_destroy (item->caption);
+  item->caption = table_item_text_clone (caption);
 }
 
 /* Submits TABLE_ITEM to the configured output drivers, and transfers ownership
index 855324416fa066cd73d6c03896567071e4279d7a..4e070ddabf778ef001eadb22b412971842efd044 100644 (file)
 #include "libpspp/compiler.h"
 #include "output/output-item.h"
 
+/* Title or caption in a table item. */
+struct table_item_text
+  {
+    char *content;
+    const struct footnote **footnotes;
+    size_t n_footnotes;
+  };
+
+struct table_item_text *table_item_text_create (const char *);
+struct table_item_text *table_item_text_clone (const struct table_item_text *);
+void table_item_text_destroy (struct table_item_text *);
+
 /* A table item.
 
    The members of struct table_item should not be accessed directly.  Use one
    of the accessor functions defined below. */
 struct table_item
   {
-    struct output_item output_item; /* Superclass. */
-    struct table *table;        /* The table to be rendered. */
-    char *title;                /* May be null if there is no title. */
-    char *caption;              /* May be null if there is no caption. */
+    struct output_item output_item;  /* Superclass. */
+    struct table *table;             /* The table to be rendered. */
+    struct table_item_text *title;   /* Null if there is no title. */
+    struct table_item_text *caption; /* Null if there is no caption. */
   };
 
 struct table_item *table_item_create (struct table *, const char *title,
@@ -45,11 +57,14 @@ struct table_item *table_item_create (struct table *, const char *title,
 
 const struct table *table_item_get_table (const struct table_item *);
 
-const char *table_item_get_title (const struct table_item *);
-void table_item_set_title (struct table_item *, const char *);
+const struct table_item_text *table_item_get_title (const struct table_item *);
+void table_item_set_title (struct table_item *,
+                           const struct table_item_text *);
 
-const char *table_item_get_caption (const struct table_item *);
-void table_item_set_caption (struct table_item *, const char *);
+const struct table_item_text *table_item_get_caption (
+  const struct table_item *);
+void table_item_set_caption (struct table_item *,
+                             const struct table_item_text *);
 \f
 /* This boilerplate for table_item, a subclass of output_item, was
    autogenerated by mk-class-boilerplate. */
index 5dbf99b15ad3e6303357a061fd7adc390932eee3..a511e13bd5c95439dc0504b91abb64f414e7aedf 100644 (file)
 
 #include "output/table.h"
 
+struct string;
+
+struct footnote
+  {
+    size_t idx;
+    char *content;
+    char *marker;
+  };
+
 /* An item of contents within a table cell. */
 struct cell_contents
   {
@@ -26,10 +35,13 @@ struct cell_contents
     char *text;                 /* A paragraph of text. */
 
     /* Optional footnote(s). */
-    char **footnotes;
+    const struct footnote **footnotes;
     size_t n_footnotes;
   };
 
+void cell_contents_format_footnote_markers (const struct cell_contents *,
+                                            struct string *);
+
 /* A cell in a table. */
 struct table_cell
   {
@@ -194,5 +206,7 @@ void table_set_nr (struct table *, int nr);
 
 void table_get_cell (const struct table *, int x, int y, struct table_cell *);
 int table_get_rule (const struct table *, enum table_axis, int x, int y);
+size_t table_collect_footnotes (const struct table_item *,
+                                const struct footnote ***);
 
 #endif /* output/table-provider.h */
index 2158ca723e53bf3c5e2a83adb91433c079bad943..7f37479984274dc357d50da44f03423d2ea891bd 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "libpspp/cast.h"
 #include "libpspp/compiler.h"
+#include "libpspp/str.h"
 #include "output/table-item.h"
 
 #include "gl/xalloc.h"
@@ -195,6 +196,80 @@ table_get_rule (const struct table *table, enum table_axis axis, int x, int y)
   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
   return table->klass->get_rule (table, axis, x, y);
 }
+
+void
+cell_contents_format_footnote_markers (const struct cell_contents *c,
+                                       struct string *s)
+{
+  for (size_t i = 0; i < c->n_footnotes; i++)
+    {
+      if (i)
+        ds_put_byte (s, ',');
+      ds_put_cstr (s, c->footnotes[i]->marker);
+    }
+}
+
+static const struct footnote **
+add_footnotes (const struct footnote **refs, size_t n_refs,
+               const struct footnote **footnotes, size_t *allocated, size_t *n)
+{
+  for (size_t i = 0; i < n_refs; i++)
+    {
+      const struct footnote *f = refs[i];
+      if (f->idx >= *allocated)
+        {
+          size_t new_allocated = (f->idx + 1) * 2;
+          footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
+          while (*allocated < new_allocated)
+            footnotes[(*allocated)++] = NULL;
+        }
+      footnotes[f->idx] = f;
+      if (f->idx >= *n)
+        *n = f->idx + 1;
+    }
+  return footnotes;
+}
+
+size_t
+table_collect_footnotes (const struct table_item *item,
+                         const struct footnote ***footnotesp)
+{
+  const struct footnote **footnotes = NULL;
+  size_t allocated = 0;
+  size_t n = 0;
+
+  struct table *t = item->table;
+  for (int y = 0; y < table_nr (t); y++)
+    {
+      struct table_cell cell;
+      for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
+        {
+          table_get_cell (t, x, y, &cell);
+
+          if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
+            for (size_t i = 0; i < cell.n_contents; i++)
+              {
+                const struct cell_contents *c = &cell.contents[i];
+                footnotes = add_footnotes (c->footnotes, c->n_footnotes,
+                                           footnotes, &allocated, &n);
+              }
+          table_cell_free (&cell);
+        }
+    }
+
+  const struct table_item_text *title = table_item_get_title (item);
+  if (title)
+    footnotes = add_footnotes (title->footnotes, title->n_footnotes,
+                               footnotes, &allocated, &n);
+
+  const struct table_item_text *caption = table_item_get_caption (item);
+  if (caption)
+    footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
+                               footnotes, &allocated, &n);
+
+  *footnotesp = footnotes;
+  return n;
+}
 \f
 struct table_unshared
   {
index f30914135a6a1fff3e1e1ecf090fb20a6b9c6707..dd8618f7337d37560f8570e010440446babea4df 100644 (file)
@@ -369,9 +369,10 @@ psppire_output_view_put (struct psppire_output_view *view,
         }
       else if (is_table_item (item))
         {
-          const char *title = table_item_get_title (to_table_item (item));
+          const struct table_item_text *title
+            = table_item_get_title (to_table_item (item));
           if (title != NULL)
-            ds_put_format (&name, "Table: %s", title);
+            ds_put_format (&name, "Table: %s", title->content);
           else
             ds_put_cstr (&name, "Table");
         }
index 03e4238e4142762349c1b3112666ae71b9ce0016..2a874d1c917c7f3948db1cf35811c7e0e0799473 100644 (file)
@@ -353,6 +353,7 @@ read_table (FILE *stream)
   int n_input = 0;
   int nr, nc, hl, hr, ht, hb;
   int r, c;
+  size_t n_footnotes = 0;
 
   if (fgets (buffer, sizeof buffer, stream) == NULL
       || (n_input = sscanf (buffer, "%d %d %d %d %d %d",
@@ -454,7 +455,13 @@ read_table (FILE *stream)
               tab_joint_text (tab, c, r, c + cs - 1, r + rs - 1, opt,
                               content);
             else
-              tab_footnote (tab, c, r, "%s", content);
+              {
+                char marker[2] = { 'a' + n_footnotes, '\0' };
+                struct footnote *f = tab_create_footnote (
+                  tab, n_footnotes, content, marker);
+                tab_add_footnote (tab, c, r, f);
+                n_footnotes++;
+              }
         }
 
   return &tab->table;