From 6fd821e557390a8f86f37c192fd1a4c4e057c9c3 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 3 Dec 2018 20:00:44 -0800 Subject: [PATCH] table: Make table cells have exactly one piece of content again. --- src/output/ascii.c | 101 ++++++++++------------------------ src/output/cairo.c | 54 +++++------------- src/output/csv.c | 33 ++++------- src/output/html.c | 45 ++++++--------- src/output/odt.c | 46 +++++++--------- src/output/render.c | 4 +- src/output/tab.c | 24 ++------ src/output/table-casereader.c | 10 ++-- src/output/table-provider.h | 33 +++-------- src/output/table.c | 24 +++----- 10 files changed, 120 insertions(+), 254 deletions(-) diff --git a/src/output/ascii.c b/src/output/ascii.c index 8ac2b189ab..330c113042 100644 --- a/src/output/ascii.c +++ b/src/output/ascii.c @@ -576,9 +576,7 @@ 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_contents != 1 - || cell->contents[0].n_footnotes - || strchr (cell->contents[0].text, ' ')) + if (cell->n_footnotes || strchr (cell->text, ' ')) { bb[H][1] = 1; ascii_layout_cell (a, cell, bb, clip, min_width, &h); @@ -771,39 +769,34 @@ text_draw (struct ascii_driver *a, unsigned int options, } static char * -add_footnote_markers (const char *text, const struct cell_contents *contents) +add_footnote_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 < contents->n_footnotes; i++) - ds_put_format (&s, "[%s]", contents->footnotes[i]->marker); + for (size_t i = 0; i < cell->n_footnotes; i++) + ds_put_format (&s, "[%s]", cell->footnotes[i]->marker); return ds_steal_cstr (&s); } -static int -ascii_layout_cell_text (struct ascii_driver *a, - const struct cell_contents *contents, - bool bold, bool underline, - int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2], - int *widthp) +static void +ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell, + int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2], + int *widthp, int *heightp) { - char *breaks; - int bb_width; - size_t pos; - - int y = bb[V][0]; + *widthp = 0; + *heightp = 0; /* Get the basic textual contents. */ - const char *plain_text = (contents->options & TAB_MARKUP - ? output_get_text_from_markup (contents->text) - : contents->text); + const char *plain_text = (cell->options & TAB_MARKUP + ? output_get_text_from_markup (cell->text) + : cell->text); /* Append footnote markers if any. */ const char *text; - if (contents->n_footnotes) + if (cell->n_footnotes) { - text = add_footnote_markers (plain_text, contents); - if (plain_text != contents->text) + text = add_footnote_markers (plain_text, cell); + if (plain_text != cell->text) free (CONST_CAST (char *, plain_text)); } else @@ -813,20 +806,20 @@ ascii_layout_cell_text (struct ascii_driver *a, size_t length = strlen (text); if (!length) { - if (text != contents->text) + if (text != cell->text) free (CONST_CAST (char *, text)); - return y; + return; } - breaks = xmalloc (length + 1); + char *breaks = xmalloc (length + 1); 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); - pos = 0; - bb_width = bb[H][1] - bb[H][0]; - for (y = bb[V][0]; y < bb[V][1] && pos < length; y++) + size_t pos = 0; + int bb_width = bb[H][1] - bb[H][0]; + for (int y = bb[V][0]; y < bb[V][1] && pos < length; y++) { const uint8_t *line = CHAR_CAST (const uint8_t *, text + pos); const char *b = breaks + pos; @@ -879,7 +872,7 @@ ascii_layout_cell_text (struct ascii_driver *a, width -= ofs - graph_ofs; /* Draw text. */ - text_draw (a, contents->options, bold, underline, + text_draw (a, cell->options, cell->style->bold, cell->style->underline, bb, clip, y, line, graph_ofs, width); /* If a new-line ended the line, just skip the new-line. Otherwise, skip @@ -892,45 +885,13 @@ ascii_layout_cell_text (struct ascii_driver *a, if (width > *widthp) *widthp = width; + ++*heightp; pos += ofs; } free (breaks); - if (text != contents->text) + if (text != cell->text) free (CONST_CAST (char *, text)); - - return y; -} - -static void -ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell, - int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2], - int *widthp, int *heightp) -{ - int bb[TABLE_N_AXES][2]; - size_t i; - - *widthp = 0; - *heightp = 0; - - memcpy (bb, bb_, sizeof bb); - for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++) - { - const struct cell_contents *contents = &cell->contents[i]; - - /* Put a blank line between contents. */ - if (i > 0) - { - bb[V][0]++; - if (bb[V][0] >= bb[V][1]) - break; - } - - bb[V][0] = ascii_layout_cell_text (a, contents, cell->style->bold, - cell->style->underline, - bb, clip, widthp); - } - *heightp = bb[V][0] - bb_[V][0]; } void @@ -944,18 +905,14 @@ ascii_test_write (struct output_driver *driver, if (a->file == NULL && !ascii_open_page (a)) return; - struct cell_contents contents = { - .options = TAB_LEFT, - .text = CONST_CAST (char *, s), - }; - struct cell_style cell_style = { + struct cell_style style = { .bold = bold, .underline = underline, }; struct table_cell cell = { - .contents = &contents, - .n_contents = 1, - .style = &cell_style, + .options = TAB_LEFT, + .text = CONST_CAST (char *, s), + .style = &style, }; bb[TABLE_HORZ][0] = x; diff --git a/src/output/cairo.c b/src/output/cairo.c index 1ff7c2a214..d00a66be68 100644 --- a/src/output/cairo.c +++ b/src/output/cairo.c @@ -1202,20 +1202,19 @@ markup_escape (const char *in, struct string *out) } static int -xr_layout_cell_text (struct xr_driver *xr, - const struct cell_contents *contents, - const struct cell_style *style, +xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell, int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2], int *widthp, int *brk) { - unsigned int options = contents->options; + const struct cell_style *style = cell->style; + unsigned int options = cell->options; int w, h; struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED] : options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS] : &xr->fonts[XR_FONT_PROPORTIONAL]); struct xr_font local_font; - if (style->font) + if (cell->style->font) { PangoFontDescription *desc = parse_font ( style->font, @@ -1233,13 +1232,13 @@ xr_layout_cell_text (struct xr_driver *xr, } int footnote_adjustment; - if (contents->n_footnotes == 0) + if (cell->n_footnotes == 0) footnote_adjustment = 0; - else if (contents->n_footnotes == 1 && (options & TAB_HALIGN) == TAB_RIGHT) + else if (cell->n_footnotes == 1 && (options & TAB_HALIGN) == TAB_RIGHT) { PangoAttrList *attrs; - const char *marker = contents->footnotes[0]->marker; + const char *marker = cell->footnotes[0]->marker; pango_layout_set_text (font->layout, marker, strlen (marker)); attrs = pango_attr_list_new (); @@ -1254,7 +1253,7 @@ xr_layout_cell_text (struct xr_driver *xr, footnote_adjustment = px_to_xr (style->margin[H][1]); struct string tmp = DS_EMPTY_INITIALIZER; - const char *text = contents->text; + const char *text = cell->text; /* 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 @@ -1295,12 +1294,12 @@ xr_layout_cell_text (struct xr_driver *xr, } size_t initial_length = ds_length (&tmp); - for (size_t i = 0; i < contents->n_footnotes; i++) + for (size_t i = 0; i < cell->n_footnotes; i++) { if (i) ds_put_byte (&tmp, ','); - const char *marker = contents->footnotes[i]->marker; + const char *marker = cell->footnotes[i]->marker; if (options & TAB_MARKUP) markup_escape (marker, &tmp); else @@ -1447,23 +1446,16 @@ xr_layout_cell_text (struct xr_driver *xr, pango_font_description_free (font->desc); } - return bb[V][0] + h; + return h; } static void xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell, - int bb_[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2], + int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2], int *width, int *height, int *brk) { - int bb[TABLE_N_AXES][2]; - size_t i; - *width = 0; *height = 0; - if (brk) - *brk = 0; - - memcpy (bb, bb_, sizeof bb); /* If enabled, draws a blue rectangle around the cell extents, which can be useful for debugging layout. */ @@ -1482,25 +1474,9 @@ xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell, } } - for (i = 0; i < cell->n_contents && bb[V][0] < bb[V][1]; i++) - { - const struct cell_contents *contents = &cell->contents[i]; - - if (brk) - *brk = bb[V][0]; - if (i > 0) - { - bb[V][0] += xr->char_height / 2; - if (bb[V][0] >= bb[V][1]) - break; - if (brk) - *brk = bb[V][0]; - } - - bb[V][0] = xr_layout_cell_text (xr, contents, cell->style, bb, clip, - width, brk); - } - *height = bb[V][0] - bb_[V][0]; + if (brk) + *brk = bb[V][0]; + *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk); } struct output_driver_factory pdf_driver_factory = diff --git a/src/output/csv.c b/src/output/csv.c index 17c26a0626..0067c61202 100644 --- a/src/output/csv.c +++ b/src/output/csv.c @@ -211,33 +211,22 @@ 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.n_contents == 1 - && cell.contents[0].text != NULL - && cell.contents[0].n_footnotes == 0) - csv_output_field (csv, cell.contents[0].text); + else if (!(cell.options & TAB_MARKUP) && !cell.n_footnotes) + csv_output_field (csv, cell.text); else { - struct string s; - size_t i; + struct string s = DS_EMPTY_INITIALIZER; - ds_init_empty (&s); - for (i = 0; i < cell.n_contents; i++) + if (cell.options & TAB_MARKUP) { - const struct cell_contents *c = &cell.contents[i]; - - if (i > 0) - ds_put_cstr (&s, "\n\n"); - - if (c->options & TAB_MARKUP) - { - char *t = output_get_text_from_markup (c->text); - ds_put_cstr (&s, t); - free (t); - } - else - ds_put_cstr (&s, c->text); - csv_format_footnotes (c->footnotes, c->n_footnotes, &s); + char *t = output_get_text_from_markup (cell.text); + ds_put_cstr (&s, t); + free (t); } + else + ds_put_cstr (&s, cell.text); + + csv_format_footnotes (cell.footnotes, cell.n_footnotes, &s); csv_output_field (csv, ds_cstr (&s)); ds_destroy (&s); } diff --git a/src/output/html.c b/src/output/html.c index f5639cc0ab..959519e46e 100644 --- a/src/output/html.c +++ b/src/output/html.c @@ -483,7 +483,6 @@ html_output_table (struct html_driver *html, const struct table_item *item) fputs (" \n", html->file); for (x = 0; x < table_nc (t); ) { - const struct cell_contents *c; struct table_cell cell; const char *tag; bool is_header; @@ -502,16 +501,12 @@ html_output_table (struct html_driver *html, const struct table_item *item) tag = is_header ? "TH" : "TD"; fprintf (html->file, " <%s", tag); - int halign = (cell.n_contents > 0 - ? cell.contents[0].options & TAB_HALIGN - : TAB_LEFT); + int halign = cell.options & TAB_HALIGN; if (halign != TAB_LEFT) fprintf (html->file, " ALIGN=\"%s\"", halign == TAB_RIGHT ? "RIGHT" : "CENTER"); - int valign = (cell.n_contents > 0 - ? cell.contents[0].options & TAB_VALIGN - : TAB_LEFT); + int valign = cell.options & TAB_VALIGN; if (valign != TAB_TOP) fprintf (html->file, " ALIGN=\"%s\"", valign == TAB_BOTTOM ? "BOTTOM" : "MIDDLE"); @@ -557,28 +552,24 @@ html_output_table (struct html_driver *html, const struct table_item *item) putc ('>', html->file); /* Output cell contents. */ - for (c = cell.contents; c < &cell.contents[cell.n_contents]; c++) + const char *s = cell.text; + if (cell.options & TAB_EMPH) + fputs ("", html->file); + if (cell.options & TAB_FIX) { - const char *s = c->text; - - if (c->options & TAB_EMPH) - fputs ("", html->file); - if (c->options & TAB_FIX) - { - fputs ("", html->file); - escape_string (html->file, s, strlen (s), " ", "
"); - fputs ("
", html->file); - } - else - { - s += strspn (s, CC_SPACES); - escape_string (html->file, s, strlen (s), " ", "
"); - } - if (c->options & TAB_EMPH) - fputs ("
", html->file); - - html_put_footnote_markers (html, c->footnotes, c->n_footnotes); + fputs ("", html->file); + escape_string (html->file, s, strlen (s), " ", "
"); + fputs ("
", html->file); } + else + { + s += strspn (s, CC_SPACES); + escape_string (html->file, s, strlen (s), " ", "
"); + } + if (cell.options & TAB_EMPH) + fputs ("
", html->file); + + html_put_footnote_markers (html, cell.footnotes, cell.n_footnotes); /* Output or . */ fprintf (html->file, "\n", tag); diff --git a/src/output/odt.c b/src/output/odt.c index f0143515e0..60a5b7fd4b 100644 --- a/src/output/odt.c +++ b/src/output/odt.c @@ -496,7 +496,6 @@ write_table (struct odt_driver *odt, const struct table_item *item) for (c = 0 ; c < table_nc (tab) ; ++c) { struct table_cell cell; - size_t i; table_get_cell (tab, c, r, &cell); @@ -518,34 +517,27 @@ write_table (struct odt_driver *odt, const struct table_item *item) odt->content_wtr, _xml("table:number-rows-spanned"), "%d", rowspan); - for (i = 0; i < cell.n_contents; i++) + xmlTextWriterStartElement (odt->content_wtr, _xml("text:p")); + + if ( r < table_ht (tab) || c < table_hl (tab) ) + xmlTextWriterWriteAttribute (odt->content_wtr, _xml("text:style-name"), _xml("Table_20_Heading")); + else + xmlTextWriterWriteAttribute (odt->content_wtr, _xml("text:style-name"), _xml("Table_20_Contents")); + + if (cell.options & TAB_MARKUP) { - const struct cell_contents *contents = &cell.contents[i]; - int j; - - xmlTextWriterStartElement (odt->content_wtr, _xml("text:p")); - - if ( r < table_ht (tab) || c < table_hl (tab) ) - xmlTextWriterWriteAttribute (odt->content_wtr, _xml("text:style-name"), _xml("Table_20_Heading")); - else - xmlTextWriterWriteAttribute (odt->content_wtr, _xml("text:style-name"), _xml("Table_20_Contents")); - - if (contents->options & TAB_MARKUP) - { - /* XXX */ - char *s = output_get_text_from_markup ( - contents->text); - write_xml_with_line_breaks (odt, s); - free (s); - } - else - 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 */ + /* XXX */ + char *s = output_get_text_from_markup (cell.text); + write_xml_with_line_breaks (odt, s); + free (s); } + else + write_xml_with_line_breaks (odt, cell.text); + + for (int i = 0; i < cell.n_footnotes; i++) + write_footnote (odt, cell.footnotes[i]); + + xmlTextWriterEndElement (odt->content_wtr); /* text:p */ xmlTextWriterEndElement (odt->content_wtr); /* table:table-cell */ } else diff --git a/src/output/render.c b/src/output/render.c index 239d0c68ad..61b03f0285 100644 --- a/src/output/render.c +++ b/src/output/render.c @@ -983,9 +983,7 @@ render_cell (const struct render_page *page, const int ofs[TABLE_N_AXES], bb[V][0] = clip[V][0] = ofs[V] + page->cp[V][cell->d[V][0] * 2 + 1]; bb[V][1] = clip[V][1] = ofs[V] + page->cp[V][cell->d[V][1] * 2]; - int valign = (cell->n_contents - ? cell->contents->options & TAB_VALIGN - : TAB_TOP); + int valign = cell->options & TAB_VALIGN; if (valign != TAB_TOP) { int height = page->params->measure_cell_height ( diff --git a/src/output/tab.c b/src/output/tab.c index a9487b375b..8eab07e6f1 100644 --- a/src/output/tab.c +++ b/src/output/tab.c @@ -814,8 +814,8 @@ tab_get_cell (const struct table *table, int x, int y, unsigned short opt = t->ct[index]; const void *cc = t->cc[index]; - cell->inline_contents.options = opt; - cell->inline_contents.n_footnotes = 0; + cell->options = opt; + cell->n_footnotes = 0; cell->destructor = NULL; int style_idx = (opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT; @@ -826,12 +826,10 @@ tab_get_cell (const struct table *table, int x, int y, if (opt & TAB_JOIN) { const struct tab_joined_cell *jc = cc; - cell->contents = &cell->inline_contents; - cell->n_contents = 1; - cell->inline_contents.text = jc->u.text; + cell->text = jc->u.text; - cell->inline_contents.footnotes = jc->footnotes; - cell->inline_contents.n_footnotes = jc->n_footnotes; + cell->footnotes = jc->footnotes; + cell->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]; @@ -847,17 +845,7 @@ tab_get_cell (const struct table *table, int x, int y, cell->d[TABLE_HORZ][1] = x + 1; cell->d[TABLE_VERT][0] = y; cell->d[TABLE_VERT][1] = y + 1; - if (cc != NULL) - { - cell->contents = &cell->inline_contents; - cell->n_contents = 1; - cell->inline_contents.text = CONST_CAST (char *, cc); - } - else - { - cell->contents = NULL; - cell->n_contents = 0; - } + cell->text = CONST_CAST (char *, cc ? cc : ""); } } diff --git a/src/output/table-casereader.c b/src/output/table-casereader.c index ecd85a1df4..018fa6328e 100644 --- a/src/output/table-casereader.c +++ b/src/output/table-casereader.c @@ -110,16 +110,14 @@ table_casereader_get_cell (const struct table *t, int x, int y, cell->d[TABLE_HORZ][1] = x + 1; cell->d[TABLE_VERT][0] = y; cell->d[TABLE_VERT][1] = y + 1; - cell->contents = &cell->inline_contents; - cell->n_contents = 1; - cell->inline_contents.options = TAB_RIGHT; - cell->inline_contents.n_footnotes = 0; + cell->options = TAB_RIGHT; + cell->n_footnotes = 0; if (tc->heading != NULL) { if (y == 0) { s = xstrdup (tc->heading); - cell->inline_contents.text = s; + cell->text = s; cell->destructor = free_string; cell->destructor_aux = s; return; @@ -135,7 +133,7 @@ table_casereader_get_cell (const struct table *t, int x, int y, s = data_out (case_data_idx (c, 0), UTF8, &tc->format); case_unref (c); } - cell->inline_contents.text = s; + cell->text = s; cell->destructor = free_string; cell->destructor_aux = s; } diff --git a/src/output/table-provider.h b/src/output/table-provider.h index 1d21cdfd5c..14c3656e4a 100644 --- a/src/output/table-provider.h +++ b/src/output/table-provider.h @@ -31,20 +31,6 @@ struct footnote struct cell_style *style; }; -/* An item of contents within a table cell. */ -struct cell_contents - { - unsigned int options; /* TAB_*. */ - char *text; /* A paragraph of text. */ - - /* Optional footnote(s). */ - const struct footnote **footnotes; - size_t n_footnotes; - }; - -void cell_contents_format_footnote_markers (const struct cell_contents *, - struct string *); - struct cell_color { uint8_t r, g, b; @@ -105,18 +91,12 @@ struct table_cell or both. */ int d[TABLE_N_AXES][2]; - /* The cell's contents. - - Most table cells contain only one item (a paragraph of text), but cells - are allowed to be empty (n_contents == 0) or contain a nested table, or - multiple items. + unsigned int options; /* TAB_*. */ + char *text; /* A paragraph of text. */ - 'inline_contents' provides a place to store a single item to handle the - common case. - */ - const struct cell_contents *contents; - size_t n_contents; - struct cell_contents inline_contents; + /* Optional footnote(s). */ + const struct footnote **footnotes; + size_t n_footnotes; const struct cell_style *style; @@ -127,6 +107,9 @@ struct table_cell void table_cell_free (struct table_cell *); +void table_cell_format_footnote_markers (const struct table_cell *, + struct string *); + /* Returns the number of columns that CELL spans. This is 1 for an ordinary cell and greater than one for a cell that joins multiple columns. */ static inline int diff --git a/src/output/table.c b/src/output/table.c index c8dfcbb0f8..b83d66fb20 100644 --- a/src/output/table.c +++ b/src/output/table.c @@ -225,14 +225,14 @@ table_get_rule (const struct table *table, enum table_axis axis, int x, int y, } void -cell_contents_format_footnote_markers (const struct cell_contents *c, - struct string *s) +table_cell_format_footnote_markers (const struct table_cell *cell, + struct string *s) { - for (size_t i = 0; i < c->n_footnotes; i++) + for (size_t i = 0; i < cell->n_footnotes; i++) { if (i) ds_put_byte (s, ','); - ds_put_cstr (s, c->footnotes[i]->marker); + ds_put_cstr (s, cell->footnotes[i]->marker); } } @@ -274,12 +274,8 @@ table_collect_footnotes (const struct table_item *item, 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); - } + footnotes = add_footnotes (cell.footnotes, cell.n_footnotes, + footnotes, &allocated, &n); table_cell_free (&cell); } } @@ -420,11 +416,9 @@ table_string_get_cell (const struct table *ts_, int x UNUSED, int y UNUSED, cell->d[TABLE_HORZ][1] = 1; cell->d[TABLE_VERT][0] = 0; cell->d[TABLE_VERT][1] = 1; - cell->contents = &cell->inline_contents; - cell->inline_contents.options = ts->options; - cell->inline_contents.text = ts->string; - cell->inline_contents.n_footnotes = 0; - cell->n_contents = 1; + cell->options = ts->options; + cell->text = ts->string; + cell->n_footnotes = 0; cell->destructor = NULL; } -- 2.30.2