stpcpy \
strerror \
strftime \
+ strsep \
strtod \
strtok_r \
sys_stat \
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 *min, int *max);
+ int footnote_idx, int *min, int *max);
static int ascii_measure_cell_height (void *, const struct table_cell *,
- int width);
+ int footnote_idx, int width);
static void ascii_draw_cell (void *, const struct table_cell *,
- int bb[TABLE_N_AXES][2],
+ int footnote_idx, int bb[TABLE_N_AXES][2],
int clip[TABLE_N_AXES][2]);
static void
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);
static void
ascii_measure_cell_width (void *a_, const struct table_cell *cell,
- int *min_width, int *max_width)
+ int footnote_idx, int *min_width, int *max_width)
{
struct ascii_driver *a = a_;
int bb[TABLE_N_AXES][2];
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, bb, clip, max_width, &h);
+ ascii_layout_cell (a, cell, footnote_idx, bb, clip, max_width, &h);
if (cell->n_contents != 1
|| cell->contents[0].table
+ || cell->contents[0].n_footnotes
|| strchr (cell->contents[0].text, ' '))
{
bb[H][1] = 1;
- ascii_layout_cell (a, cell, bb, clip, min_width, &h);
+ ascii_layout_cell (a, cell, footnote_idx, bb, clip, min_width, &h);
}
else
*min_width = *max_width;
}
static int
-ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
+ascii_measure_cell_height (void *a_, const struct table_cell *cell,
+ int footnote_idx, int width)
{
struct ascii_driver *a = a_;
int bb[TABLE_N_AXES][2];
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, bb, clip, &w, &h);
+ ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
return h;
}
static void
-ascii_draw_cell (void *a_, const struct table_cell *cell,
+ascii_draw_cell (void *a_, const struct table_cell *cell, int footnote_idx,
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, bb, clip, &w, &h);
+ ascii_layout_cell (a, cell, footnote_idx, bb, clip, &w, &h);
}
static char *
static int
ascii_layout_cell_text (struct ascii_driver *a,
- const struct cell_contents *contents,
+ const struct cell_contents *contents, int *footnote_idx,
int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
int *widthp)
{
- size_t length = strlen (contents->text);
+ size_t length;
+ const char *text;
char *breaks;
int bb_width;
size_t pos;
int y;
y = bb[V][0];
- if (length == 0)
- return y;
+ length = strlen (contents->text);
+ if (contents->n_footnotes)
+ {
+ struct string s;
+ int i;
+
+ ds_init_empty (&s);
+ 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);
+ }
+
+ length = ds_length (&s);
+ text = ds_steal_cstr (&s);
+ }
+ else
+ {
+ if (length == 0)
+ return y;
+ text = contents->text;
+ }
breaks = xmalloc (length + 1);
- u8_possible_linebreaks (CHAR_CAST (const uint8_t *, contents->text), length,
+ u8_possible_linebreaks (CHAR_CAST (const uint8_t *, text), length,
"UTF-8", breaks);
breaks[length] = (breaks[length - 1] == UC_BREAK_MANDATORY
? UC_BREAK_PROHIBITED : UC_BREAK_POSSIBLE);
bb_width = bb[H][1] - bb[H][0];
for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
{
- const uint8_t *line = CHAR_CAST (const uint8_t *, contents->text + pos);
+ const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos);
const char *b = breaks + pos;
size_t n = length - pos;
}
free (breaks);
+ if (text != contents->text)
+ free (CONST_CAST (char *, text));
return y;
}
static int
ascii_layout_subtable (struct ascii_driver *a,
const struct cell_contents *contents,
+ int *footnote_idx UNUSED /* XXX */,
int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2] UNUSED,
int *widthp)
{
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)
{
}
if (contents->text)
- bb[V][0] = ascii_layout_cell_text (a, contents, bb, clip, widthp);
+ bb[V][0] = ascii_layout_cell_text (a, contents, &footnote_idx,
+ bb, clip, widthp);
else
- bb[V][0] = ascii_layout_subtable (a, contents, bb, clip, widthp);
+ bb[V][0] = ascii_layout_subtable (a, contents, &footnote_idx,
+ bb, clip, widthp);
}
*heightp = bb[V][0] - bb_[V][0];
}
contents.options = options | TAB_LEFT;
contents.text = CONST_CAST (char *, s);
contents.table = NULL;
+ contents.n_footnotes = 0;
memset (&cell, 0, sizeof cell);
cell.contents = &contents;
bb[TABLE_VERT][0] = y;
bb[TABLE_VERT][1] = a->length;
- ascii_layout_cell (a, &cell, bb, bb, &width, &height);
+ ascii_layout_cell (a, &cell, 0, bb, bb, &width, &height);
a->y = 1;
}
XR_FONT_PROPORTIONAL,
XR_FONT_EMPHASIS,
XR_FONT_FIXED,
+ XR_FONT_MARKER,
XR_N_FONTS
};
int line_space; /* Space between lines. */
int line_width; /* Width of lines. */
+ int cell_margin;
+
int min_break[TABLE_N_AXES]; /* Min cell size to break across pages. */
struct xr_color bg; /* Background color */
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 *min, int *max);
+ int footnote_idx, int *min, int *max);
static int xr_measure_cell_height (void *, const struct table_cell *,
- 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 footnote_idx,
int bb[TABLE_N_AXES][2],
int clip[TABLE_N_AXES][2]);
-static int xr_adjust_break (void *, const struct table_cell *,
+static int xr_adjust_break (void *, const struct table_cell *, int footnote_idx,
int width, int height);
static struct xr_render_fsm *xr_render_output_item (
"serif", font_size);
xr->fonts[XR_FONT_EMPHASIS].desc = parse_font (d, o, "emph-font",
"serif italic", font_size);
+ xr->fonts[XR_FONT_MARKER].desc = parse_font (d, o, "marker-font", "serif",
+ font_size * PANGO_SCALE_X_SMALL);
- xr->line_gutter = parse_dimension (opt (d, o, "gutter", "3pt")) * scale;
+ xr->line_gutter = 0;
xr->line_space = XR_POINT;
xr->line_width = XR_POINT / 2;
xr->page_number = 0;
xr->char_width = MAX (xr->char_width, pango_to_xr (char_width));
xr->char_height = MAX (xr->char_height, pango_to_xr (char_height));
}
+ xr->cell_margin = xr->char_width;
if (xr->params == NULL)
{
}
\f
static void
-xr_layout_cell (struct xr_driver *, const struct table_cell *,
+xr_layout_cell (struct xr_driver *, const struct table_cell *, int footnote_idx,
int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
int *width, int *height, int *brk);
static void
xr_measure_cell_width (void *xr_, const struct table_cell *cell,
- int *min_width, int *max_width)
+ int footnote_idx, int *min_width, int *max_width)
{
struct xr_driver *xr = xr_;
int bb[TABLE_N_AXES][2];
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, bb, clip, max_width, &h, NULL);
+ xr_layout_cell (xr, cell, footnote_idx, bb, clip, max_width, &h, NULL);
bb[H][1] = 1;
- xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
+ xr_layout_cell (xr, cell, footnote_idx, bb, clip, min_width, &h, NULL);
+
+ if (*min_width > 0)
+ *min_width += xr->cell_margin * 2;
+ if (*max_width > 0)
+ *max_width += xr->cell_margin * 2;
}
static int
-xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
+xr_measure_cell_height (void *xr_, const struct table_cell *cell,
+ int footnote_idx, int width)
{
struct xr_driver *xr = xr_;
int bb[TABLE_N_AXES][2];
int w, h;
bb[H][0] = 0;
- bb[H][1] = width;
+ bb[H][1] = width - xr->cell_margin * 2;
+ if (bb[H][1] <= 0)
+ return 0;
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, bb, clip, &w, &h, NULL);
+ xr_layout_cell (xr, cell, footnote_idx, bb, clip, &w, &h, NULL);
return h;
}
static void
-xr_draw_cell (void *xr_, const struct table_cell *cell,
+xr_draw_cell (void *xr_, const struct table_cell *cell, int footnote_idx,
int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
{
struct xr_driver *xr = xr_;
int w, h, brk;
- xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
+ bb[H][0] += xr->cell_margin;
+ 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);
}
static int
-xr_adjust_break (void *xr_, const struct table_cell *cell,
+xr_adjust_break (void *xr_, const struct table_cell *cell, int footnote_idx,
int width, int height)
{
struct xr_driver *xr = xr_;
int clip[TABLE_N_AXES][2];
int w, h, brk;
- if (xr_measure_cell_height (xr_, cell, width) < height)
+ if (xr_measure_cell_height (xr_, cell, footnote_idx, width) < height)
return -1;
bb[H][0] = 0;
- bb[H][1] = width;
+ bb[H][1] = width - 2 * xr->cell_margin;
+ if (bb[H][1] <= 0)
+ return 0;
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, bb, clip, &w, &h, &brk);
+ xr_layout_cell (xr, cell, footnote_idx, bb, clip, &w, &h, &brk);
return brk;
}
\f
}
}
+static void
+add_attr_with_start (PangoAttrList *list, PangoAttribute *attr, guint start_index)
+{
+ attr->start_index = start_index;
+ pango_attr_list_insert (list, attr);
+}
+
static int
xr_layout_cell_text (struct xr_driver *xr,
- const struct cell_contents *contents,
+ const struct cell_contents *contents, int footnote_idx,
int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
int y, int *widthp, int *brk)
{
unsigned int options = contents->options;
struct xr_font *font;
+ bool merge_footnotes;
+ size_t length;
int w, h;
+ if (contents->n_footnotes == 0)
+ merge_footnotes = false;
+ 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);
+ pango_layout_set_text (font->layout, marker, strlen (marker));
+
+ attrs = pango_attr_list_new ();
+ pango_attr_list_insert (attrs, pango_attr_rise_new (7000));
+ pango_layout_set_attributes (font->layout, attrs);
+ pango_attr_list_unref (attrs);
+
+ pango_layout_get_size (font->layout, &w, &h);
+ merge_footnotes = w > xr->cell_margin;
+ if (!merge_footnotes && clip[H][0] != clip[H][1])
+ {
+ cairo_save (xr->cairo);
+ xr_clip (xr, clip);
+ cairo_translate (xr->cairo,
+ xr_to_pt (bb[H][1] + xr->x),
+ xr_to_pt (y + xr->y));
+ pango_layout_set_alignment (font->layout, PANGO_ALIGN_LEFT);
+ pango_layout_set_width (font->layout, -1);
+ pango_cairo_show_layout (xr->cairo, font->layout);
+ cairo_restore (xr->cairo);
+ }
+
+ pango_layout_set_attributes (font->layout, NULL);
+ }
+ else
+ merge_footnotes = true;
+
font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
: options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
: &xr->fonts[XR_FONT_PROPORTIONAL]);
- pango_layout_set_text (font->layout, contents->text, -1);
+ length = strlen (contents->text);
+ if (merge_footnotes)
+ {
+ 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);
+ }
+ pango_layout_set_text (font->layout, ds_cstr (&s), ds_length (&s));
+ ds_destroy (&s);
+
+ attrs = pango_attr_list_new ();
+ add_attr_with_start (attrs, pango_attr_rise_new (7000), length);
+ add_attr_with_start (
+ attrs, pango_attr_font_desc_new (xr->fonts[XR_FONT_MARKER].desc), length);
+ pango_layout_set_attributes (font->layout, attrs);
+ pango_attr_list_unref (attrs);
+ }
+ else
+ pango_layout_set_text (font->layout, contents->text, -1);
pango_layout_set_alignment (
font->layout,
}
}
}
+
+ pango_layout_set_attributes (font->layout, NULL);
return y + h;
}
static int
xr_layout_cell_subtable (struct xr_driver *xr,
const struct cell_contents *contents,
+ int footnote_idx UNUSED,
int bb[TABLE_N_AXES][2],
int clip[TABLE_N_AXES][2], int *widthp, int *brk)
{
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)
{
}
if (contents->text)
- bb[V][0] = xr_layout_cell_text (xr, contents, bb, clip,
+ bb[V][0] = xr_layout_cell_text (xr, contents, footnote_idx, bb, clip,
bb[V][0], width, brk);
else
- bb[V][0] = xr_layout_cell_subtable (xr, contents, bb, clip, width, brk);
+ bb[V][0] = xr_layout_cell_subtable (xr, contents, footnote_idx,
+ bb, clip, width, brk);
+ footnote_idx += contents->n_footnotes;
}
*height = bb[V][0] - bb_[V][0];
}
struct table_item *table_item = to_table_item (output_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);
putc ('\n', csv->file);
}
+ footnote_idx = 0;
for (y = 0; y < table_nr (t); y++)
{
for (x = 0; x < table_nc (t); x++)
if (x != cell.d[TABLE_HORZ][0] || y != cell.d[TABLE_VERT][0])
csv_output_field (csv, "");
- else if (cell.n_contents == 1 && cell.contents[0].text != NULL)
+ else if (cell.n_contents == 1
+ && cell.contents[0].text != NULL
+ && cell.contents[0].n_footnotes == 0)
csv_output_field (csv, cell.contents[0].text);
else
{
ds_init_empty (&s);
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");
- if (cell.contents[i].text != NULL)
- ds_put_cstr (&s, cell.contents[i].text);
+ if (c->text != NULL)
+ ds_put_cstr (&s, c->text);
else
- csv_output_subtable (csv, &s, cell.contents[i].table);
+ csv_output_subtable (csv, &s, c->table);
+
+ 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_output_field (csv, ds_cstr (&s));
ds_destroy (&s);
}
putc ('\n', csv->file);
}
+
+ if (footnote_idx)
+ {
+ 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);
+ }
+ }
+ }
}
else if (is_text_item (output_item))
{
{
const struct table *t = table_item_get_table (item);
const char *caption = table_item_get_caption (item);
- int x, y;
+ int footnote_idx = 0;
+ int y;
- fputs ("<TABLE><TBODY VALIGN=\"TOP\">\n", html->file);
+ fputs ("<TABLE>", html->file);
+
+ 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])
+ continue;
+
+ for (c = cell.contents; c < &cell.contents[cell.n_contents]; c++)
+ {
+ int i;
+
+ for (i = 0; i < c->n_footnotes; i++)
+ {
+ char marker[16];
+
+ if (!footnote_idx)
+ fprintf (html->file, "<TFOOT><TR><TD COLSPAN=%d>",
+ table_nc (t));
+ else
+ fputs ("\n<BR>", html->file);
+ 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>");
+ }
+ }
+ x = cell.d[TABLE_HORZ][1];
+ table_cell_free (&cell);
+ }
+ }
+ if (footnote_idx)
+ {
+ fputs ("</TD></TR></TFOOT>\n", html->file);
+ footnote_idx = 0;
+ }
+
+ fputs ("<TBODY VALIGN=\"TOP\">\n", html->file);
if (caption != NULL)
{
for (y = 0; y < table_nr (t); y++)
{
+ int x;
+
fputs (" <TR>\n", html->file);
- for (x = 0; x < table_nc (t); x++)
+ for (x = 0; x < table_nc (t); )
{
const struct cell_contents *c;
struct table_cell cell;
if (c->text)
{
const char *s = c->text;
+ int i;
if (c->options & TAB_EMPH)
fputs ("<EM>", html->file);
}
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);
+ }
}
else
html_output_table (html, c->table);
/* Output </TH> or </TD>. */
fprintf (html->file, "</%s>\n", tag);
+ x = cell.d[TABLE_HORZ][1];
table_cell_free (&cell);
}
fputs (" </TR>\n", html->file);
/* Name of current command. */
char *command_name;
+
+ /* Number of footnotes so far. */
+ int n_footnotes;
};
static const struct output_driver_class odt_driver_class;
}
static void
-write_xml_with_line_breaks (xmlTextWriterPtr writer, char *line)
+write_xml_with_line_breaks (struct odt_driver *odt, const char *line_)
{
- char *newline;
- char *p;
+ xmlTextWriterPtr writer = odt->content_wtr;
- for (p = line; *p; p = newline + 1)
+ if (!strchr (line_, '\n'))
+ xmlTextWriterWriteString (writer, _xml(line_));
+ else
{
- newline = strchr (p, '\n');
+ char *line = xstrdup (line_);
+ char *newline;
+ char *p;
- if (!newline)
+ for (p = line; *p; p = newline + 1)
{
+ newline = strchr (p, '\n');
+
+ if (!newline)
+ {
+ xmlTextWriterWriteString (writer, _xml(p));
+ free (line);
+ return;
+ }
+
+ if (newline > p && newline[-1] == '\r')
+ newline[-1] = '\0';
+ else
+ *newline = '\0';
xmlTextWriterWriteString (writer, _xml(p));
- return;
+ xmlTextWriterWriteElement (writer, _xml("text:line-break"), _xml(""));
}
-
- if (newline > p && newline[-1] == '\r')
- newline[-1] = '\0';
- else
- *newline = '\0';
- xmlTextWriterWriteString (writer, _xml(p));
- xmlTextWriterWriteElement (writer, _xml("text:line-break"), _xml(""));
}
}
+static void
+write_footnote (struct odt_driver *odt, const char *footnote)
+{
+ 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)
+ xmlTextWriterWriteFormatAttribute (odt->content_wtr, _xml("text:label"),
+ "(%s)", marker);
+ else
+ xmlTextWriterWriteAttribute (odt->content_wtr, _xml("text:label"),
+ _xml(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);
+ xmlTextWriterEndElement (odt->content_wtr);
+ xmlTextWriterEndElement (odt->content_wtr);
+
+ xmlTextWriterEndElement (odt->content_wtr);
+}
+
static void
write_table (struct odt_driver *odt, const struct table_item *item)
{
for (i = 0; i < cell.n_contents; i++)
{
const struct cell_contents *contents = &cell.contents[i];
+ int j;
if (contents->text)
{
else
xmlTextWriterWriteAttribute (odt->content_wtr, _xml("text:style-name"), _xml("Table_20_Contents"));
- if (strchr (contents->text, '\n'))
- {
- char *line = xstrdup (contents->text);
- write_xml_with_line_breaks (odt->content_wtr, line);
- free (line);
- }
- else
- xmlTextWriterWriteString (odt->content_wtr, _xml(contents->text));
+ write_xml_with_line_breaks (odt, contents->text);
+
+ for (j = 0; j < contents->n_footnotes; j++)
+ write_footnote (odt, contents->footnotes[j]);
xmlTextWriterEndElement (odt->content_wtr); /* text:p */
}
else if (contents->table)
- {
- write_table (odt, contents->table);
- continue;
- }
+ write_table (odt, contents->table);
+
}
xmlTextWriterEndElement (odt->content_wtr); /* table:table-cell */
}
#include "libpspp/hash-functions.h"
#include "libpspp/hmap.h"
#include "output/render.h"
+#include "output/tab.h"
#include "output/table-item.h"
#include "output/table.h"
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
int overflow[TABLE_N_AXES][2];
};
-/* Returns a hash value for (X,Y). */
+/* Returns a hash value for (,Y). */
static unsigned int
-hash_overflow (int x, int y)
+hash_cell (int x, int y)
{
return hash_int (x + (y << 16), 0);
}
const struct render_overflow *of;
HMAP_FOR_EACH_WITH_HASH (of, struct render_overflow, node,
- hash_overflow (x, y), &page->overflows)
+ hash_cell (x, y), &page->overflows)
if (x == of->d[H] && y == of->d[V])
return of;
}
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
}
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;
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;
}
/* Calculate minimum and maximum widths of cells that do not
- span multiple columns. */
+ span multiple columns. Assign footnote markers. */
+ hmap_init (&footnotes);
+ footnote_idx = 0;
for (i = 0; i < 2; i++)
columns[i] = xzalloc (nc * sizeof *columns[i]);
for (y = 0; y < nr; y++)
struct table_cell cell;
table_get_cell (table, x, y, &cell);
- if (y == cell.d[V][0] && table_cell_colspan (&cell) == 1)
+ if (y == cell.d[V][0])
{
- int w[2];
- int i;
+ int n;
- 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];
+ if (table_cell_colspan (&cell) == 1)
+ {
+ int w[2];
+ int i;
+
+ params->measure_cell_width (params->aux, &cell, footnote_idx,
+ &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);
{
int w[2];
- params->measure_cell_width (params->aux, &cell, &w[MIN], &w[MAX]);
+ params->measure_cell_width (params->aux, &cell,
+ find_footnote_idx (&cell, &footnotes),
+ &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));
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, w);
+ int h = params->measure_cell_height (
+ params->aux, &cell, find_footnote_idx (&cell, &footnotes), w);
if (h > r->unspanned)
r->unspanned = r->width = h;
}
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, w);
+ int h = params->measure_cell_height (
+ params->aux, &cell, find_footnote_idx (&cell, &footnotes), w);
distribute_spanned_width (h, &rows[cell.d[V][0]], rules[V],
table_cell_rowspan (&cell));
}
}
}
+ hmap_swap (&page->footnotes, &footnotes);
+ hmap_destroy (&footnotes);
+ page->n_footnotes = footnote_idx;
+
free (rules[H]);
free (rules[V]);
}
}
- page->params->draw_cell (page->params->aux, cell, bb, clip);
+ page->params->draw_cell (page->params->aux, cell,
+ find_footnote_idx (cell, &page->footnotes), bb, clip);
}
/* Draws the cells of PAGE indicated in BB. */
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, w, pixel);
+ page->params->aux, &cell,
+ find_footnote_idx (&cell, &page->footnotes), w, pixel);
x = cell.d[H][1];
table_cell_free (&cell);
struct render_pager
{
- int width;
+ const struct render_params *params;
+
struct render_page **pages;
- size_t cur_page, n_pages;
+ size_t n_pages, allocated_pages;
+
+ size_t cur_page;
struct render_break x_break;
struct render_break y_break;
};
static void
-render_pager_add_table (struct render_pager *p, struct table *table,
- const struct render_params *params,
- size_t *allocated_pages)
+render_pager_add_table (struct render_pager *p, struct table *table)
{
- if (p->n_pages >= *allocated_pages)
- p->pages = x2nrealloc (p->pages, allocated_pages, sizeof *p->pages);
- p->pages[p->n_pages++] = render_page_create (params, table);
+ if (p->n_pages >= p->allocated_pages)
+ p->pages = x2nrealloc (p->pages, &p->allocated_pages, sizeof *p->pages);
+ p->pages[p->n_pages++] = render_page_create (p->params, table);
}
static void
render_break_init_empty (&p->y_break);
}
+static void
+add_footnote_page (struct render_pager *p, const struct render_page *body)
+{
+ 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)
+ return;
+
+ t = tab_create (2, body->n_footnotes);
+ for (y = 0; y < nr; y++)
+ for (x = 0; x < nc; )
+ {
+ struct table_cell cell;
+
+ 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);
+ }
+ render_pager_add_table (p, &t->table);
+}
+
/* Creates and returns a new render_pager for rendering TABLE_ITEM on the
device with the given PARAMS. */
struct render_pager *
const struct table_item *table_item)
{
struct render_pager *p;
- size_t allocated_pages = 0;
const char *caption;
p = xzalloc (sizeof *p);
- p->width = params->size[H];
+ p->params = params;
caption = table_item_get_caption (table_item);
if (caption)
- render_pager_add_table (p, table_from_string (TAB_LEFT, caption), params,
- &allocated_pages);
- render_pager_add_table (p, table_ref (table_item_get_table (table_item)), params,
- &allocated_pages);
+ render_pager_add_table (p, table_from_string (TAB_LEFT, caption));
+ render_pager_add_table (p, table_ref (table_item_get_table (table_item)));
+ add_footnote_page (p, p->pages[p->n_pages - 1]);
render_pager_start_page (p);
}
else
render_break_init (&p->y_break,
- render_break_next (&p->x_break, p->width), V);
+ render_break_next (&p->x_break, p->params->size[H]), V);
}
return true;
}
const struct table_cell *);
/* Creates and returns a new render_page whose contents are a subregion of
- PAGE's contents. The new render_page includes cells Z0 through Z1 along
- AXIS, plus any headers on AXIS.
+ PAGE's contents. The new render_page includes cells Z0 through Z1
+ (exclusive) along AXIS, plus any headers on AXIS.
If P0 is nonzero, then it is a number of pixels to exclude from the left or
top (according to AXIS) of cell Z0. Similarly, P1 is a number of pixels to
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;
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;
}
of = xzalloc (sizeof *of);
cell_to_subpage (s, cell, of->d);
hmap_insert (&s->subpage->overflows, &of->node,
- hash_overflow (of->d[H], of->d[V]));
+ hash_cell (of->d[H], of->d[V]));
old = find_overflow (s->page, cell->d[H][0], cell->d[V][0]);
if (old != NULL)
RENDER_N_LINES
};
+/* Parameters for rendering a table_item to a device.
+
+
+ Coordinate system
+ =================
+
+ The rendering code assumes that larger 'x' is to the right and larger 'y'
+ toward the bottom of the page.
+
+ The rendering code assumes that the table being rendered has its upper left
+ corner at (0,0) in device coordinates. This is usually not the case from
+ the driver's perspective, so the driver should expect to apply its own
+ offset to coordinates passed to callback functions.
+
+
+ Callback functions
+ ==================
+
+ 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
{
/* Measures CELL's width. Stores in *MIN_WIDTH the minimum width required
to avoid splitting a single word across multiple lines (normally, this
is the width of the longest word in the cell) and in *MAX_WIDTH the
- minimum width required to avoid line breaks other than at new-lines. */
+ 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 width);
+ int footnote_idx, 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,
Optional. If NULL, the rendering engine assumes that all breakpoints
are acceptable. */
int (*adjust_break) (void *aux, const struct table_cell *cell,
- int width, int height);
+ int footnote_idx, 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
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. */
struct table_item *subtable;
}
u;
+
+ size_t n_footnotes;
+ char **footnotes;
};
static const struct table_class tab_table_class;
j->d[TABLE_VERT][0] = y1 + table->row_ofs;
j->d[TABLE_HORZ][1] = ++x2 + table->col_ofs;
j->d[TABLE_VERT][1] = ++y2 + table->row_ofs;
+ j->n_footnotes = 0;
+ j->footnotes = NULL;
{
void **cc = &table->cc[x1 + y1 * table->cf];
add_joined_cell (table, x1, y1, x2, y2, opt)->u.text = s;
}
+void
+tab_footnote (struct tab_table *table, int x, int y, const char *format, ...)
+{
+ 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];
+ else
+ {
+ char *text = table->cc[index];
+
+ j = add_joined_cell (table, x, y, x, y, table->ct[index]);
+ j->u.text = text ? text : xstrdup ("");
+ }
+
+ j->footnotes = xrealloc (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);
+}
+
static void
subtable_unref (void *subtable)
{
cell->inline_contents.options = opt;
cell->inline_contents.table = NULL;
+ cell->inline_contents.n_footnotes = 0;
cell->destructor = NULL;
if (opt & TAB_JOIN)
cell->inline_contents.text = jc->u.text;
}
+ cell->inline_contents.footnotes = jc->footnotes;
+ cell->inline_contents.n_footnotes = jc->n_footnotes;
+
cell->d[TABLE_HORZ][0] = jc->d[TABLE_HORZ][0];
cell->d[TABLE_HORZ][1] = jc->d[TABLE_HORZ][1];
cell->d[TABLE_VERT][0] = jc->d[TABLE_VERT][0];
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);
+
void tab_subtable (struct tab_table *, int x1, int y1, int x2, int y2,
unsigned opt, struct table_item *subtable);
void tab_subtable_bare (struct tab_table *, int x1, int y1, int x2, int y2,
/* PSPP - a program for statistical analysis.
- Copyright (C) 2009, 2011, 2013 Free Software Foundation, Inc.
+ Copyright (C) 2009, 2011, 2013, 2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
cell->n_contents = 1;
cell->inline_contents.options = TAB_RIGHT;
cell->inline_contents.table = NULL;
+ cell->inline_contents.n_footnotes = 0;
if (tc->heading != NULL)
{
if (y == 0)
/* Exactly one of these must be nonnull. */
char *text; /* A paragraph of text. */
struct table_item *table; /* A table nested within the cell. */
+
+ /* Optional footnote(s). */
+ char **footnotes;
+ size_t n_footnotes;
};
/* A cell in a table. */
cell->inline_contents.options = ts->options;
cell->inline_contents.text = ts->string;
cell->inline_contents.table = NULL;
+ cell->inline_contents.n_footnotes = 0;
cell->n_contents = 1;
cell->destructor = NULL;
}
cell->inline_contents.options = TAB_LEFT;
cell->inline_contents.text = NULL;
cell->inline_contents.table = tn->inner;
+ cell->inline_contents.n_footnotes = 0;
cell->n_contents = 1;
cell->destructor = NULL;
}
/* --pdf: Also render PDF output. */
static int render_pdf;
+/* --csv: Also render CSV output. */
+static int render_csv;
+
/* ASCII driver, for ASCII driver test mode. */
static struct output_driver *ascii_driver;
}
#endif
+ /* Render to <base>.csv. */
+ if (render_csv)
+ {
+ string_map_clear (&options);
+ string_map_insert_nocopy (&options, xstrdup ("output-file"),
+ xasprintf ("%s.csv", output_base));
+ driver = output_driver_create (&options);
+ if (driver == NULL)
+ exit (EXIT_FAILURE);
+ output_driver_register (driver);
+ }
+
/* Render to <base>.odt. */
string_map_replace_nocopy (&options, xstrdup ("output-file"),
xasprintf ("%s.odt", output_base));
{"no-txt", no_argument, &render_txt, 0},
{"no-stdout", no_argument, &render_stdout, 0},
{"pdf", no_argument, &render_pdf, 1},
+ {"csv", no_argument, &render_csv, 1},
{"output", required_argument, NULL, 'o'},
{"help", no_argument, NULL, OPT_HELP},
{NULL, 0, NULL, 0},
table_item_create (table, NULL));
}
else
- tab_joint_text (tab, c, r, c + cs - 1, r + rs - 1, opt, text);
+ {
+ char *pos = text;
+ char *content;
+ int i;
+
+ for (i = 0; (content = strsep (&pos, "#")) != NULL; i++)
+ if (!i)
+ tab_joint_text (tab, c, r, c + cs - 1, r + rs - 1, opt,
+ content);
+ else
+ tab_footnote (tab, c, r, content);
+ }
}
return &tab->table;
])
AT_CLEANUP
+AT_SETUP([joined rows and columns (with footnotes)])
+AT_KEYWORDS([render rendering footnote])
+AT_DATA([input], [3 3
+1*2 @abc#Approximation.
+2*1 @d\ne\nf#This is a very long footnote that will have to wrap from one line to the next. Let's see if the rendering engine does it acceptably.
+2*1 @g\nh\ni#One#Two#Three
+@j
+1*2 @klm
+])
+AT_CHECK([render-test --csv input], [0],
+[[+------------+----+
+| abc[a]| d|
++----------+-+ e|
+| g|j|f[b]|
+| h+-+----+
+|i[c][d][e]| klm|
++----------+------+
+[a] Approximation.
+[b] This is a very long footnote that will have to wrap from one line to the
+ next. Let's see if the rendering engine does it acceptably.
+[c] One
+[d] Two
+[e] Three
+]])
+AT_CHECK([cat render.csv], [0],
+[[abc[a],,"d
+e
+f[b]"
+"g
+h
+i[c][d][e]",j,
+,klm,
+
+Footnotes:
+a,Approximation.
+b,This is a very long footnote that will have to wrap from one line to the next. Let's see if the rendering engine does it acceptably.
+c,One
+d,Two
+e,Three
+]])
+AT_CLEANUP
+
AT_SETUP([6x6, joined rows and columns])
AT_KEYWORDS([render rendering])
AT_DATA([input], [WEAVE_6X6])