output: Refine support for footnotes, subscripts, and superscripts.
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 29 Dec 2019 05:39:50 +0000 (05:39 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 29 Dec 2019 05:53:22 +0000 (05:53 +0000)
The code for all this wasn't fully plumbed through from the pivot table
code down to the drivers.  This commit fixes all that up.

src/output/ascii.c
src/output/cairo.c
src/output/csv.c
src/output/html.c
src/output/pivot-output.c
src/output/pivot-table.c
src/output/pivot-table.h
src/output/table-provider.h
src/output/table.c
src/output/table.h

index 899c2751b8efa5b0c6ee1dd19085df4de6c5d1da..e41e128c6df109878426acc84214951b8e928108 100644 (file)
@@ -615,7 +615,8 @@ ascii_measure_cell_width (void *a_, const struct table_cell *cell,
   clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
   ascii_layout_cell (a, cell, bb, clip, max_width, &h);
 
-  if (cell->n_footnotes || strchr (cell->text, ' '))
+  if (cell->n_footnotes || strchr (cell->text, ' ')
+      || cell->n_subscripts || cell->superscript)
     {
       bb[H][1] = 1;
       ascii_layout_cell (a, cell, bb, clip, min_width, &h);
@@ -809,10 +810,14 @@ text_draw (struct ascii_driver *a, enum table_halign halign, int options,
 }
 
 static char *
-add_footnote_markers (const char *text, const struct table_cell *cell)
+add_markers (const char *text, const struct table_cell *cell)
 {
   struct string s = DS_EMPTY_INITIALIZER;
   ds_put_cstr (&s, text);
+  for (size_t i = 0; i < cell->n_subscripts; i++)
+    ds_put_format (&s, "%c%s", i ? ',' : '_', cell->subscripts[i]);
+  if (cell->superscript)
+    ds_put_format (&s, "^%s", cell->superscript);
   for (size_t i = 0; i < cell->n_footnotes; i++)
     ds_put_format (&s, "[%s]", cell->footnotes[i]->marker);
   return ds_steal_cstr (&s);
@@ -831,11 +836,11 @@ ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
                             ? output_get_text_from_markup (cell->text)
                             : cell->text);
 
-  /* Append footnote markers if any. */
+  /* Append footnotes, subscripts, superscript if any. */
   const char *text;
-  if (cell->n_footnotes)
+  if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
     {
-      text = add_footnote_markers (plain_text, cell);
+      text = add_markers (plain_text, cell);
       if (plain_text != cell->text)
         free (CONST_CAST (char *, plain_text));
     }
index 2b6ae706c53a0912ae962f2f858d185471809f71..b1a7e273686861782b471242d296115a5aa0223c 100644 (file)
@@ -1422,31 +1422,45 @@ xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
 }
 
 static void
-add_attr_with_start (PangoAttrList *list, PangoAttribute *attr, guint start_index)
+add_attr (PangoAttrList *list, PangoAttribute *attr,
+          guint start_index, guint end_index)
 {
   attr->start_index = start_index;
+  attr->end_index = end_index;
   pango_attr_list_insert (list, attr);
 }
 
 static void
-markup_escape (const char *in, struct string *out)
-{
-  for (int c = *in++; c; c = *in++)
-    switch (c)
-      {
-      case '&':
-        ds_put_cstr (out, "&amp;");
-        break;
-      case '<':
-        ds_put_cstr (out, "&lt;");
-        break;
-      case '>':
-        ds_put_cstr (out, "&gt;");
-        break;
-      default:
-        ds_put_byte (out, c);
-        break;
-      }
+markup_escape (struct string *out, unsigned int options,
+               const char *in, size_t len)
+{
+  if (!(options & TAB_MARKUP))
+    {
+      ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
+      return;
+    }
+
+  while (len-- > 0)
+    {
+      int c = *in++;
+      switch (c)
+        {
+        case 0:
+          return;
+        case '&':
+          ds_put_cstr (out, "&amp;");
+          break;
+        case '<':
+          ds_put_cstr (out, "&lt;");
+          break;
+        case '>':
+          ds_put_cstr (out, "&gt;");
+          break;
+        default:
+          ds_put_byte (out, c);
+          break;
+        }
+    }
 }
 
 static int
@@ -1510,6 +1524,7 @@ xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
     }
 
   struct string tmp = DS_EMPTY_INITIALIZER;
+  PangoAttrList *attrs = NULL;
 
   /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in
      Pango's implementation of it): it will break after a period or a comma
@@ -1525,7 +1540,23 @@ xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
      happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups
      are present then there will always be a digit on both sides of every
      period and comma. */
-  if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
+  if (options & TAB_MARKUP)
+    {
+      PangoAttrList *new_attrs;
+      char *new_text;
+      if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
+        {
+          attrs = new_attrs;
+          tmp.ss = ss_cstr (new_text);
+          tmp.capacity = tmp.ss.length;
+        }
+      else
+        {
+          /* XXX should we report the error? */
+          ds_put_cstr (&tmp, text);
+        }
+    }
+  else if (options & TAB_ROTATE || bb[H][1] != INT_MAX)
     {
       const char *decimal = text + strcspn (text, ".,");
       if (decimal[0]
@@ -1533,93 +1564,106 @@ xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell,
           && (decimal == text || !c_isdigit (decimal[-1])))
         {
           ds_extend (&tmp, strlen (text) + 16);
-          ds_put_substring (&tmp, ss_buffer (text, decimal - text + 1));
+          markup_escape (&tmp, options, text, decimal - text + 1);
           ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
-          ds_put_cstr (&tmp, decimal + 1);
+          markup_escape (&tmp, options, decimal + 1, -1);
         }
     }
 
-  if (cell->n_footnotes)
+  if (font_style->underline)
     {
-      int footnote_adjustment;
-      if (cell->n_footnotes == 1 && halign == TABLE_HALIGN_RIGHT)
-        {
-          const char *marker = cell->footnotes[0]->marker;
-          pango_layout_set_text (font->layout, marker, strlen (marker));
-
-          PangoAttrList *attrs = pango_attr_list_new ();
-          pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
-          pango_attr_list_insert (attrs, pango_attr_rise_new (3000));
-          pango_layout_set_attributes (font->layout, attrs);
-          pango_attr_list_unref (attrs);
-
-          int w = get_layout_dimension (font->layout, X);
-          int right_margin = px_to_xr (cell_style->margin[X][R]);
-          footnote_adjustment = MIN (w, right_margin);
-
-          pango_layout_set_attributes (font->layout, NULL);
-        }
-      else
-        footnote_adjustment = px_to_xr (cell_style->margin[X][R]);
-
-      if (R)
-        bb[X][R] += footnote_adjustment;
-      else
-        bb[X][R] -= footnote_adjustment;
+      if (!attrs)
+        attrs = pango_attr_list_new ();
+      pango_attr_list_insert (attrs, pango_attr_underline_new (
+                                PANGO_UNDERLINE_SINGLE));
+    }
 
+  if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
+    {
+      /* If we haven't already put TEXT into tmp, do it now. */
       if (ds_is_empty (&tmp))
         {
           ds_extend (&tmp, strlen (text) + 16);
-          ds_put_cstr (&tmp, text);
+          markup_escape (&tmp, options, text, -1);
+        }
+
+      size_t subscript_ofs = ds_length (&tmp);
+      for (size_t i = 0; i < cell->n_subscripts; i++)
+        {
+          if (i)
+            ds_put_byte (&tmp, ',');
+          ds_put_cstr (&tmp, cell->subscripts[i]);
         }
-      size_t initial_length = ds_length (&tmp);
 
+      size_t superscript_ofs = ds_length (&tmp);
+      if (cell->superscript)
+        ds_put_cstr (&tmp, cell->superscript);
+
+      size_t footnote_ofs = ds_length (&tmp);
       for (size_t i = 0; i < cell->n_footnotes; i++)
         {
           if (i)
             ds_put_byte (&tmp, ',');
+          ds_put_cstr (&tmp, cell->footnotes[i]->marker);
+        }
 
-          const char *marker = cell->footnotes[i]->marker;
-          if (options & TAB_MARKUP)
-            markup_escape (marker, &tmp);
-          else
-            ds_put_cstr (&tmp, marker);
+      /* Allow footnote markers to occupy the right margin.  That way, numbers
+         in the column are still aligned. */
+      if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT)
+        {
+          /* Measure the width of the footnote marker, so we know how much we
+             need to make room for. */
+          pango_layout_set_text (font->layout, ds_cstr (&tmp) + footnote_ofs,
+                                 ds_length (&tmp) - footnote_ofs);
+
+          PangoAttrList *fn_attrs = pango_attr_list_new ();
+          pango_attr_list_insert (
+            fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
+          pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
+          pango_layout_set_attributes (font->layout, fn_attrs);
+          pango_attr_list_unref (fn_attrs);
+          int footnote_width = get_layout_dimension (font->layout, X);
+
+          /* Bound the adjustment by the width of the right margin. */
+          int right_margin = px_to_xr (cell_style->margin[X][R]);
+          int footnote_adjustment = MIN (footnote_width, right_margin);
+
+          /* Adjust the bounding box. */
+          if (options & TAB_ROTATE)
+            footnote_adjustment = -footnote_adjustment;
+          bb[X][R] += footnote_adjustment;
+
+          /* Clean up. */
+          pango_layout_set_attributes (font->layout, NULL);
         }
 
-      if (options & TAB_MARKUP)
-        pango_layout_set_markup (font->layout,
-                                 ds_cstr (&tmp), ds_length (&tmp));
-      else
-        pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
-
-      PangoAttrList *attrs = pango_attr_list_new ();
-      if (font_style->underline)
-        pango_attr_list_insert (attrs, pango_attr_underline_new (
-                               PANGO_UNDERLINE_SINGLE));
-      add_attr_with_start (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL), initial_length);
-      add_attr_with_start (attrs, pango_attr_rise_new (3000), initial_length);
-      add_attr_with_start (
-        attrs, pango_attr_font_desc_new (font->desc), initial_length);
+      /* Set attributes. */
+      if (!attrs)
+        attrs = pango_attr_list_new ();
+      add_attr (attrs, pango_attr_font_desc_new (font->desc), subscript_ofs,
+                PANGO_ATTR_INDEX_TO_TEXT_END);
+      add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL),
+                subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
+      if (cell->n_subscripts)
+        add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs,
+                  superscript_ofs - subscript_ofs);
+      if (cell->superscript || cell->n_footnotes)
+        add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs,
+                  PANGO_ATTR_INDEX_TO_TEXT_END);
+    }
+
+  /* Set the attributes, if any. */
+  if (attrs)
+    {
       pango_layout_set_attributes (font->layout, attrs);
       pango_attr_list_unref (attrs);
     }
