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);
}
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);
? 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));
}
}
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, "&");
- break;
- case '<':
- ds_put_cstr (out, "<");
- break;
- case '>':
- ds_put_cstr (out, ">");
- 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, "&");
+ break;
+ case '<':
+ ds_put_cstr (out, "<");
+ break;
+ case '>':
+ ds_put_cstr (out, ">");
+ break;
+ default:
+ ds_put_byte (out, c);
+ break;
+ }
+ }
}
static int
}
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
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]
&& (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,
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
{
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);
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);
if (content != NULL)
{
fprintf (file, "<%s>", name);
- escape_string (file, content, strlen (content), " ", " - ");
+ escape_string (file, content, " ", " - ");
fprintf (file, "</%s>\n", name);
}
}
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;
}
}
-/* Write LENGTH characters in TEXT to file F, escaping characters as necessary
- for HTML. Spaces are replaced by SPACE, which should be " " or " "
- 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 " " 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;
}
}
+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)
{
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);
}
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);
}
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);
}
}
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)
/* Output cell contents. */
const char *s = cell.text;
if (cell.options & TAB_FIX)
- {
- fputs ("<TT>", html->file);
- escape_string (html->file, s, strlen (s), " ", "<BR>");
- fputs ("</TT>", html->file);
- }
+ escape_tag (html->file, "TT", s, " ", "<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],
+ " ", "<BR>");
+ }
+ fputs ("</SUB>", html->file);
+ }
+ if (cell.superscript)
+ escape_tag (html->file, "SUP", cell.superscript, " ", "<BR>");
html_put_footnote_markers (html, cell.footnotes, cell.n_footnotes);
/* Output </TH> or </TD>. */
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);
}
}
{
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);
/* 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)
{
{
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;
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;
};
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)];
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.
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