From 1c02620baa5dd413e3284071f6a5184c1e77bf40 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 10 Jan 2021 13:52:34 -0800 Subject: [PATCH] text-item: Simplify text_item further to just hold a pivot_value. pivot_value has built-in support for font styling, so it eliminates the need for a separate font_style object. --- src/output/csv.c | 20 ++----- src/output/driver-provider.h | 2 - src/output/driver.c | 52 ++++------------- src/output/html.c | 4 +- src/output/journal.c | 6 +- src/output/odt.c | 6 +- src/output/pivot-table.c | 35 +++++++++++- src/output/spv/spv-output.c | 20 ++----- src/output/spv/spv-writer.c | 4 +- src/output/tex.c | 3 +- src/output/text-item.c | 97 +++++++++++++++++++++----------- src/output/text-item.h | 10 ++-- src/ui/gui/psppire-output-view.c | 6 +- 13 files changed, 146 insertions(+), 119 deletions(-) diff --git a/src/output/csv.c b/src/output/csv.c index b97bfda600..dd8296059d 100644 --- a/src/output/csv.c +++ b/src/output/csv.c @@ -184,12 +184,6 @@ csv_output_table_cell (struct csv_driver *csv, const struct pivot_table *pt, if (leader) ds_put_format (&s, "%s: ", leader); pivot_value_format (cell->value, pt, &s); - if (cell->font_style->markup) - { - char *t = output_get_text_from_markup (ds_cstr (&s)); - ds_assign_cstr (&s, t); - free (t); - } csv_output_field (csv, ds_cstr (&s)); ds_destroy (&s); } @@ -260,22 +254,16 @@ csv_submit (struct output_driver *driver, else if (is_text_item (output_item)) { const struct text_item *text_item = to_text_item (output_item); - enum text_item_type type = text_item_get_type (text_item); - const char *text = text_item_get_text (text_item); + enum text_item_type type = text_item_get_type (text_item); if (type == TEXT_ITEM_SYNTAX || type == TEXT_ITEM_PAGE_TITLE) return; csv_put_separator (csv); - if (text_item->style.markup) - { - char *plain_text = output_get_text_from_markup (text); - csv_output_lines (csv, plain_text); - free (plain_text); - } - else - csv_output_lines (csv, text); + char *text = text_item_get_plain_text (text_item); + csv_output_lines (csv, text); + free (text); } else if (is_page_break_item (output_item)) { diff --git a/src/output/driver-provider.h b/src/output/driver-provider.h index c44a2e303b..0f01fa9b30 100644 --- a/src/output/driver-provider.h +++ b/src/output/driver-provider.h @@ -105,6 +105,4 @@ struct output_driver_factory struct string_map *options); }; -char *output_get_text_from_markup (const char *markup); - #endif /* output/driver-provider.h */ diff --git a/src/output/driver.c b/src/output/driver.c index 55eac27d18..a4718c939a 100644 --- a/src/output/driver.c +++ b/src/output/driver.c @@ -21,8 +21,6 @@ #include #include -#include -#include #include #include #include @@ -240,27 +238,21 @@ output_submit (struct output_item *item) size_t idx = --e->n_groups; free (e->groups[idx]); - if (idx >= 1 && idx <= 4) - { - char *key = xasprintf ("Head%zu", idx); - free (string_map_find_and_delete (&e->heading_vars, key)); - free (key); - } + + char *key = xasprintf ("Head%zu", idx); + free (string_map_find_and_delete (&e->heading_vars, key)); + free (key); } else if (is_text_item (item)) { const struct text_item *text_item = to_text_item (item); enum text_item_type type = text_item_get_type (text_item); - const char *text = text_item_get_text (text_item); - if (type == TEXT_ITEM_TITLE - && e->n_groups >= 1 && e->n_groups <= 4) - { - char *key = xasprintf ("Head%zu", e->n_groups); - string_map_replace (&e->heading_vars, key, text); - free (key); - } - else if (type == TEXT_ITEM_PAGE_TITLE) - string_map_replace (&e->heading_vars, "PageTitle", text); + char *key = (type == TEXT_ITEM_TITLE ? xasprintf ("Head%zu", e->n_groups) + : type == TEXT_ITEM_PAGE_TITLE ? xstrdup ("PageTitle") + : NULL); + if (key) + string_map_replace_nocopy (&e->heading_vars, key, + text_item_get_plain_text (text_item)); } output_submit__ (e, item); @@ -554,30 +546,6 @@ output_driver_parse_option (const char *option, struct string_map *options) string_map_insert_nocopy (options, key, value); } -/* Extracts the actual text content from the given Pango MARKUP and returns it - as as a malloc()'d string. */ -char * -output_get_text_from_markup (const char *markup) -{ - xmlParserCtxt *parser = xmlCreatePushParserCtxt (NULL, NULL, NULL, 0, NULL); - if (!parser) - return xstrdup (markup); - - xmlParseChunk (parser, "", strlen (""), false); - xmlParseChunk (parser, markup, strlen (markup), false); - xmlParseChunk (parser, "", strlen (""), true); - - char *content - = (parser->wellFormed - ? CHAR_CAST (char *, - xmlNodeGetContent (xmlDocGetRootElement (parser->myDoc))) - : xstrdup (markup)); - xmlFreeDoc (parser->myDoc); - xmlFreeParserCtxt (parser); - - return content; -} - char * output_driver_substitute_heading_vars (const char *src, int page_number) { diff --git a/src/output/html.c b/src/output/html.c index 644ef49eba..0be011d128 100644 --- a/src/output/html.c +++ b/src/output/html.c @@ -292,7 +292,7 @@ html_submit (struct output_driver *driver, else if (is_text_item (output_item)) { struct text_item *text_item = to_text_item (output_item); - const char *s = text_item_get_text (text_item); + char *s = text_item_get_plain_text (text_item); switch (text_item_get_type (text_item)) { @@ -319,6 +319,8 @@ html_submit (struct output_driver *driver, fprintf (html->file, "

