From a7a300e7d9963ce9a26fd87e4db922ed74111731 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 26 Dec 2018 14:07:16 -0800 Subject: [PATCH] output: Support decimal and mixed alignment, --- src/output/ascii.c | 23 +++-- src/output/cairo.c | 141 +++++++++++++++----------- src/output/html.c | 22 +++-- src/output/render.c | 12 +-- src/output/tab.c | 36 ++++++- src/output/tab.h | 25 ++++- src/output/table-casereader.c | 8 +- src/output/table-item.c | 8 +- src/output/table-item.h | 4 +- src/output/table-provider.h | 46 +-------- src/output/table.c | 173 +++++++++++++++++++++++++++++--- src/output/table.h | 181 +++++++++++++++++++++++++--------- src/output/text-item.c | 16 +-- src/output/text-item.h | 3 +- 14 files changed, 493 insertions(+), 205 deletions(-) diff --git a/src/output/ascii.c b/src/output/ascii.c index 330c113042..c0218e6626 100644 --- a/src/output/ascii.c +++ b/src/output/ascii.c @@ -436,7 +436,7 @@ ascii_output_text (struct ascii_driver *a, const char *text) { struct table_item *table_item; - table_item = table_item_create (table_from_string (TAB_LEFT, text), + table_item = table_item_create (table_from_string (TABLE_HALIGN_LEFT, text), NULL, NULL); ascii_output_table_item (a, table_item); table_item_unref (table_item); @@ -631,7 +631,7 @@ ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n) } static void -text_draw (struct ascii_driver *a, unsigned int options, +text_draw (struct ascii_driver *a, enum table_halign halign, int options, bool bold, bool underline, int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2], int y, const uint8_t *string, int n, size_t width) @@ -645,15 +645,16 @@ text_draw (struct ascii_driver *a, unsigned int options, if (y < y0 || y >= y1) return; - switch (options & TAB_HALIGN) + switch (table_halign_interpret (halign, options & TAB_NUMERIC)) { - case TAB_LEFT: + case TABLE_HALIGN_LEFT: x = bb[H][0]; break; - case TAB_CENTER: + case TABLE_HALIGN_CENTER: x = (bb[H][0] + bb[H][1] - width + 1) / 2; break; - case TAB_RIGHT: + case TABLE_HALIGN_RIGHT: + case TABLE_HALIGN_DECIMAL: x = bb[H][1] - width; break; default: @@ -872,7 +873,9 @@ ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell, width -= ofs - graph_ofs; /* Draw text. */ - text_draw (a, cell->options, cell->style->bold, cell->style->underline, + text_draw (a, cell->style->cell_style.halign, cell->options, + cell->style->font_style.bold, + cell->style->font_style.underline, bb, clip, y, line, graph_ofs, width); /* If a new-line ended the line, just skip the new-line. Otherwise, skip @@ -905,9 +908,9 @@ ascii_test_write (struct output_driver *driver, if (a->file == NULL && !ascii_open_page (a)) return; - struct cell_style style = { - .bold = bold, - .underline = underline, + struct area_style style = { + .font_style.bold = bold, + .font_style.underline = underline, }; struct table_cell cell = { .options = TAB_LEFT, diff --git a/src/output/cairo.c b/src/output/cairo.c index 4584181d32..1dc8fe0966 100644 --- a/src/output/cairo.c +++ b/src/output/cairo.c @@ -408,6 +408,14 @@ xr_measure_fonts (cairo_t *cairo, const struct xr_font fonts[XR_N_FONTS], } } +static int +get_layout_height (PangoLayout *layout) +{ + int w, h; + pango_layout_get_size (layout, &w, &h); + return h; +} + static int xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font, const struct page_heading *ph, int page_number, @@ -426,10 +434,12 @@ xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font, pango_layout_set_markup (layout, markup, -1); free (markup); - pango_layout_set_alignment (layout, - (pp->halign == TAB_RIGHT ? PANGO_ALIGN_RIGHT - : pp->halign == TAB_LEFT ? PANGO_ALIGN_LEFT - : PANGO_ALIGN_CENTER)); + pango_layout_set_alignment ( + layout, + (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT + : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER + : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT + : PANGO_ALIGN_RIGHT)); pango_layout_set_width (layout, xr_to_pango (width)); if (draw) { @@ -439,9 +449,7 @@ xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font, cairo_restore (cairo); } - int w, h; - pango_layout_get_size (layout, &w, &h); - y += pango_to_xr (h); + y += pango_to_xr (get_layout_height (layout)); } g_object_unref (G_OBJECT (layout)); @@ -1056,11 +1064,11 @@ xr_measure_cell_width (void *xr_, const struct table_cell *cell, xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL); if (*min_width > 0) - *min_width += px_to_xr (cell->style->margin[H][0] - + cell->style->margin[H][1]); + *min_width += px_to_xr (cell->style->cell_style.margin[H][0] + + cell->style->cell_style.margin[H][1]); if (*max_width > 0) - *max_width += px_to_xr (cell->style->margin[H][0] - + cell->style->margin[H][1]); + *max_width += px_to_xr (cell->style->cell_style.margin[H][0] + + cell->style->cell_style.margin[H][1]); } static int @@ -1072,13 +1080,14 @@ xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width) int w, h; bb[H][0] = 0; - bb[H][1] = width - px_to_xr (cell->style->margin[H][0] - + cell->style->margin[H][1]); + bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0] + + cell->style->cell_style.margin[H][1]); 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); - h += px_to_xr (cell->style->margin[V][0] + cell->style->margin[V][1]); + h += px_to_xr (cell->style->cell_style.margin[V][0] + + cell->style->cell_style.margin[V][1]); return h; } @@ -1107,9 +1116,9 @@ xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx, } xr_clip (xr, bg_clip); cairo_set_source_rgb (xr->cairo, - cell->style->bg[color_idx].r / 255., - cell->style->bg[color_idx].g / 255., - cell->style->bg[color_idx].b / 255.); + cell->style->font_style.bg[color_idx].r / 255., + cell->style->font_style.bg[color_idx].g / 255., + cell->style->font_style.bg[color_idx].b / 255.); fill_rectangle (xr, bb[H][0] - spill[H][0], bb[V][0] - spill[V][0], @@ -1119,14 +1128,14 @@ xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx, cairo_save (xr->cairo); cairo_set_source_rgb (xr->cairo, - cell->style->fg[color_idx].r / 255., - cell->style->fg[color_idx].g / 255., - cell->style->fg[color_idx].b / 255.); + cell->style->font_style.fg[color_idx].r / 255., + cell->style->font_style.fg[color_idx].g / 255., + cell->style->font_style.fg[color_idx].b / 255.); for (int axis = 0; axis < TABLE_N_AXES; axis++) { - bb[axis][0] += px_to_xr (cell->style->margin[axis][0]); - bb[axis][1] -= px_to_xr (cell->style->margin[axis][1]); + bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]); + bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]); } if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1]) xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk); @@ -1146,13 +1155,13 @@ xr_adjust_break (void *xr_, const struct table_cell *cell, return -1; bb[H][0] = 0; - bb[H][1] = width - px_to_xr (cell->style->margin[H][0] - + cell->style->margin[H][1]); + bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0] + + cell->style->cell_style.margin[H][1]); if (bb[H][1] <= 0) return 0; bb[V][0] = 0; - bb[V][1] = height - px_to_xr (cell->style->margin[V][0] - + cell->style->margin[V][1]); + bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0] + + cell->style->cell_style.margin[V][1]); clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0; xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk); return brk; @@ -1214,7 +1223,8 @@ 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) { - const struct cell_style *style = cell->style; + const struct font_style *font_style = &cell->style->font_style; + const struct cell_style *cell_style = &cell->style->cell_style; unsigned int options = cell->options; enum table_axis X = options & TAB_ROTATE ? V : H; @@ -1225,12 +1235,12 @@ xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell, : options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS] : &xr->fonts[XR_FONT_PROPORTIONAL]); struct xr_font local_font; - if (cell->style->typeface) + if (font_style->typeface) { PangoFontDescription *desc = parse_font ( - style->typeface, - style->size ? style->size * 1000 * 72 / 128 : 10000, - style->bold, style->italic); + font_style->typeface, + font_style->size ? font_style->size * 1000 * 72 / 128 : 10000, + font_style->bold, font_style->italic); if (desc) { PangoLayout *layout = pango_cairo_create_layout (xr->cairo); @@ -1242,30 +1252,26 @@ xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell, } } - int footnote_adjustment; - if (cell->n_footnotes == 0) - footnote_adjustment = 0; - else if (cell->n_footnotes == 1 && (options & TAB_HALIGN) == TAB_RIGHT) + const char *text = cell->text; + enum table_halign halign = table_halign_interpret ( + cell_style->halign, cell->options & TAB_NUMERIC); + if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE)) { - const char *marker = cell->footnotes[0]->marker; - pango_layout_set_text (font->layout, marker, strlen (marker)); + int margin_adjustment = -px_to_xr (cell_style->decimal_offset); - PangoAttrList *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); - - 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); + const char *decimal = strrchr (text, cell_style->decimal_char); + if (decimal) + { + pango_layout_set_text (font->layout, decimal, strlen (decimal)); + pango_layout_set_width (font->layout, -1); + margin_adjustment += get_layout_dimension (font->layout, H); + } - pango_layout_set_attributes (font->layout, NULL); + if (margin_adjustment < 0) + bb[H][1] += margin_adjustment; } - else - footnote_adjustment = px_to_xr (cell->style->margin[X][R]); struct string tmp = DS_EMPTY_INITIALIZER; - 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,8 +1301,28 @@ xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell, } } - if (footnote_adjustment) + if (cell->n_footnotes) { + 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_rise_new (7000)); + 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 @@ -1328,7 +1354,7 @@ xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell, pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp)); PangoAttrList *attrs = pango_attr_list_new (); - if (style->underline) + if (font_style->underline) pango_attr_list_insert (attrs, pango_attr_underline_new ( PANGO_UNDERLINE_SINGLE)); add_attr_with_start (attrs, pango_attr_rise_new (7000), initial_length); @@ -1345,7 +1371,7 @@ xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell, else pango_layout_set_text (font->layout, content, -1); - if (style->underline) + if (font_style->underline) { PangoAttrList *attrs = pango_attr_list_new (); pango_attr_list_insert (attrs, pango_attr_underline_new ( @@ -1356,11 +1382,10 @@ xr_layout_cell_text (struct xr_driver *xr, const struct table_cell *cell, } ds_destroy (&tmp); - pango_layout_set_alignment ( - font->layout, - ((options & TAB_HALIGN) == TAB_RIGHT ? PANGO_ALIGN_RIGHT - : (options & TAB_HALIGN) == TAB_LEFT ? PANGO_ALIGN_LEFT - : PANGO_ALIGN_CENTER)); + pango_layout_set_alignment (font->layout, + (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT + : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT + : PANGO_ALIGN_CENTER)); pango_layout_set_width ( font->layout, bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0])); @@ -1561,7 +1586,7 @@ xr_rendering_create_text (struct xr_driver *xr, const char *text, cairo_t *cr) struct table_item *table_item; struct xr_rendering *r; - table_item = table_item_create (table_from_string (TAB_LEFT, text), + table_item = table_item_create (table_from_string (TABLE_HALIGN_LEFT, text), NULL, NULL); r = xr_rendering_create (xr, &table_item->output_item, cr); table_item_unref (table_item); diff --git a/src/output/html.c b/src/output/html.c index 959519e46e..05fc08eb62 100644 --- a/src/output/html.c +++ b/src/output/html.c @@ -501,15 +501,23 @@ 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.options & TAB_HALIGN; - if (halign != TAB_LEFT) - fprintf (html->file, " ALIGN=\"%s\"", - halign == TAB_RIGHT ? "RIGHT" : "CENTER"); + enum table_halign halign = table_halign_interpret ( + cell.style->cell_style.halign, cell.options & TAB_NUMERIC); + if (halign != TABLE_HALIGN_LEFT) + { + fprintf (html->file, " ALIGN=\"%s\"", + (halign == TABLE_HALIGN_RIGHT ? "RIGHT" + : halign == TABLE_HALIGN_CENTER ? "CENTER" + : "CHAR")); + if (cell.style->cell_style.decimal_char) + fprintf (html->file, " CHAR=\"%c\"", + cell.style->cell_style.decimal_char); + } - int valign = cell.options & TAB_VALIGN; - if (valign != TAB_TOP) + if (cell.style->cell_style.valign != TABLE_VALIGN_TOP) fprintf (html->file, " ALIGN=\"%s\"", - valign == TAB_BOTTOM ? "BOTTOM" : "MIDDLE"); + (cell.style->cell_style.valign == TABLE_VALIGN_BOTTOM + ? "BOTTOM" : "MIDDLE")); colspan = table_cell_colspan (&cell); if (colspan > 1) diff --git a/src/output/render.c b/src/output/render.c index 61b03f0285..4e9a155095 100644 --- a/src/output/render.c +++ b/src/output/render.c @@ -507,7 +507,7 @@ measure_rule (const struct render_params *params, const struct table *table, /* Calculate maximum width of the rules that are present. */ int width = 0; - for (size_t i = 0; i < N_LINES; i++) + for (size_t i = 0; i < TABLE_N_STROKES; i++) if (rules & (1u << i)) width = MAX (width, params->line_widths[a][rule_to_render_type (i)]); return width; @@ -983,15 +983,15 @@ 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->options & TAB_VALIGN; - if (valign != TAB_TOP) + enum table_valign valign = cell->style->cell_style.valign; + if (valign != TABLE_VALIGN_TOP) { int height = page->params->measure_cell_height ( page->params->aux, cell, bb[H][1] - bb[H][0]); int extra = bb[V][1] - bb[V][0] - height; if (extra > 0) { - if (valign == TAB_MIDDLE) + if (valign == TABLE_VALIGN_CENTER) extra /= 2; bb[V][0] += extra; } @@ -1453,11 +1453,11 @@ add_text_page (struct render_pager *p, const struct table_item_text *t, return; struct tab_table *tab = tab_create (1, 1); - tab_text (tab, 0, 0, t->halign, t->content); + tab_text (tab, 0, 0, 0, t->content); for (size_t i = 0; i < t->n_footnotes; i++) tab_add_footnote (tab, 0, 0, t->footnotes[i]); if (t->style) - tab->styles[0] = cell_style_clone (tab->container, t->style); + tab->styles[0] = area_style_clone (tab->container, t->style); render_pager_add_table (p, &tab->table, min_width); } diff --git a/src/output/tab.c b/src/output/tab.c index 8eab07e6f1..ec00c43222 100644 --- a/src/output/tab.c +++ b/src/output/tab.c @@ -65,7 +65,7 @@ struct tab_joined_cell size_t n_footnotes; const struct footnote **footnotes; - const struct cell_style *style; + const struct area_style *style; }; static const struct table_class tab_table_class; @@ -621,7 +621,7 @@ tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, struct footnote * tab_create_footnote (struct tab_table *table, size_t idx, const char *content, - const char *marker, struct cell_style *style) + const char *marker, struct area_style *style) { struct footnote *f = pool_alloc (table->container, sizeof *f); f->idx = idx; @@ -657,7 +657,7 @@ tab_add_footnote (struct tab_table *table, int x, int y, void tab_add_style (struct tab_table *table, int x, int y, - const struct cell_style *style) + const struct area_style *style) { int index = x + y * table->cf; unsigned short opt = table->ct[index]; @@ -819,9 +819,37 @@ tab_get_cell (const struct table *table, int x, int y, cell->destructor = NULL; int style_idx = (opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT; - const struct cell_style *style = t->styles[style_idx]; + const struct area_style *style = t->styles[style_idx]; if (style) cell->style = style; + else + { + static const struct area_style styles[3][3] = { +#define S(H,V) [H][V] = { AREA_STYLE_INITIALIZER__, \ + .cell_style.halign = H, \ + .cell_style.valign = V } + S(TABLE_HALIGN_LEFT, TABLE_VALIGN_TOP), + S(TABLE_HALIGN_LEFT, TABLE_VALIGN_CENTER), + S(TABLE_HALIGN_LEFT, TABLE_VALIGN_BOTTOM), + S(TABLE_HALIGN_CENTER, TABLE_VALIGN_TOP), + S(TABLE_HALIGN_CENTER, TABLE_VALIGN_CENTER), + S(TABLE_HALIGN_CENTER, TABLE_VALIGN_BOTTOM), + S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_TOP), + S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_CENTER), + S(TABLE_HALIGN_RIGHT, TABLE_VALIGN_BOTTOM), + }; + + enum table_halign halign + = ((opt & TAB_HALIGN) == TAB_LEFT ? TABLE_HALIGN_LEFT + : (opt & TAB_HALIGN) == TAB_CENTER ? TABLE_HALIGN_CENTER + : TABLE_HALIGN_RIGHT); + enum table_valign valign + = ((opt & TAB_VALIGN) == TAB_TOP ? TABLE_VALIGN_TOP + : (opt & TAB_VALIGN) == TAB_CENTER ? TABLE_VALIGN_CENTER + : TABLE_VALIGN_BOTTOM); + + cell->style = &styles[halign][valign]; + } if (opt & TAB_JOIN) { diff --git a/src/output/tab.h b/src/output/tab.h index 40f871b12d..abd786119c 100644 --- a/src/output/tab.h +++ b/src/output/tab.h @@ -51,8 +51,23 @@ enum result_class n_RC }; -#define TAB_STYLE_MASK (7u << (TAB_FIRST_AVAILABLE + 3)) -#define TAB_STYLE_SHIFT (TAB_FIRST_AVAILABLE + 3) +#define TAB_STYLE_MASK (7u << (TAB_FIRST_AVAILABLE + 1)) +#define TAB_STYLE_SHIFT (TAB_FIRST_AVAILABLE + 1) + +enum + { + /* Horizontal alignment of cell contents. */ + TAB_RIGHT = 0 << (TAB_FIRST_AVAILABLE + 2), + TAB_LEFT = 1 << (TAB_FIRST_AVAILABLE + 2), + TAB_CENTER = 2 << (TAB_FIRST_AVAILABLE + 2), + TAB_HALIGN = 3 << (TAB_FIRST_AVAILABLE + 2), /* Alignment mask. */ + + /* Vertical alignment of cell contents. */ + TAB_TOP = 0 << (TAB_FIRST_AVAILABLE + 4), + TAB_MIDDLE = 1 << (TAB_FIRST_AVAILABLE + 4), + TAB_BOTTOM = 2 << (TAB_FIRST_AVAILABLE + 4), + TAB_VALIGN = 3 << (TAB_FIRST_AVAILABLE + 4), /* Alignment mask. */ + }; /* Rule masks. */ #define TAB_RULE_TYPE_MASK 7 @@ -78,7 +93,7 @@ struct tab_table points to a struct tab_joined_cell. */ void **cc; /* Cell contents; void *[nr][nc]. */ unsigned short *ct; /* Cell types; unsigned short[nr][nc]. */ - struct cell_style *styles[8]; + struct area_style *styles[8]; /* Rules. */ unsigned char *rh; /* Horiz rules; unsigned char[nr+1][nc]. */ @@ -156,12 +171,12 @@ void tab_joint_text_format (struct tab_table *, int x1, int y1, int x2, int y2, struct footnote *tab_create_footnote (struct tab_table *, size_t idx, const char *content, const char *marker, - struct cell_style *); + struct area_style *); void tab_add_footnote (struct tab_table *, int x, int y, const struct footnote *); void tab_add_style (struct tab_table *, int x, int y, - const struct cell_style *); + const struct area_style *); bool tab_cell_is_empty (const struct tab_table *, int c, int r); diff --git a/src/output/table-casereader.c b/src/output/table-casereader.c index 018fa6328e..e1b75ac5da 100644 --- a/src/output/table-casereader.c +++ b/src/output/table-casereader.c @@ -106,11 +106,17 @@ table_casereader_get_cell (const struct table *t, int x, int y, struct ccase *c; char *s; + static const struct area_style style = { + AREA_STYLE_INITIALIZER__, + .cell_style.halign = TABLE_HALIGN_RIGHT + }; + cell->d[TABLE_HORZ][0] = x; cell->d[TABLE_HORZ][1] = x + 1; cell->d[TABLE_VERT][0] = y; cell->d[TABLE_VERT][1] = y + 1; - cell->options = TAB_RIGHT; + cell->style = &style; + cell->options = 0; cell->n_footnotes = 0; if (tc->heading != NULL) { diff --git a/src/output/table-item.c b/src/output/table-item.c index 1308cb8650..e24d59b37a 100644 --- a/src/output/table-item.c +++ b/src/output/table-item.c @@ -35,8 +35,7 @@ table_item_text_create (const char *content) return NULL; struct table_item_text *text = xmalloc (sizeof *text); - *text = (struct table_item_text) { .content = xstrdup (content), - .halign = TAB_LEFT }; + *text = (struct table_item_text) { .content = xstrdup (content) }; return text; } @@ -52,8 +51,7 @@ table_item_text_clone (const struct table_item_text *old) .footnotes = xmemdup (old->footnotes, old->n_footnotes * sizeof *old->footnotes), .n_footnotes = old->n_footnotes, - .style = cell_style_clone (NULL, old->style), - .halign = old->halign, + .style = area_style_clone (NULL, old->style), }; return new; } @@ -65,7 +63,7 @@ table_item_text_destroy (struct table_item_text *text) { free (text->content); free (text->footnotes); - cell_style_free (text->style); + area_style_free (text->style); free (text); } } diff --git a/src/output/table-item.h b/src/output/table-item.h index ffdc4b682c..b7866a3c9b 100644 --- a/src/output/table-item.h +++ b/src/output/table-item.h @@ -27,6 +27,7 @@ #include "libpspp/compiler.h" #include "output/output-item.h" +#include "output/table.h" /* Title or caption in a table item. */ struct table_item_text @@ -34,8 +35,7 @@ struct table_item_text char *content; const struct footnote **footnotes; size_t n_footnotes; - int halign; /* TAB_*. */ - struct cell_style *style; + struct area_style *style; }; struct table_item_text *table_item_text_create (const char *); diff --git a/src/output/table-provider.h b/src/output/table-provider.h index ab39b4b25c..ef1f6ea5b9 100644 --- a/src/output/table-provider.h +++ b/src/output/table-provider.h @@ -23,54 +23,16 @@ struct pool; struct string; +enum table_halign table_halign_interpret (enum table_halign, bool numeric); + struct footnote { size_t idx; char *content; char *marker; - struct cell_style *style; - }; - -struct cell_color - { - uint8_t r, g, b; + struct area_style *style; }; -#define CELL_COLOR(r, g, b) (struct cell_color) { r, g, b } -#define CELL_COLOR_BLACK CELL_COLOR (0, 0, 0) -#define CELL_COLOR_WHITE CELL_COLOR (255, 255, 255) - -static inline bool -cell_color_equal (const struct cell_color *a, const struct cell_color *b) -{ - return a->r == b->r && a->g == b->g && a->b == b->b; -} - -struct cell_style - { - struct cell_color fg[2], bg[2]; - int margin[TABLE_N_AXES][2]; - char *typeface; - int size; - bool bold, italic, underline; - }; - -#define CELL_STYLE_INITIALIZER \ - { \ - .fg = { [0] = CELL_COLOR_BLACK, [1] = CELL_COLOR_BLACK}, \ - .bg = { [0] = CELL_COLOR_WHITE, [1] = CELL_COLOR_WHITE}, \ - .margin = { [TABLE_HORZ][0] = 8, [TABLE_HORZ][1] = 11, \ - [TABLE_VERT][0] = 1, [TABLE_VERT][1] = 1 }, \ - .typeface = NULL, \ - .size = 0, \ - .bold = false, \ - .italic = false, \ - .underline = false, \ - } - -struct cell_style *cell_style_clone (struct pool *, const struct cell_style *); -void cell_style_free (struct cell_style *); - /* A cell in a table. */ struct table_cell { @@ -98,7 +60,7 @@ struct table_cell const struct footnote **footnotes; size_t n_footnotes; - const struct cell_style *style; + const struct area_style *style; /* Called to free the cell's data, if nonnull. */ void (*destructor) (void *destructor_aux); diff --git a/src/output/table.c b/src/output/table.c index f909bf3ade..d94316eed2 100644 --- a/src/output/table.c +++ b/src/output/table.c @@ -20,8 +20,10 @@ #include "output/table-provider.h" #include +#include #include +#include "libpspp/assertion.h" #include "libpspp/cast.h" #include "libpspp/compiler.h" #include "libpspp/pool.h" @@ -130,22 +132,22 @@ table_set_nr (struct table *table, int nr) table->n[TABLE_VERT] = nr; } -struct cell_style * -cell_style_clone (struct pool *pool, const struct cell_style *old) +struct area_style * +area_style_clone (struct pool *pool, const struct area_style *old) { - struct cell_style *new = pool_malloc (pool, sizeof *new); + struct area_style *new = pool_malloc (pool, sizeof *new); *new = *old; - if (new->typeface) - new->typeface = pool_strdup (pool, new->typeface); + if (new->font_style.typeface) + new->font_style.typeface = pool_strdup (pool, new->font_style.typeface); return new; } void -cell_style_free (struct cell_style *style) +area_style_free (struct area_style *style) { if (style) { - free (style->typeface); + free (style->font_style.typeface); free (style); } } @@ -162,7 +164,7 @@ table_get_cell (const struct table *table, int x, int y, assert (x >= 0 && x < table->n[TABLE_HORZ]); assert (y >= 0 && y < table->n[TABLE_VERT]); - static const struct cell_style default_style = CELL_STYLE_INITIALIZER; + static const struct area_style default_style = AREA_STYLE_INITIALIZER; cell->style = &default_style; table->klass->get_cell (table, x, y, cell); @@ -220,7 +222,7 @@ table_get_rule (const struct table *table, enum table_axis axis, int x, int y, { assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ)); assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT)); - *color = CELL_COLOR_BLACK; + *color = (struct cell_color) CELL_COLOR_BLACK; return table->klass->get_rule (table, axis, x, y, color); } @@ -374,7 +376,7 @@ struct table_string { struct table table; char *string; - unsigned int options; + enum table_halign halign; }; static const struct table_class table_string_class; @@ -382,13 +384,13 @@ static const struct table_class table_string_class; /* Returns a table that contains a single cell, whose contents are S with options OPTIONS (a combination of TAB_* values). */ struct table * -table_from_string (unsigned int options, const char *s) +table_from_string (enum table_halign halign, const char *s) { struct table_string *ts = xmalloc (sizeof *ts); table_init (&ts->table, &table_string_class); ts->table.n[TABLE_HORZ] = ts->table.n[TABLE_VERT] = 1; ts->string = xstrdup (s); - ts->options = options; + ts->halign = halign; return &ts->table; } @@ -411,12 +413,21 @@ static void table_string_get_cell (const struct table *ts_, int x UNUSED, int y UNUSED, struct table_cell *cell) { + static const struct area_style styles[] = { +#define S(H) [H] = { AREA_STYLE_INITIALIZER__, .cell_style.halign = H } + S(TABLE_HALIGN_LEFT), + S(TABLE_HALIGN_CENTER), + S(TABLE_HALIGN_RIGHT), + S(TABLE_HALIGN_MIXED), + S(TABLE_HALIGN_DECIMAL), + }; struct table_string *ts = table_string_cast (ts_); cell->d[TABLE_HORZ][0] = 0; cell->d[TABLE_HORZ][1] = 1; cell->d[TABLE_VERT][0] = 0; cell->d[TABLE_VERT][1] = 1; - cell->options = ts->options; + cell->options = 0; + cell->style = &styles[table_halign_interpret (ts->halign, false)]; cell->text = ts->string; cell->n_footnotes = 0; cell->destructor = NULL; @@ -439,3 +450,139 @@ static const struct table_class table_string_class = NULL, /* paste */ NULL, /* select */ }; + +const char * +table_halign_to_string (enum table_halign halign) +{ + switch (halign) + { + case TABLE_HALIGN_LEFT: return "left"; + case TABLE_HALIGN_CENTER: return "center"; + case TABLE_HALIGN_RIGHT: return "right"; + case TABLE_HALIGN_DECIMAL: return "decimal"; + case TABLE_HALIGN_MIXED: return "mixed"; + default: return "**error**"; + } +} + +const char * +table_valign_to_string (enum table_valign valign) +{ + switch (valign) + { + case TABLE_VALIGN_TOP: return "top"; + case TABLE_VALIGN_CENTER: return "center"; + case TABLE_VALIGN_BOTTOM: return "bottom"; + default: return "**error**"; + } +} + +enum table_halign +table_halign_interpret (enum table_halign halign, bool numeric) +{ + switch (halign) + { + case TABLE_HALIGN_LEFT: + case TABLE_HALIGN_CENTER: + case TABLE_HALIGN_RIGHT: + return halign; + + case TABLE_HALIGN_MIXED: + return numeric ? TABLE_HALIGN_RIGHT : TABLE_HALIGN_LEFT; + + case TABLE_HALIGN_DECIMAL: + return TABLE_HALIGN_DECIMAL; + + default: + NOT_REACHED (); + } +} + +void +font_style_copy (struct font_style *dst, const struct font_style *src) +{ + *dst = *src; + if (dst->typeface) + dst->typeface = xstrdup (dst->typeface); +} + +void +font_style_uninit (struct font_style *font) +{ + if (font) + free (font->typeface); +} + +void +area_style_copy (struct area_style *dst, const struct area_style *src) +{ + font_style_copy (&dst->font_style, &src->font_style); + dst->cell_style = src->cell_style; +} + +void +area_style_uninit (struct area_style *area) +{ + if (area) + font_style_uninit (&area->font_style); +} + +const char * +table_stroke_to_string (enum table_stroke stroke) +{ + switch (stroke) + { + case TABLE_STROKE_NONE: return "none"; + case TABLE_STROKE_SOLID: return "solid"; + case TABLE_STROKE_DASHED: return "dashed"; + case TABLE_STROKE_THICK: return "thick"; + case TABLE_STROKE_THIN: return "thin"; + case TABLE_STROKE_DOUBLE: return "double"; + default: + return "**error**"; + } +} + +void +cell_color_dump (const struct cell_color *c) +{ + if (c->alpha != 255) + printf ("rgba(%d, %d, %d, %d)", c->r, c->g, c->b, c->alpha); + else + printf ("#%02"PRIx8"%02"PRIx8"%02"PRIx8, c->r, c->g, c->b); +} + +void +font_style_dump (const struct font_style *f) +{ + printf ("%s %dpx ", f->typeface, f->size); + cell_color_dump (&f->fg[0]); + putchar ('/'); + cell_color_dump (&f->bg[0]); + if (!cell_color_equal (&f->fg[0], &f->fg[1]) + || !cell_color_equal (&f->bg[0], &f->bg[1])) + { + printf (" alt="); + cell_color_dump (&f->fg[1]); + putchar ('/'); + cell_color_dump (&f->bg[1]); + } + if (f->bold) + fputs (" bold", stdout); + if (f->italic) + fputs (" italic", stdout); + if (f->underline) + fputs (" underline", stdout); +} + +void +cell_style_dump (const struct cell_style *c) +{ + fputs (table_halign_to_string (c->halign), stdout); + if (c->halign == TABLE_HALIGN_DECIMAL) + printf ("(%.2gpx)", c->decimal_offset); + printf (" %s", table_valign_to_string (c->valign)); + printf (" %d,%d,%d,%dpx", + c->margin[TABLE_HORZ][0], c->margin[TABLE_HORZ][1], + c->margin[TABLE_VERT][0], c->margin[TABLE_VERT][1]); +} diff --git a/src/output/table.h b/src/output/table.h index 8586ac1779..b2c194367f 100644 --- a/src/output/table.h +++ b/src/output/table.h @@ -36,55 +36,165 @@ table-item.h) for that purpose. */ #include +#include #include struct casereader; struct fmt_spec; +struct pool; struct table_item; struct variable; -/* Properties of a table cell. */ -enum +/* A table axis. + + Many table-related declarations use 2-element arrays in place of "x" and "y" + variables. This reduces code duplication significantly, because much table + code treats rows and columns the same way. + + A lot of code that uses these enumerations assumes that the two values are 0 + and 1, so don't change them to other values. */ +enum table_axis { - TAB_NONE = 0, + TABLE_HORZ, + TABLE_VERT, + TABLE_N_AXES + }; + +struct cell_color + { + uint8_t alpha, r, g, b; + }; + +#define CELL_COLOR(r, g, b) (struct cell_color) { 255, r, g, b } +#define CELL_COLOR_BLACK CELL_COLOR (0, 0, 0) +#define CELL_COLOR_WHITE CELL_COLOR (255, 255, 255) + +static inline bool +cell_color_equal (const struct cell_color *a, const struct cell_color *b) +{ + return a->alpha == b->alpha && a->r == b->r && a->g == b->g && a->b == b->b; +} + +void cell_color_dump (const struct cell_color *); + +enum table_stroke + { + TABLE_STROKE_NONE, + TABLE_STROKE_SOLID, + TABLE_STROKE_DASHED, + TABLE_STROKE_THICK, + TABLE_STROKE_THIN, + TABLE_STROKE_DOUBLE, + TABLE_N_STROKES, + }; + +const char *table_stroke_to_string (enum table_stroke); + +struct table_border_style + { + enum table_stroke stroke; + struct cell_color color; + }; + +#define TABLE_BORDER_STYLE_INITIALIZER { TABLE_STROKE_SOLID, CELL_COLOR_BLACK } + +enum table_halign + { + TABLE_HALIGN_RIGHT, + TABLE_HALIGN_LEFT, + TABLE_HALIGN_CENTER, + TABLE_HALIGN_MIXED, + TABLE_HALIGN_DECIMAL + }; + +const char *table_halign_to_string (enum table_halign); - /* Horizontal alignment of cell contents. */ - TAB_RIGHT = 0 << 0, - TAB_LEFT = 1 << 0, - TAB_CENTER = 2 << 0, - TAB_HALIGN = 3 << 0, /* Alignment mask. */ +enum table_valign + { + TABLE_VALIGN_TOP, + TABLE_VALIGN_CENTER, + TABLE_VALIGN_BOTTOM, + }; + +const char *table_valign_to_string (enum table_valign); + +struct cell_style + { + enum table_halign halign; + enum table_valign valign; + double decimal_offset; /* In 1/96" units. */ + char decimal_char; /* Either '.' or ','. */ + int margin[TABLE_N_AXES][2]; /* In 1/96" units. */ + }; + +#define CELL_STYLE_INITIALIZER { CELL_STYLE_INITIALIZER__ } +#define CELL_STYLE_INITIALIZER__ \ + .margin = { [TABLE_HORZ][0] = 8, [TABLE_HORZ][1] = 11, \ + [TABLE_VERT][0] = 1, [TABLE_VERT][1] = 1 } + +void cell_style_dump (const struct cell_style *); + +struct font_style + { + bool bold, italic, underline, markup; + struct cell_color fg[2], bg[2]; + char *typeface; + int size; + }; + +#define FONT_STYLE_INITIALIZER { FONT_STYLE_INITIALIZER__ } +#define FONT_STYLE_INITIALIZER__ \ + .fg = { [0] = CELL_COLOR_BLACK, [1] = CELL_COLOR_BLACK}, \ + .bg = { [0] = CELL_COLOR_WHITE, [1] = CELL_COLOR_WHITE}, - /* Vertical alignment of cell contents. */ - TAB_TOP = 0 << 2, - TAB_MIDDLE = 1 << 2, - TAB_BOTTOM = 2 << 2, - TAB_VALIGN = 3 << 2, /* Alignment mask. */ +void font_style_copy (struct font_style *, const struct font_style *); +void font_style_uninit (struct font_style *); +void font_style_dump (const struct font_style *); - /* These flags may be combined with any alignment. */ - TAB_EMPH = 1 << 4, /* Emphasize cell contents. */ - TAB_FIX = 1 << 5, /* Use fixed font. */ - TAB_MARKUP = 1 << 6, /* Text contains Pango markup. */ - TAB_ROTATE = 1 << 7, /* Rotate cell contents 90 degrees. */ +struct area_style + { + struct cell_style cell_style; + struct font_style font_style; + }; + +#define AREA_STYLE_INITIALIZER { AREA_STYLE_INITIALIZER__ } +#define AREA_STYLE_INITIALIZER__ \ + .cell_style = CELL_STYLE_INITIALIZER, \ + .font_style = FONT_STYLE_INITIALIZER + +struct area_style *area_style_clone (struct pool *, const struct area_style *); +void area_style_copy (struct area_style *, const struct area_style *); +void area_style_uninit (struct area_style *); +void area_style_free (struct area_style *); + +/* Properties of a table cell. */ +enum + { + TAB_NONE = 0, + TAB_EMPH = 1 << 0, /* Emphasize cell contents. */ + TAB_FIX = 1 << 1, /* Use fixed font. */ + TAB_MARKUP = 1 << 2, /* Text contains Pango markup. */ + TAB_NUMERIC = 1 << 3, /* Cell contents are numeric. */ + TAB_ROTATE = 1 << 4, /* Rotate cell contents 90 degrees. */ /* Bits with values (1 << TAB_FIRST_AVAILABLE) and higher are not used, so they are available for subclasses to use as they wish. */ - TAB_FIRST_AVAILABLE = 8 + TAB_FIRST_AVAILABLE = 5 }; /* Styles for the rules around table cells. */ enum { - TAL_NONE, /* No spacing. */ + TAL_NONE = TABLE_STROKE_NONE, #define TAL_0 TAL_NONE - TAL_SOLID, + TAL_SOLID = TABLE_STROKE_SOLID, #define TAL_1 TAL_SOLID - TAL_DASHED, - TAL_THICK, - TAL_THIN, - TAL_DOUBLE, + TAL_DASHED = TABLE_STROKE_DASHED, + TAL_THICK = TABLE_STROKE_THICK, + TAL_THIN = TABLE_STROKE_THIN, + TAL_DOUBLE = TABLE_STROKE_DOUBLE, #define TAL_2 TAL_DOUBLE - N_LINES }; /* Given line styles A and B (each one of the TAL_* enumeration constants @@ -98,21 +208,6 @@ static inline int table_rule_combine (int a, int b) return a > b ? a : b; } -/* A table axis. - - Many table-related declarations use 2-element arrays in place of "x" and "y" - variables. This reduces code duplication significantly, because much table - code has treat rows and columns the same way. - - A lot of code that uses these enumerations assumes that the two values are 0 - and 1, so don't change them to other values. */ -enum table_axis - { - TABLE_HORZ, - TABLE_VERT, - TABLE_N_AXES - }; - /* A table. */ struct table { @@ -175,9 +270,7 @@ void table_set_hb (struct table *, int hb); /* Table classes. */ /* Simple kinds of tables. */ -struct table *table_from_string (unsigned int options, const char *); -struct table *table_from_string_span (unsigned int options, const char *, - int colspan, int rowspan); +struct table *table_from_string (enum table_halign, const char *); struct table *table_from_variables (unsigned int options, struct variable **, size_t); struct table *table_from_casereader (const struct casereader *, diff --git a/src/output/text-item.c b/src/output/text-item.c index 5b18e54c5d..a122caf3bf 100644 --- a/src/output/text-item.c +++ b/src/output/text-item.c @@ -99,14 +99,16 @@ text_item_to_table_item (struct text_item *text_item) { struct tab_table *tab = tab_create (1, 1); - struct cell_style *style = pool_alloc (tab->container, sizeof *style); - *style = (struct cell_style) CELL_STYLE_INITIALIZER; + struct area_style *style = pool_alloc (tab->container, sizeof *style); + *style = (struct area_style) AREA_STYLE_INITIALIZER; + struct font_style *font_style = &style->font_style; if (text_item->typeface) - style->typeface = pool_strdup (tab->container, text_item->typeface); - style->size = text_item->size; - style->bold = text_item->bold; - style->italic = text_item->italic; - style->underline = text_item->underline; + font_style->typeface = pool_strdup (tab->container, text_item->typeface); + font_style->size = text_item->size; + font_style->bold = text_item->bold; + font_style->italic = text_item->italic; + font_style->underline = text_item->underline; + font_style->markup = text_item->markup; tab->styles[0] = style; int opts = TAB_LEFT; diff --git a/src/output/text-item.h b/src/output/text-item.h index a8f1e493e9..2e27529fec 100644 --- a/src/output/text-item.h +++ b/src/output/text-item.h @@ -50,9 +50,10 @@ struct text_item struct output_item output_item; char *text; /* The content. */ enum text_item_type type; /* Type. */ + + bool bold, italic, underline, markup; char *typeface; int size; - bool bold, italic, underline, markup; }; struct text_item *text_item_create (enum text_item_type, const char *text); -- 2.30.2