-  else
-    {
-      const char *content = ds_is_empty (&tmp) ? text : ds_cstr (&tmp);
-      if (options & TAB_MARKUP)
-        pango_layout_set_markup (font->layout, content, -1);
-      else
-        pango_layout_set_text (font->layout, content, -1);
 
-      if (font_style->underline)
-        {
-          PangoAttrList *attrs = pango_attr_list_new ();
-          pango_attr_list_insert (attrs, pango_attr_underline_new (
-                                    PANGO_UNDERLINE_SINGLE));
-          pango_layout_set_attributes (font->layout, attrs);
-          pango_attr_list_unref (attrs);
-        }
-    }
+  /* Set the text. */
+  if (ds_is_empty (&tmp))
+    pango_layout_set_text (font->layout, text, -1);
+  else
+    pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
   ds_destroy (&tmp);
 
   pango_layout_set_alignment (font->layout,
index 6c2f7495c45d494acab3e6165f4389ceb3f0b0b7..1c6662793fc3fafd5c40f83d3a668f700e32bc8d 100644 (file)
@@ -227,7 +227,8 @@ csv_submit (struct output_driver *driver,
 
               if (x != cell.d[TABLE_HORZ][0] || y != cell.d[TABLE_VERT][0])
                 csv_output_field (csv, "");
-              else if (!(cell.options & TAB_MARKUP) && !cell.n_footnotes)
+              else if (!(cell.options & TAB_MARKUP) && !cell.n_footnotes
+                       && !cell.n_subscripts && !cell.superscript)
                 csv_output_field (csv, cell.text);
               else
                 {
@@ -242,6 +243,12 @@ csv_submit (struct output_driver *driver,
                   else
                     ds_put_cstr (&s, cell.text);
 
+                  if (cell.n_subscripts)
+                    for (size_t i = 0; i < cell.n_subscripts; i++)
+                      ds_put_format (&s, "%c%s",
+                                     i ? ',' : '_', cell.subscripts[i]);
+                  if (cell.superscript)
+                    ds_put_format (&s, "^%s", cell.superscript);
                   csv_format_footnotes (cell.footnotes, cell.n_footnotes, &s);
                   csv_output_field (csv, ds_cstr (&s));
                   ds_destroy (&s);
index 5233fc8fe55f0afb3746a7f8ed75a5d9d6554ca2..989476ecaadd394b0133bdae7040966b16bec08d 100644 (file)
@@ -66,8 +66,7 @@ struct html_driver
 static const struct output_driver_class html_driver_class;
 
 static void html_output_table (struct html_driver *, const struct table_item *);
-static void escape_string (FILE *file,
-                           const char *text, size_t length,
+static void escape_string (FILE *file, const char *text,
                            const char *space, const char *newline);
 static void print_title_tag (FILE *file, const char *name,
                              const char *content);
@@ -201,7 +200,7 @@ print_title_tag (FILE *file, const char *name, const char *content)
   if (content != NULL)
     {
        fprintf (file, "<%s>", name);
-      escape_string (file, content, strlen (content), " ", " - ");
+      escape_string (file, content, " ", " - ");
       fprintf (file, "</%s>\n", name);
     }
 }
@@ -275,7 +274,7 @@ html_submit (struct output_driver *driver,
 
         case TEXT_ITEM_SYNTAX:
           fprintf (html->file, "<PRE class=\"syntax\">");
-          escape_string (html->file, s, strlen (s), " ", "<BR>");
+          escape_string (html->file, s, " ", "<BR>");
           fprintf (html->file, "</PRE>\n");
           break;
 
@@ -297,20 +296,20 @@ html_submit (struct output_driver *driver,
     }
 }
 
-/* Write LENGTH characters in TEXT to file F, escaping characters as necessary
-   for HTML.  Spaces are replaced by SPACE, which should be " " or "&nbsp;"
-   New-lines are replaced by NEWLINE, which might be "<BR>" or "\n" or
-   something else appropriate. */
+/* Write TEXT to file F, escaping characters as necessary for HTML.  Spaces are
+   replaced by SPACE, which should be " " or "&nbsp;" New-lines are replaced by
+   NEWLINE, which might be "<BR>" or "\n" or something else appropriate. */
 static void
-escape_string (FILE *file,
-               const char *text, size_t length,
+escape_string (FILE *file, const char *text,
                const char *space, const char *newline)
 {
-  while (length-- > 0)
+  for (;;)
     {
       char c = *text++;
       switch (c)
         {
+        case 0:
+          return;
         case '\n':
           fputs (newline, file);
           break;
@@ -336,6 +335,18 @@ escape_string (FILE *file,
     }
 }
 
+static void
+escape_tag (FILE *file, const char *tag,
+            const char *text, const char *space, const char *newline)
+{
+  if (!text || !*text)
+    return;
+
+  fprintf (file, "<%s>", tag);
+  escape_string (file, text, space, newline);
+  fprintf (file, "</%s>", tag);
+}
+
 static const char *
 border_to_css (int border)
 {
@@ -403,8 +414,7 @@ html_put_footnote_markers (struct html_driver *html,
 
           if (i > 0)
             putc (',', html->file);
-          escape_string (html->file, f->marker,
-                         strlen (f->marker), " ", "<BR>");
+          escape_string (html->file, f->marker, " ", "<BR>");
         }
       fputs ("</SUP>", html->file);
     }
@@ -414,8 +424,7 @@ 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>");
+  escape_string (html->file, text->content, " ", "<BR>");
   html_put_footnote_markers (html, text->footnotes, text->n_footnotes);
 }
 
@@ -429,8 +438,7 @@ html_put_table_item_layers (struct html_driver *html,
         fputs ("<BR>\n", html->file);
 
       const struct table_item_layer *layer = &layers->layers[i];
-      escape_string (html->file, layer->content, strlen (layer->content),
-                     " ", "<BR>");
+      escape_string (html->file, layer->content, " ", "<BR>");
       html_put_footnote_markers (html, layer->footnotes, layer->n_footnotes);
     }
 }
@@ -456,12 +464,8 @@ html_output_table (struct html_driver *html, const struct table_item *item)
   for (size_t i = 0; i < n_footnotes; 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>");
+      escape_tag (html->file, "SUP", f[i]->marker, " ", "<BR>");
+      escape_string (html->file, f[i]->content, " ", "<BR>");
     }
   free (f);
   if (tfoot)
@@ -569,17 +573,27 @@ html_output_table (struct html_driver *html, const struct table_item *item)
           /* Output cell contents. */
           const char *s = cell.text;
           if (cell.options & TAB_FIX)
-            {
-              fputs ("<TT>", html->file);
-              escape_string (html->file, s, strlen (s), "&nbsp;", "<BR>");
-              fputs ("</TT>", html->file);
-            }
+            escape_tag (html->file, "TT", s, "&nbsp;", "<BR>");
           else
             {
               s += strspn (s, CC_SPACES);
-              escape_string (html->file, s, strlen (s), " ", "<BR>");
+              escape_string (html->file, s, " ", "<BR>");
             }
 
+          if (cell.n_subscripts)
+            {
+              fputs ("<SUB>", html->file);
+              for (size_t i = 0; i < cell.n_subscripts; i++)
+                {
+                  if (i)
+                    putc (',', html->file);
+                  escape_string (html->file, cell.subscripts[i],
+                                 "&nbsp;", "<BR>");
+                }
+              fputs ("</SUB>", html->file);
+            }
+          if (cell.superscript)
+            escape_tag (html->file, "SUP", cell.superscript, "&nbsp;", "<BR>");
           html_put_footnote_markers (html, cell.footnotes, cell.n_footnotes);
 
           /* Output </TH> or </TD>. */
index f90a953e5d5758c30049e104de39313597320f6e..fc41cb870f36d6439c3955982d1bed0760a42eaf 100644 (file)
@@ -124,6 +124,10 @@ fill_cell (struct table *t, int x1, int y1, int x2, int y2,
       
       for (size_t i = 0; i < value->n_footnotes; i++)
         table_add_footnote (t, x1, y1, footnotes[value->footnotes[i]->idx]);
+
+      if (value->n_subscripts)
+        table_add_subscripts (t, x1, y1,
+                              value->subscripts, value->n_subscripts);
     }
 }
 
index d4649292ac807a9fa0a79af453eb6aa8379cb328..ead1a1cc473ad81a8d30f08264746f42d74b8023 100644 (file)
@@ -1891,8 +1891,11 @@ pivot_value_format (const struct pivot_value *value,
 {
   pivot_value_format_body ( value, show_values, show_variables, out);
 
-  if (value->subscript)
-    ds_put_format (out, "_%s", value->subscript);
+  if (value->n_subscripts)
+    {
+      for (size_t i = 0; i < value->n_subscripts; i++)
+        ds_put_format (out, "%c%s", i ? ',' : '_', value->subscripts[i]);
+    }
 
   if (value->superscript)
     ds_put_format (out, "^%s", value->superscript);
@@ -1929,7 +1932,12 @@ pivot_value_destroy (struct pivot_value *value)
       /* Do not free the elements of footnotes because VALUE does not own
          them. */
       free (value->footnotes);
-      free (value->subscript);
+
+      for (size_t i = 0; i < value->n_subscripts; i++)
+        free (value->subscripts[i]);
+      free (value->subscripts);
+
+      free (value->superscript);
 
       switch (value->type)
         {
index 096e50a1ebcf932ea2dd772f38ad294cdb840909..f2f0c564ab6c88e6b7ac32f8257797b8811859ac 100644 (file)
@@ -599,7 +599,10 @@ struct pivot_value
   {
     struct font_style *font_style;
     struct cell_style *cell_style;
-    char *subscript;
+
+    char **subscripts;
+    size_t n_subscripts;
+
     char *superscript;
 
     const struct pivot_footnote **footnotes;
index d961d43e10184058ff0124b8aecdf2b9df0af667..711f84d6b3960a529a60022a21354cca4b0a4b92 100644 (file)
@@ -55,11 +55,11 @@ struct table_cell
 
     unsigned int options;       /* TAB_*. */
     char *text;                 /* A paragraph of text. */
-
-    /* Optional footnote(s). */
+    char **subscripts;
+    size_t n_subscripts;
+    char *superscript;
     const struct footnote **footnotes;
     size_t n_footnotes;
-
     const struct area_style *style;
   };
 
index b1707a84d875dfb633f87c8736ad70eb507fa0ca..01ab1bffc0acef9c4b1b10a5957776a294a7081c 100644 (file)
@@ -580,14 +580,11 @@ add_joined_cell (struct table *table, int x1, int y1, int x2, int y2,
              x1, y1, x2, y2);
 
   struct table_cell *cell = pool_alloc (table->container, sizeof *cell);
-  cell->d[TABLE_HORZ][0] = x1;
-  cell->d[TABLE_VERT][0] = y1;
-  cell->d[TABLE_HORZ][1] = ++x2;
-  cell->d[TABLE_VERT][1] = ++y2;
-  cell->options = opt;
-  cell->footnotes = NULL;
-  cell->n_footnotes = 0;
-  cell->style = NULL;
+  *cell = (struct table_cell) {
+    .d = { [TABLE_HORZ] = { x1, ++x2 },
+           [TABLE_VERT] = { y1, ++y2 } },
+    .options = opt,
+  };
 
   void **cc = &table->cc[x1 + y1 * table_nc (table)];
   unsigned short *ct = &table->ct[x1 + y1 * table_nc (table)];
@@ -639,6 +636,29 @@ get_joined_cell (struct table *table, int x, int y)
   return cell;
 }
 
+/* Sets the subscripts for column X, row Y in TABLE. */
+void
+table_add_subscripts (struct table *table, int x, int y,
+                      char **subscripts, size_t n_subscripts)
+{
+  struct table_cell *cell = get_joined_cell (table, x, y);
+
+  cell->n_subscripts = n_subscripts;
+  cell->subscripts = pool_nalloc (table->container, n_subscripts,
+                                  sizeof *cell->subscripts);
+  for (size_t i = 0; i < n_subscripts; i++)
+    cell->subscripts[i] = pool_strdup (table->container, subscripts[i]);
+}
+
+/* Sets the superscript for column X, row Y in TABLE. */
+void
+table_add_superscript (struct table *table, int x, int y,
+                       const char *superscript)
+{
+  get_joined_cell (table, x, y)->superscript
+    = pool_strdup (table->container, superscript);
+}
+
 /* Create a footnote in TABLE with MARKER (e.g. "a") as its marker and CONTENT
    as its content.  The footnote will be styled as STYLE, which is mandatory.
    IDX must uniquely identify the footnote within TABLE.
index 9f84f5dd3f26a00df5e8eb9f1c61d05019d8baa5..0cdeef1b4ea1a7661dcf717fd53b6c134a216161 100644 (file)
@@ -283,10 +283,14 @@ void table_text (struct table *, int c, int r, unsigned opt, const char *);
 void table_text_format (struct table *, int c, int r, unsigned opt,
                         const char *, ...)
   PRINTF_FORMAT (5, 6);
-
 void table_joint_text (struct table *, int x1, int y1, int x2, int y2,
                        unsigned opt, const char *);
 
+void table_add_subscripts (struct table *, int x, int y,
+                           char **subscripts, size_t n_subscripts);
+void table_add_superscript (struct table *, int x, int y,
+                            const char *superscript);
+
 /* Footnotes.
 
    Use table_create_footnote() to create the footnotes themselves, then use