From: Ben Pfaff Date: Tue, 20 Nov 2018 18:24:20 +0000 (-0800) Subject: output: Add support for fonts. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp;a=commitdiff_plain;h=37a14e6b8ab908b2c23d77e0cb6a9085fe2d73e1 output: Add support for fonts. --- diff --git a/src/output/ascii.c b/src/output/ascii.c index 67634126f9..72c628871c 100644 --- a/src/output/ascii.c +++ b/src/output/ascii.c @@ -172,14 +172,6 @@ make_box_index (int left_, int right_, int top_, int bottom_) return idx; } -/* How to emphasize text. */ -enum emphasis_style - { - EMPH_BOLD, /* Overstrike for bold. */ - EMPH_UNDERLINE, /* Overstrike for underlining. */ - EMPH_NONE /* No emphasis. */ - }; - /* ASCII output driver. */ struct ascii_driver { @@ -187,7 +179,7 @@ struct ascii_driver /* User parameters. */ bool append; /* Append if output file already exists? */ - enum emphasis_style emphasis; /* How to emphasize text. */ + bool emphasis; /* Enable bold and underline in output? */ char *chart_file_name; /* Name of files used for charts. */ #ifdef HAVE_CAIRO @@ -259,11 +251,7 @@ ascii_create (struct file_handle *fh, enum settings_output_devices device_type, d = &a->driver; output_driver_init (&a->driver, &ascii_driver_class, fh_get_file_name (fh), device_type); a->append = parse_boolean (opt (d, o, "append", "false")); - a->emphasis = parse_enum (opt (d, o, "emphasis", "none"), - "bold", EMPH_BOLD, - "underline", EMPH_UNDERLINE, - "none", EMPH_NONE, - NULL_SENTINEL); + a->emphasis = parse_boolean (opt (d, o, "emphasis", "false")); a->chart_file_name = parse_chart_file_name (opt (d, o, "charts", fh_get_file_name (fh))); a->handle = fh; @@ -651,6 +639,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, + 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) { @@ -730,7 +719,7 @@ text_draw (struct ascii_driver *a, unsigned int options, return; } - if (!(options & TAB_EMPH) || a->emphasis == EMPH_NONE) + if (!a->emphasis || (!bold && !underline)) memcpy (ascii_reserve (a, y, x, x + width, n), string, n); else { @@ -750,7 +739,12 @@ text_draw (struct ascii_driver *a, unsigned int options, w = uc_width (uc, "UTF-8"); if (w > 0) - n_out += a->emphasis == EMPH_UNDERLINE ? 2 : 1 + mblen; + { + if (bold) + n_out += 1 + mblen; + if (underline) + n_out += 2; + } } /* Then insert them. */ @@ -765,11 +759,16 @@ text_draw (struct ascii_driver *a, unsigned int options, if (w > 0) { - if (a->emphasis == EMPH_UNDERLINE) - *out++ = '_'; - else - out = mempcpy (out, string + ofs, mblen); - *out++ = '\b'; + if (bold) + { + out = mempcpy (out, string + ofs, mblen); + *out++ = '\b'; + } + if (underline) + { + *out++ = '_'; + *out++ = '\b'; + } } out = mempcpy (out, string + ofs, mblen); } @@ -779,6 +778,7 @@ text_draw (struct ascii_driver *a, unsigned int options, 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) { @@ -873,7 +873,8 @@ ascii_layout_cell_text (struct ascii_driver *a, width -= ofs - graph_ofs; /* Draw text. */ - text_draw (a, contents->options, bb, clip, y, line, graph_ofs, width); + text_draw (a, contents->options, bold, underline, + bb, clip, y, line, graph_ofs, width); /* If a new-line ended the line, just skip the new-line. Otherwise, skip past any spaces past the end of the line (but not past a new-line). */ @@ -919,14 +920,16 @@ ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell, break; } - bb[V][0] = ascii_layout_cell_text (a, contents, bb, clip, widthp); + 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 ascii_test_write (struct output_driver *driver, - const char *s, int x, int y, unsigned int options) + const char *s, int x, int y, bool bold, bool underline) { struct ascii_driver *a = ascii_driver_cast (driver); int bb[TABLE_N_AXES][2]; @@ -936,13 +939,17 @@ ascii_test_write (struct output_driver *driver, return; struct cell_contents contents = { - .options = options | TAB_LEFT, + .options = TAB_LEFT, .text = CONST_CAST (char *, s), }; - + struct cell_style cell_style = { + .bold = bold, + .underline = underline, + }; struct table_cell cell = { .contents = &contents, .n_contents = 1, + .style = &cell_style, }; bb[TABLE_HORZ][0] = x; diff --git a/src/output/ascii.h b/src/output/ascii.h index eb4e4afec6..7622b8f5dd 100644 --- a/src/output/ascii.h +++ b/src/output/ascii.h @@ -20,7 +20,7 @@ struct output_driver; void ascii_test_write (struct output_driver *, - const char *s, int x, int y, unsigned int options); + const char *s, int x, int y, bool bold, bool underline); void ascii_test_set_length (struct output_driver *, int y, int length); void ascii_test_flush (struct output_driver *); diff --git a/src/output/cairo.c b/src/output/cairo.c index 72da1a3f21..ad5dd345ab 100644 --- a/src/output/cairo.c +++ b/src/output/cairo.c @@ -57,6 +57,7 @@ #include #include +#include "gl/c-strcase.h" #include "gl/intprops.h" #include "gl/minmax.h" #include "gl/xalloc.h" @@ -99,7 +100,6 @@ enum xr_font_type XR_FONT_PROPORTIONAL, XR_FONT_EMPHASIS, XR_FONT_FIXED, - XR_FONT_MARKER, XR_N_FONTS }; @@ -228,37 +228,52 @@ parse_color (struct output_driver *d, struct string_map *options, } static PangoFontDescription * -parse_font (struct output_driver *d, struct string_map *options, - const char *key, const char *default_value, - int default_size) +parse_font (const char *font, int default_size, bool bold, bool italic) { - PangoFontDescription *desc; - char *string; + if (!c_strcasecmp (font, "Monospaced")) + font = "Monospace"; - /* Parse KEY as a font description. */ - string = parse_string (opt (d, options, key, default_value)); - desc = pango_font_description_from_string (string); + PangoFontDescription *desc = pango_font_description_from_string (font); if (desc == NULL) + return NULL; + + /* If the font description didn't include an explicit font size, then set it + to DEFAULT_SIZE, which is in inch/72000 units. */ + if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE)) + pango_font_description_set_size (desc, + (default_size / 1000.0) * PANGO_SCALE); + + pango_font_description_set_weight (desc, (bold + ? PANGO_WEIGHT_BOLD + : PANGO_WEIGHT_NORMAL)); + pango_font_description_set_style (desc, (italic + ? PANGO_STYLE_ITALIC + : PANGO_STYLE_NORMAL)); + + return desc; +} + +static PangoFontDescription * +parse_font_option (struct output_driver *d, struct string_map *options, + const char *key, const char *default_value, + int default_size, bool bold, bool italic) +{ + char *string = parse_string (opt (d, options, key, default_value)); + PangoFontDescription *desc = parse_font (string, default_size, bold, italic); + if (!desc) { msg (MW, _("`%s': bad font specification"), string); /* Fall back to DEFAULT_VALUE, which had better be a valid font description. */ - desc = pango_font_description_from_string (default_value); + desc = parse_font (default_value, default_size, bold, italic); assert (desc != NULL); } free (string); - /* If the font description didn't include an explicit font size, then set it - to DEFAULT_SIZE, which is in inch/72000 units. */ - if (!(pango_font_description_get_set_fields (desc) & PANGO_FONT_MASK_SIZE)) - pango_font_description_set_size (desc, - (default_size / 1000.0) * PANGO_SCALE); - return desc; } - static void apply_options (struct xr_driver *xr, struct string_map *o) { @@ -285,14 +300,12 @@ apply_options (struct xr_driver *xr, struct string_map *o) } font_size = parse_int (opt (d, o, "font-size", "10000"), 1000, 1000000); - xr->fonts[XR_FONT_FIXED].desc = parse_font (d, o, "fixed-font", "monospace", - font_size); - xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font (d, o, "prop-font", - "sans serif", font_size); - xr->fonts[XR_FONT_EMPHASIS].desc = parse_font (d, o, "emph-font", - "sans serif italic", font_size); - xr->fonts[XR_FONT_MARKER].desc = parse_font (d, o, "marker-font", "sans serif", - font_size * PANGO_SCALE_X_SMALL); + xr->fonts[XR_FONT_FIXED].desc = parse_font_option + (d, o, "fixed-font", "monospace", font_size, false, false); + xr->fonts[XR_FONT_PROPORTIONAL].desc = parse_font_option ( + d, o, "prop-font", "sans serif", font_size, false, false); + xr->fonts[XR_FONT_EMPHASIS].desc = parse_font_option ( + d, o, "emph-font", "sans serif", font_size, false, true); xr->line_space = XR_POINT; xr->line_width = XR_POINT / 2; @@ -397,7 +410,7 @@ xr_set_cairo (struct xr_driver *xr, cairo_t *cairo) xr->params->line_widths[i][RENDER_LINE_NONE] = 0; xr->params->line_widths[i][RENDER_LINE_SINGLE] = lw; xr->params->line_widths[i][RENDER_LINE_DASHED] = lw; - xr->params->line_widths[i][RENDER_LINE_THICK] = lw * 3; + xr->params->line_widths[i][RENDER_LINE_THICK] = lw * 2; xr->params->line_widths[i][RENDER_LINE_THIN] = lw / 2; xr->params->line_widths[i][RENDER_LINE_DOUBLE] = 2 * lw + ls; } @@ -649,7 +662,7 @@ dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style) cairo_new_path (xr->cairo); cairo_set_line_width ( xr->cairo, - xr_to_pt (style == RENDER_LINE_THICK ? xr->line_width * 3 + xr_to_pt (style == RENDER_LINE_THICK ? xr->line_width * 2 : style == RENDER_LINE_THIN ? xr->line_width / 2 : xr->line_width)); cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y)); @@ -984,19 +997,37 @@ xr_layout_cell_text (struct xr_driver *xr, int *widthp, int *brk) { unsigned int options = contents->options; - struct xr_font *font; - bool merge_footnotes; size_t length; 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) + { + PangoFontDescription *desc = parse_font ( + style->font, + style->font_size ? style->font_size * 1000 * 72 / 128 : 10000, + style->bold, style->italic); + if (desc) + { + PangoLayout *layout = pango_cairo_create_layout (xr->cairo); + pango_layout_set_font_description (layout, desc); + + local_font.desc = desc; + local_font.layout = layout; + font = &local_font; + } + } + + int footnote_adjustment; if (contents->n_footnotes == 0) - merge_footnotes = false; + footnote_adjustment = 0; else if (contents->n_footnotes == 1 && (options & TAB_HALIGN) == TAB_RIGHT) { PangoAttrList *attrs; - font = &xr->fonts[XR_FONT_MARKER]; - const char *marker = contents->footnotes[0]->marker; pango_layout_set_text (font->layout, marker, strlen (marker)); @@ -1006,36 +1037,18 @@ xr_layout_cell_text (struct xr_driver *xr, pango_attr_list_unref (attrs); pango_layout_get_size (font->layout, &w, &h); - merge_footnotes = w > px_to_xr (style->margin[H][1]); - 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 (bb[V][0] + 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); + footnote_adjustment = MIN (w, px_to_xr (style->margin[H][1])); } 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]); + footnote_adjustment = px_to_xr (style->margin[H][1]); length = strlen (contents->text); - if (merge_footnotes) + if (footnote_adjustment) { PangoAttrList *attrs; struct string s; - bb[H][1] += px_to_xr (style->margin[H][1]); + bb[H][1] += footnote_adjustment; ds_init_empty (&s); ds_extend (&s, length + contents->n_footnotes * 10); @@ -1045,14 +1058,28 @@ xr_layout_cell_text (struct xr_driver *xr, ds_destroy (&s); attrs = pango_attr_list_new (); + if (style->underline) + pango_attr_list_insert (attrs, pango_attr_underline_new ( + PANGO_UNDERLINE_SINGLE)); 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); + attrs, pango_attr_font_desc_new (font->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_text (font->layout, contents->text, -1); + + if (style->underline) + { + PangoAttrList *attrs = pango_attr_list_new (); + pango_attr_list_insert (attrs, pango_attr_underline_new ( + PANGO_UNDERLINE_SINGLE)); + pango_layout_set_attributes (font->layout, attrs); + pango_attr_list_unref (attrs); + } + } pango_layout_set_alignment ( font->layout, @@ -1156,6 +1183,13 @@ xr_layout_cell_text (struct xr_driver *xr, } pango_layout_set_attributes (font->layout, NULL); + + if (font == &local_font) + { + g_object_unref (G_OBJECT (font->layout)); + pango_font_description_free (font->desc); + } + return bb[V][0] + h; } diff --git a/src/output/render.c b/src/output/render.c index d30818a355..418193b87e 100644 --- a/src/output/render.c +++ b/src/output/render.c @@ -25,6 +25,7 @@ #include "libpspp/assertion.h" #include "libpspp/hash-functions.h" #include "libpspp/hmap.h" +#include "libpspp/pool.h" #include "output/render.h" #include "output/tab.h" #include "output/table-item.h" @@ -1434,6 +1435,12 @@ add_text_page (struct render_pager *p, const struct table_item_text *t) tab_text (tab, 0, 0, TAB_LEFT, 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] = pool_clone (tab->container, t->style, sizeof *t->style); + if (t->style->font) + tab->styles[0]->font = pool_strdup (tab->container, t->style->font); + } render_pager_add_table (p, &tab->table); } diff --git a/src/output/table-item.c b/src/output/table-item.c index 645d8ca45f..d2d3ea54ab 100644 --- a/src/output/table-item.c +++ b/src/output/table-item.c @@ -51,6 +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 (old->style), }; return new; } @@ -62,6 +63,7 @@ table_item_text_destroy (struct table_item_text *text) { free (text->content); free (text->footnotes); + cell_style_free (text->style); free (text); } } diff --git a/src/output/table-item.h b/src/output/table-item.h index 4e070ddabf..1cc92eaf5d 100644 --- a/src/output/table-item.h +++ b/src/output/table-item.h @@ -34,6 +34,7 @@ struct table_item_text char *content; const struct footnote **footnotes; size_t n_footnotes; + struct cell_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 0e22989b99..ae7f77d420 100644 --- a/src/output/table-provider.h +++ b/src/output/table-provider.h @@ -62,8 +62,27 @@ struct cell_style { struct cell_color fg, bg; int margin[TABLE_N_AXES][2]; + char *font; + int font_size; + bool bold, italic, underline; }; +#define CELL_STYLE_INITIALIZER \ + { \ + .fg = CELL_COLOR_BLACK, \ + .bg = CELL_COLOR_WHITE, \ + .margin = { [TABLE_HORZ][0] = 8, [TABLE_HORZ][1] = 11, \ + [TABLE_VERT][0] = 1, [TABLE_VERT][1] = 1 }, \ + .font = NULL, \ + .font_size = 0, \ + .bold = false, \ + .italic = false, \ + .underline = false, \ + } + +struct cell_style *cell_style_clone (const struct cell_style *); +void cell_style_free (struct cell_style *); + /* A cell in a table. */ struct table_cell { diff --git a/src/output/table.c b/src/output/table.c index 0cd05d7039..3fccde9c98 100644 --- a/src/output/table.c +++ b/src/output/table.c @@ -129,6 +129,26 @@ table_set_nr (struct table *table, int nr) table->n[TABLE_VERT] = nr; } +struct cell_style * +cell_style_clone (const struct cell_style *old) +{ + struct cell_style *new = xmalloc (sizeof *new); + *new = *old; + if (new->font) + new->font = strdup (new->font); + return new; +} + +void +cell_style_free (struct cell_style *style) +{ + if (style) + { + free (style->font); + free (style); + } +} + /* Initializes CELL with the contents of the table cell at column X and row Y within TABLE. When CELL is no longer needed, the caller is responsible for freeing it by calling table_cell_free(CELL). @@ -141,13 +161,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 = - { - .fg = { 0, 0, 0 }, - .bg = { 255, 255, 255 }, - .margin = { [TABLE_HORZ][0] = 8, [TABLE_HORZ][1] = 11, - [TABLE_VERT][0] = 1, [TABLE_VERT][1] = 1 }, - }; + static const struct cell_style default_style = CELL_STYLE_INITIALIZER; cell->style = &default_style; table->klass->get_cell (table, x, y, cell); diff --git a/tests/output/render-test.c b/tests/output/render-test.c index e286f6bea4..42b8795336 100644 --- a/tests/output/render-test.c +++ b/tests/output/render-test.c @@ -39,7 +39,8 @@ static int transpose; /* --emphasis: ASCII driver emphasis option. */ -static char *emphasis; +static bool bold; +static bool underline; /* --box: ASCII driver box option. */ static char *box; @@ -143,8 +144,8 @@ configure_drivers (int width, int length, int min_break) if (min_break >= 0) string_map_insert_nocopy (&options, xstrdup ("min-hbreak"), xasprintf ("%d", min_break)); - if (emphasis != NULL) - string_map_insert (&options, "emphasis", emphasis); + if (bold || underline) + string_map_insert (&options, "emphasis", "true"); if (box != NULL) string_map_insert (&options, "box", box); @@ -281,7 +282,23 @@ parse_options (int argc, char **argv) break; case OPT_EMPHASIS: - emphasis = optarg; + if (!strcmp (optarg, "bold")) + { + bold = true; + underline = false; + } + else if (!strcmp (optarg, "underline")) + { + bold = false; + underline = true; + } + else if (!strcmp (optarg, "none")) + { + bold = underline = false; + } + else + error (1, 0, "argument to --emphasis must be \"bold\" or " + "\"underline\" or \"none\""); break; case OPT_BOX: @@ -485,7 +502,8 @@ draw (FILE *stream) continue; if (sscanf (buffer, "%d %d %d %[^\n]", &x, &y, &emph, text) == 4) - ascii_test_write (ascii_driver, text, x, y, emph ? TAB_EMPH : 0); + ascii_test_write (ascii_driver, text, x, y, emph ? bold : false, + emph ? underline : false); else if (sscanf (buffer, "set-length %d %d", &y, &length) == 2) ascii_test_set_length (ascii_driver, y, length); else