\n"); break; } + + free (s); } else if (is_message_item (output_item)) { diff --git a/src/output/journal.c b/src/output/journal.c index 9cab788051..f93ed0bc84 100644 --- a/src/output/journal.c +++ b/src/output/journal.c @@ -109,7 +109,11 @@ journal_submit (struct output_driver *driver, const struct output_item *item) enum text_item_type type = text_item_get_type (text_item); if (type == TEXT_ITEM_SYNTAX) - journal_output (j, text_item_get_text (text_item)); + { + char *text = text_item_get_plain_text (text_item); + journal_output (j, text); + free (text); + } } else if (is_message_item (item)) { diff --git a/src/output/odt.c b/src/output/odt.c index 4a87815548..82480bb3e3 100644 --- a/src/output/odt.c +++ b/src/output/odt.c @@ -598,7 +598,11 @@ odt_submit (struct output_driver *driver, if (is_table_item (output_item)) write_table (odt, to_table_item (output_item)); else if (is_text_item (output_item)) - odt_output_text (odt, text_item_get_text (to_text_item (output_item))); + { + char *text = text_item_get_plain_text (to_text_item (output_item)); + odt_output_text (odt, text); + free (text); + } else if (is_message_item (output_item)) { const struct message_item *message_item = to_message_item (output_item); diff --git a/src/output/pivot-table.c b/src/output/pivot-table.c index 85ac16d09c..1b3014c629 100644 --- a/src/output/pivot-table.c +++ b/src/output/pivot-table.c @@ -18,6 +18,8 @@ #include "output/pivot-table.h" +#include +#include #include #include @@ -2301,6 +2303,33 @@ interpret_show (enum settings_value_show global_show, : global_show); } +/* Appends to OUT the actual text content from the given Pango MARKUP. */ +static void +get_text_from_markup (const char *markup, struct string *out) +{ + xmlParserCtxt *parser = xmlCreatePushParserCtxt (NULL, NULL, NULL, 0, NULL); + if (!parser) + { + ds_put_cstr (out, markup); + return; + } + + xmlParseChunk (parser, "", strlen (""), false); + xmlParseChunk (parser, markup, strlen (markup), false); + xmlParseChunk (parser, "", strlen (""), true); + + if (parser->wellFormed) + { + xmlChar *s = xmlNodeGetContent (xmlDocGetRootElement (parser->myDoc)); + ds_put_cstr (out, CHAR_CAST (char *, s)); + xmlFree (s); + } + else + ds_put_cstr (out, markup); + xmlFreeDoc (parser->myDoc); + xmlFreeParserCtxt (parser); +} + /* Appends a text representation of the body of VALUE to OUT. Settings on PT control whether variable and value labels are included. @@ -2388,7 +2417,10 @@ pivot_value_format_body (const struct pivot_value *value, break; case PIVOT_VALUE_TEXT: - ds_put_cstr (out, value->text.local); + if (value->font_style && value->font_style->markup) + get_text_from_markup (value->text.local, out); + else + ds_put_cstr (out, value->text.local); break; case PIVOT_VALUE_TEMPLATE: @@ -2451,6 +2483,7 @@ pivot_value_to_string_defaults (const struct pivot_value *value) static const struct pivot_table pt = { .show_values = SETTINGS_VALUE_SHOW_DEFAULT, .show_variables = SETTINGS_VALUE_SHOW_DEFAULT, + .settings = FMT_SETTINGS_INIT, }; return pivot_value_to_string (value, &pt); } diff --git a/src/output/spv/spv-output.c b/src/output/spv/spv-output.c index c21ec22bcb..edca066ed2 100644 --- a/src/output/spv/spv-output.c +++ b/src/output/spv/spv-output.c @@ -28,19 +28,11 @@ void spv_text_submit (const struct spv_item *in) { enum spv_item_class class = spv_item_get_class (in); - enum text_item_type type - = (class == SPV_CLASS_HEADINGS ? TEXT_ITEM_TITLE - : class == SPV_CLASS_PAGETITLE ? TEXT_ITEM_PAGE_TITLE - : TEXT_ITEM_LOG); - const struct pivot_value *value = spv_item_get_text (in); - char *text = pivot_value_to_string_defaults (value); - char *label = in->label ? xstrdup (in->label) : NULL; - struct text_item *item = text_item_create_nocopy (type, text, label); - - if (value->font_style) - { - font_style_uninit (&item->style); - font_style_copy (NULL, &item->style, value->font_style); - } + struct text_item *item = text_item_create_value ( + (class == SPV_CLASS_HEADINGS ? TEXT_ITEM_TITLE + : class == SPV_CLASS_PAGETITLE ? TEXT_ITEM_PAGE_TITLE + : TEXT_ITEM_LOG), + pivot_value_clone (spv_item_get_text (in)), + in->label ? xstrdup (in->label) : NULL); text_item_submit (item); } diff --git a/src/output/spv/spv-writer.c b/src/output/spv/spv-writer.c index 7f8f1e4155..6256b32f12 100644 --- a/src/output/spv/spv-writer.c +++ b/src/output/spv/spv-writer.c @@ -314,7 +314,9 @@ spv_writer_put_text (struct spv_writer *w, const struct text_item *text, write_attr (w, "commandName", command_id); start_elem (w, "html"); - write_text (w, text->text); /* XXX */ + char *s = text_item_get_plain_text (text); + write_text (w, s); + free (s); end_elem (w); /* html */ end_elem (w); /* vtx:text */ end_elem (w); /* container */ diff --git a/src/output/tex.c b/src/output/tex.c index c5591011f8..e569a12788 100644 --- a/src/output/tex.c +++ b/src/output/tex.c @@ -350,7 +350,7 @@ tex_submit (struct output_driver *driver, else if (is_text_item (output_item)) { struct text_item *text_item = to_text_item (output_item); - const char *s = text_item_get_text (text_item); + char *s = text_item_get_plain_text (text_item); switch (text_item_get_type (text_item)) { @@ -372,6 +372,7 @@ tex_submit (struct output_driver *driver, printf ("Unhandled type %d\n", text_item_get_type (text_item)); break; } + free (s); } else if (is_message_item (output_item)) { diff --git a/src/output/text-item.c b/src/output/text-item.c index 371ee901e6..7bcf2aa10d 100644 --- a/src/output/text-item.c +++ b/src/output/text-item.c @@ -63,22 +63,8 @@ text_item_type_to_string (enum text_item_type type) struct text_item * text_item_create_nocopy (enum text_item_type type, char *text, char *label) { - struct text_item *item = xzalloc (sizeof *item); - *item = (struct text_item) { - .output_item = OUTPUT_ITEM_INITIALIZER (&text_item_class), - .output_item.label = label, - .text = text, - .type = type, - .style = FONT_STYLE_INITIALIZER, - }; - - if (type == TEXT_ITEM_SYNTAX || type == TEXT_ITEM_LOG) - { - free (item->style.typeface); - item->style.typeface = xstrdup ("Monospaced"); - } - - return item; + return text_item_create_value (type, pivot_value_new_user_text_nocopy (text), + label); } /* Creates and returns a new text item containing a copy of TEXT and the @@ -92,6 +78,35 @@ text_item_create (enum text_item_type type, const char *text, label ? xstrdup (label) : NULL); } +/* Creates and returns a new text item containing VALUE, TYPE, and LABEL. + Takes ownership of VALUE and LABEL. If LABEL is null, uses a default label + for TYPE. */ +struct text_item * +text_item_create_value (enum text_item_type type, struct pivot_value *value, + char *label) +{ + if (type == TEXT_ITEM_SYNTAX || type == TEXT_ITEM_LOG) + { + if (!value->font_style) + { + value->font_style = xmalloc (sizeof *value->font_style); + *value->font_style = (struct font_style) FONT_STYLE_INITIALIZER; + } + + free (value->font_style->typeface); + value->font_style->typeface = xstrdup ("Monospaced"); + } + + struct text_item *item = xzalloc (sizeof *item); + *item = (struct text_item) { + .output_item = OUTPUT_ITEM_INITIALIZER (&text_item_class), + .output_item.label = label, + .type = type, + .text = value, + }; + return item; +} + /* Returns ITEM's type. */ enum text_item_type text_item_get_type (const struct text_item *item) @@ -99,11 +114,11 @@ text_item_get_type (const struct text_item *item) return item->type; } -/* Returns ITEM's text, which the caller may not modify or free. */ -const char * -text_item_get_text (const struct text_item *item) +/* Returns ITEM's text, which the caller must eventually free. */ +char * +text_item_get_plain_text (const struct text_item *item) { - return item->text; + return pivot_value_to_string_defaults (item->text); } /* Submits ITEM to the configured output drivers, and transfers ownership to @@ -125,13 +140,19 @@ text_item_unshare (struct text_item *old) struct text_item *new = xmalloc (sizeof *new); *new = (struct text_item) { .output_item = OUTPUT_ITEM_CLONE_INITIALIZER (&old->output_item), - .text = xstrdup (old->text), + .text = pivot_value_clone (old->text), .type = old->type, }; - font_style_copy (NULL, &new->style, &old->style); return new; } +static bool +nullable_font_style_equal (const struct font_style *a, + const struct font_style *b) +{ + return a && b ? font_style_equal (a, b) : !a && !b; +} + /* Attempts to append the text in SRC to DST. If successful, returns true, otherwise false. @@ -147,14 +168,28 @@ text_item_append (struct text_item *dst, const struct text_item *src) || (dst->type != TEXT_ITEM_SYNTAX && dst->type != TEXT_ITEM_LOG) || strcmp (output_item_get_label (&dst->output_item), output_item_get_label (&src->output_item)) - || !font_style_equal (&dst->style, &src->style) - || dst->style.markup) + || !nullable_font_style_equal (dst->text->font_style, + src->text->font_style) + || (dst->text->font_style && dst->text->font_style->markup) + || src->text->type != PIVOT_VALUE_TEXT + || dst->text->type != PIVOT_VALUE_TEXT) return false; else { - char *new_text = xasprintf ("%s\n%s", dst->text, src->text); - free (dst->text); - dst->text = new_text; + char *new_text = xasprintf ("%s\n%s", dst->text->text.local, + src->text->text.local); + + free (dst->text->text.local); + if (dst->text->text.c != dst->text->text.local) + free (dst->text->text.c); + if (dst->text->text.id != dst->text->text.local + && dst->text->text.id != dst->text->text.c) + free (dst->text->text.id); + + dst->text->text.local = new_text; + dst->text->text.c = new_text; + dst->text->text.id = new_text; + return true; } } @@ -187,10 +222,7 @@ text_item_to_table_item (struct text_item *text_item) d->hide_all_labels = true; pivot_category_create_leaf (d->root, pivot_value_new_text ("null")); - struct pivot_value *content = pivot_value_new_user_text ( - text_item->text, SIZE_MAX); - pivot_value_set_font_style (content, &text_item->style); - pivot_table_put1 (table, 0, content); + pivot_table_put1 (table, 0, pivot_value_clone (text_item->text)); text_item_unref (text_item); @@ -208,8 +240,7 @@ static void text_item_destroy (struct output_item *output_item) { struct text_item *item = to_text_item (output_item); - free (item->text); - font_style_uninit (&item->style); + pivot_value_destroy (item->text); free (item); } diff --git a/src/output/text-item.h b/src/output/text-item.h index 8b4114e006..49b5bbe1eb 100644 --- a/src/output/text-item.h +++ b/src/output/text-item.h @@ -43,18 +43,20 @@ const char *text_item_type_to_string (enum text_item_type); struct text_item { struct output_item output_item; - char *text; /* The content. */ - enum text_item_type type; /* Type. */ - struct font_style style; + enum text_item_type type; + struct pivot_value *text; }; struct text_item *text_item_create (enum text_item_type, const char *text, const char *label); struct text_item *text_item_create_nocopy (enum text_item_type, char *text, char *label); +struct text_item *text_item_create_value (enum text_item_type, + struct pivot_value *value, + char *label); enum text_item_type text_item_get_type (const struct text_item *); -const char *text_item_get_text (const struct text_item *); +char *text_item_get_plain_text (const struct text_item *); struct text_item *text_item_unshare (struct text_item *); bool text_item_append (struct text_item *dst, const struct text_item *src); diff --git a/src/ui/gui/psppire-output-view.c b/src/ui/gui/psppire-output-view.c index 4a9a666cef..746f3b942b 100644 --- a/src/ui/gui/psppire-output-view.c +++ b/src/ui/gui/psppire-output-view.c @@ -419,8 +419,10 @@ psppire_output_view_put (struct psppire_output_view *view, else if (is_text_item (item)) { const struct text_item *text_item = to_text_item (item); - const char *text = text_item_get_text (text_item); - if (text[0] == '\0') + char *text = text_item_get_plain_text (text_item); + bool text_is_empty = text[0] == '\0'; + free (text); + if (text_is_empty) return; } -- 2.30.2