From 29020c71a21e3c666cf8bc92972d8c0ef46b9a9e Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Mon, 2 Jul 2012 22:07:33 -0400 Subject: [PATCH] markup: Allow incrementally parsing pango markup This is intended for applications that need to parse pango markup from some sort of GIO stream. https://bugzilla.gnome.org/show_bug.cgi?id=679299 --- docs/pango-sections.txt | 2 + docs/pango_markup.sgml | 5 +- pango/pango-attributes.h | 8 ++ pango/pango-markup.c | 230 ++++++++++++++++++++++++++++++++++------------- pango/pango.def | 2 + 5 files changed, 181 insertions(+), 66 deletions(-) diff --git a/docs/pango-sections.txt b/docs/pango-sections.txt index f22eadf..5adfa93 100644 --- a/docs/pango-sections.txt +++ b/docs/pango-sections.txt @@ -335,6 +335,8 @@ PangoAttrFontDesc PangoAttrShape PangoAttrSize pango_parse_markup +pango_markup_parser_new +pango_markup_parser_finish pango_attr_type_register pango_attr_type_get_name pango_attribute_init diff --git a/docs/pango_markup.sgml b/docs/pango_markup.sgml index 8fc430a..227b413 100644 --- a/docs/pango_markup.sgml +++ b/docs/pango_markup.sgml @@ -29,8 +29,9 @@ position. The solution is to include the text attributes in the string to be translated. Pango provides this feature with a small markup language. You can parse a marked-up string into the string text plus a -PangoAttrList using the function -pango_parse_markup(). +PangoAttrList using either of +pango_parse_markup() or +pango_markup_parser_new(). diff --git a/pango/pango-attributes.h b/pango/pango-attributes.h index 88b7d2d..7cd80d8 100644 --- a/pango/pango-attributes.h +++ b/pango/pango-attributes.h @@ -293,6 +293,14 @@ gboolean pango_parse_markup (const char *markup_text, gunichar *accel_char, GError **error); +GMarkupParseContext * pango_markup_parser_new (gunichar accel_marker, + GError **error); +gboolean pango_markup_parser_finish (GMarkupParseContext *context, + PangoAttrList **attr_list, + char **text, + gunichar *accel_char, + GError **error); + G_END_DECLS #endif /* __PANGO_ATTRIBUTES_H__ */ diff --git a/pango/pango-markup.c b/pango/pango-markup.c index a66fe50..0f83185 100644 --- a/pango/pango-markup.c +++ b/pango/pango-markup.c @@ -547,6 +547,65 @@ static const GMarkupParser pango_markup_parser = { NULL }; +static void +destroy_markup_data (MarkupData *md) +{ + g_slist_free_full (md->tag_stack, (GDestroyNotify) open_tag_free); + g_slist_free_full (md->to_apply, (GDestroyNotify) pango_attribute_destroy); + if (md->text) + g_string_free (md->text, TRUE); + + if (md->attr_list) + pango_attr_list_unref (md->attr_list); + + g_slice_free (MarkupData, md); +} + +static GMarkupParseContext * +pango_markup_parser_new_internal (char accel_marker, + GError **error, + gboolean want_attr_list) +{ + MarkupData *md; + GMarkupParseContext *context; + + md = g_slice_new (MarkupData); + + /* Don't bother creating these if they weren't requested; + * might be useful e.g. if you just want to validate + * some markup. + */ + if (want_attr_list) + md->attr_list = pango_attr_list_new (); + else + md->attr_list = NULL; + + md->text = g_string_new (NULL); + + md->accel_marker = accel_marker; + md->accel_char = 0; + + md->index = 0; + md->tag_stack = NULL; + md->to_apply = NULL; + + context = g_markup_parse_context_new (&pango_markup_parser, + 0, md, + (GDestroyNotify)destroy_markup_data); + + if (!g_markup_parse_context_parse (context, + "", + -1, + error)) + goto error; + + return context; + + error: + g_markup_parse_context_free (context); + return NULL; +} + /** * pango_parse_markup: * @markup_text: markup to parse (see markup format) @@ -569,6 +628,8 @@ static const GMarkupParser pango_markup_parser = { * Two @accel_marker characters following each other produce a single * literal @accel_marker character. * + * To parse a stream of pango markup incrementally, use pango_markup_parser_new(). + * * If any error happens, none of the output arguments are touched except * for @error. * @@ -584,39 +645,12 @@ pango_parse_markup (const char *markup_text, GError **error) { GMarkupParseContext *context = NULL; - MarkupData *md = NULL; - GSList *tmp_list; + gboolean ret = FALSE; const char *p; const char *end; g_return_val_if_fail (markup_text != NULL, FALSE); - md = g_slice_new (MarkupData); - - /* Don't bother creating these if they weren't requested; - * might be useful e.g. if you just want to validate - * some markup. - */ - if (attr_list) - md->attr_list = pango_attr_list_new (); - else - md->attr_list = NULL; - - md->text = g_string_new (NULL); - - if (accel_char) - *accel_char = 0; - - md->accel_marker = accel_marker; - md->accel_char = 0; - - md->index = 0; - md->tag_stack = NULL; - md->to_apply = NULL; - - context = g_markup_parse_context_new (&pango_markup_parser, - 0, md, NULL); - if (length < 0) length = strlen (markup_text); @@ -625,29 +659,110 @@ pango_parse_markup (const char *markup_text, while (p != end && xml_isspace (*p)) ++p; + context = pango_markup_parser_new_internal (accel_marker, + error, + (attr_list != NULL)); + if (context == NULL) + goto out; + if (!g_markup_parse_context_parse (context, - "", - -1, + markup_text, + length, error)) - goto error; + goto out; + if (!pango_markup_parser_finish (context, + attr_list, + text, + accel_char, + error)) + goto out; - if (!g_markup_parse_context_parse (context, - markup_text, - length, - error)) - goto error; + ret = TRUE; + + out: + if (context != NULL) + g_markup_parse_context_free (context); + return ret; +} + +/** + * pango_markup_parser_new: + * @accel_marker: character that precedes an accelerator, or 0 for none + * @error: address of return location for errors, or %NULL + * + * Parses marked-up text (see + * markup format) to create + * a plain-text string and an attribute list. + * + * If @accel_marker is nonzero, the given character will mark the + * character following it as an accelerator. For example, @accel_marker + * might be an ampersand or underscore. All characters marked + * as an accelerator will receive a %PANGO_UNDERLINE_LOW attribute, + * and the first character so marked will be returned in @accel_char, + * when calling finish(). Two @accel_marker characters following each + * other produce a single literal @accel_marker character. + * + * If any error happens, none of the output arguments are touched except + * for @error. + * + * To feed markup to the parser, use g_markup_parse_context_parse() + * on the returned #GMarkupParseContext. When done with feeding markup + * to the parser, use pango_markup_parser_finish() to get the data out + * of it, and then use g_markup_parse_context_free() to free it. + * + * This function is designed for applications that read pango markup + * from streams. To simply parse a string containing pango markup, + * the simpler pango_parse_markup() API is recommended instead. + * + * Return value: (transfer none): a #GMarkupParseContext that should be + * destroyed with g_markup_parse_context_free(). + * + * Since: 1.31.0 + **/ +GMarkupParseContext * +pango_markup_parser_new (gunichar accel_marker, + GError **error) +{ + return pango_markup_parser_new_internal (accel_marker, error, TRUE); +} + +/** + * pango_markup_parser_finish: + * @context: A valid parse context that was returned from pango_markup_parser_new() + * @attr_list: (out) (allow-none): address of return location for a #PangoAttrList, or %NULL + * @text: (out) (allow-none): address of return location for text with tags stripped, or %NULL + * @accel_char: (out) (allow-none): address of return location for accelerator char, or %NULL + * @error: address of return location for errors, or %NULL + * + * After feeding a pango markup parser some data with g_markup_parse_context_parse(), + * use this function to get the list of pango attributes and text out of the + * markup. This function will not free @context, use g_markup_parse_context_free() + * to do so. + * + * Return value: %FALSE if @error is set, otherwise %TRUE + * + * Since: 1.31.0 + */ +gboolean +pango_markup_parser_finish (GMarkupParseContext *context, + PangoAttrList **attr_list, + char **text, + gunichar *accel_char, + GError **error) +{ + gboolean ret = FALSE; + MarkupData *md = g_markup_parse_context_get_user_data (context); + GSList *tmp_list; if (!g_markup_parse_context_parse (context, "", -1, error)) - goto error; + goto out; if (!g_markup_parse_context_end_parse (context, error)) - goto error; - - g_markup_parse_context_free (context); + goto out; if (md->attr_list) { @@ -669,38 +784,25 @@ pango_parse_markup (const char *markup_text, } if (attr_list) - *attr_list = md->attr_list; + { + *attr_list = md->attr_list; + md->attr_list = NULL; + } if (text) - *text = g_string_free (md->text, FALSE); - else - g_string_free (md->text, TRUE); + { + *text = g_string_free (md->text, FALSE); + md->text = NULL; + } if (accel_char) *accel_char = md->accel_char; g_assert (md->tag_stack == NULL); + ret = TRUE; - g_slice_free (MarkupData, md); - - return TRUE; - - error: - g_slist_foreach (md->tag_stack, (GFunc) open_tag_free, NULL); - g_slist_free (md->tag_stack); - g_slist_foreach (md->to_apply, (GFunc) pango_attribute_destroy, NULL); - g_slist_free (md->to_apply); - g_string_free (md->text, TRUE); - - if (md->attr_list) - pango_attr_list_unref (md->attr_list); - - g_slice_free (MarkupData, md); - - if (context) - g_markup_parse_context_free (context); - - return FALSE; + out: + return ret; } static void diff --git a/pango/pango.def b/pango/pango.def index 77098e7..9605d3d 100644 --- a/pango/pango.def +++ b/pango/pango.def @@ -321,6 +321,8 @@ EXPORTS pango_lookup_aliases pango_map_get_engine pango_map_get_engines + pango_markup_parser_new + pango_markup_parser_finish pango_matrix_concat pango_matrix_copy pango_matrix_free -- 2.7.4