2 * pango-markup.c: Parse markup into attributed text
4 * Copyright (C) 2000 Red Hat Software
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
27 #include "pango-attributes.h"
28 #include "pango-font.h"
29 #include "pango-enum-types.h"
30 #include "pango-impl-utils.h"
47 typedef struct _MarkupData MarkupData;
51 PangoAttrList *attr_list;
56 gunichar accel_marker;
60 typedef struct _OpenTag OpenTag;
66 /* Current total scale level; reset whenever
67 * an absolute size is set.
68 * Each "larger" ups it 1, each "smaller" decrements it 1
71 /* Our impact on scale_level, so we know whether we
72 * need to create an attribute ourselves on close
74 gint scale_level_delta;
75 /* Base scale factor currently in effect
76 * or size that this tag
77 * forces, or parent's scale factor or size.
79 double base_scale_factor;
81 guint has_base_font_size : 1;
84 typedef gboolean (*TagParseFunc) (MarkupData *md,
88 GMarkupParseContext *context,
91 static gboolean b_parse_func (MarkupData *md,
95 GMarkupParseContext *context,
97 static gboolean big_parse_func (MarkupData *md,
100 const gchar **values,
101 GMarkupParseContext *context,
103 static gboolean span_parse_func (MarkupData *md,
106 const gchar **values,
107 GMarkupParseContext *context,
109 static gboolean i_parse_func (MarkupData *md,
112 const gchar **values,
113 GMarkupParseContext *context,
115 static gboolean markup_parse_func (MarkupData *md,
118 const gchar **values,
119 GMarkupParseContext *context,
121 static gboolean s_parse_func (MarkupData *md,
124 const gchar **values,
125 GMarkupParseContext *context,
127 static gboolean sub_parse_func (MarkupData *md,
130 const gchar **values,
131 GMarkupParseContext *context,
133 static gboolean sup_parse_func (MarkupData *md,
136 const gchar **values,
137 GMarkupParseContext *context,
139 static gboolean small_parse_func (MarkupData *md,
142 const gchar **values,
143 GMarkupParseContext *context,
145 static gboolean tt_parse_func (MarkupData *md,
148 const gchar **values,
149 GMarkupParseContext *context,
151 static gboolean u_parse_func (MarkupData *md,
154 const gchar **values,
155 GMarkupParseContext *context,
159 scale_factor (int scale_level, double base)
161 double factor = base;
164 /* 1.2 is the CSS scale factor between sizes */
169 while (i < scale_level)
176 else if (scale_level < 0)
191 open_tag_free (OpenTag *ot)
193 g_slist_foreach (ot->attrs, (GFunc) pango_attribute_destroy, NULL);
194 g_slist_free (ot->attrs);
195 g_slice_free (OpenTag, ot);
199 open_tag_set_absolute_font_size (OpenTag *ot,
202 ot->base_font_size = font_size;
203 ot->has_base_font_size = TRUE;
205 ot->scale_level_delta = 0;
209 open_tag_set_absolute_font_scale (OpenTag *ot,
212 ot->base_scale_factor = scale;
213 ot->has_base_font_size = FALSE;
215 ot->scale_level_delta = 0;
219 markup_data_open_tag (MarkupData *md)
222 OpenTag *parent = NULL;
224 if (md->attr_list == NULL)
228 parent = md->tag_stack->data;
230 ot = g_slice_new (OpenTag);
232 ot->start_index = md->index;
233 ot->scale_level_delta = 0;
237 ot->base_scale_factor = 1.0;
238 ot->base_font_size = 0;
239 ot->has_base_font_size = FALSE;
244 ot->base_scale_factor = parent->base_scale_factor;
245 ot->base_font_size = parent->base_font_size;
246 ot->has_base_font_size = parent->has_base_font_size;
247 ot->scale_level = parent->scale_level;
250 md->tag_stack = g_slist_prepend (md->tag_stack, ot);
256 markup_data_close_tag (MarkupData *md)
261 if (md->attr_list == NULL)
265 ot = md->tag_stack->data;
266 md->tag_stack = g_slist_delete_link (md->tag_stack,
269 /* Adjust end indexes, and push each attr onto the front of the
270 * to_apply list. This means that outermost tags are on the front of
271 * that list; if we apply the list in order, then the innermost
272 * tags will "win" which is correct.
274 tmp_list = ot->attrs;
275 while (tmp_list != NULL)
277 PangoAttribute *a = tmp_list->data;
279 a->start_index = ot->start_index;
280 a->end_index = md->index;
282 md->to_apply = g_slist_prepend (md->to_apply, a);
284 tmp_list = g_slist_next (tmp_list);
287 if (ot->scale_level_delta != 0)
289 /* We affected relative font size; create an appropriate
290 * attribute and reverse our effects on the current level
294 if (ot->has_base_font_size)
296 /* Create a font using the absolute point size
297 * as the base size to be scaled from
299 a = pango_attr_size_new (scale_factor (ot->scale_level,
305 /* Create a font using the current scale factor
306 * as the base size to be scaled from
308 a = pango_attr_scale_new (scale_factor (ot->scale_level,
309 ot->base_scale_factor));
312 a->start_index = ot->start_index;
313 a->end_index = md->index;
315 md->to_apply = g_slist_prepend (md->to_apply, a);
318 g_slist_free (ot->attrs);
319 g_slice_free (OpenTag, ot);
323 start_element_handler (GMarkupParseContext *context,
324 const gchar *element_name,
325 const gchar **attribute_names,
326 const gchar **attribute_values,
330 TagParseFunc parse_func = NULL;
333 switch (*element_name)
336 if (strcmp ("b", element_name) == 0)
337 parse_func = b_parse_func;
338 else if (strcmp ("big", element_name) == 0)
339 parse_func = big_parse_func;
343 if (strcmp ("i", element_name) == 0)
344 parse_func = i_parse_func;
348 if (strcmp ("markup", element_name) == 0)
349 parse_func = markup_parse_func;
353 if (strcmp ("span", element_name) == 0)
354 parse_func = span_parse_func;
355 else if (strcmp ("s", element_name) == 0)
356 parse_func = s_parse_func;
357 else if (strcmp ("sub", element_name) == 0)
358 parse_func = sub_parse_func;
359 else if (strcmp ("sup", element_name) == 0)
360 parse_func = sup_parse_func;
361 else if (strcmp ("small", element_name) == 0)
362 parse_func = small_parse_func;
366 if (strcmp ("tt", element_name) == 0)
367 parse_func = tt_parse_func;
371 if (strcmp ("u", element_name) == 0)
372 parse_func = u_parse_func;
376 if (parse_func == NULL)
378 gint line_number, char_number;
380 g_markup_parse_context_get_position (context,
381 &line_number, &char_number);
385 G_MARKUP_ERROR_UNKNOWN_ELEMENT,
386 _("Unknown tag '%s' on line %d char %d"),
388 line_number, char_number);
393 ot = markup_data_open_tag (user_data);
395 /* note ot may be NULL if the user didn't want the attribute list */
397 if (!(*parse_func) (user_data, ot,
398 attribute_names, attribute_values,
401 /* there's nothing to do; we return an error, and end up
402 * freeing ot off the tag stack later.
408 end_element_handler (GMarkupParseContext *context G_GNUC_UNUSED,
409 const gchar *element_name G_GNUC_UNUSED,
411 GError **error G_GNUC_UNUSED)
413 markup_data_close_tag (user_data);
417 text_handler (GMarkupParseContext *context G_GNUC_UNUSED,
421 GError **error G_GNUC_UNUSED)
423 MarkupData *md = user_data;
425 if (md->accel_marker == 0)
427 /* Just append all the text */
429 md->index += text_len;
431 g_string_append_len (md->text, text, text_len);
435 /* Parse the accelerator */
438 const gchar *range_start;
439 const gchar *range_end;
440 gssize uline_index = -1;
441 gsize uline_len = 0; /* Quiet GCC */
446 end = text + text_len;
452 c = g_utf8_get_char (p);
456 if (c == md->accel_marker)
458 /* escaped accel marker; move range_end
459 * past the accel marker that came before,
460 * append the whole thing
462 range_end = g_utf8_next_char (range_end);
463 g_string_append_len (md->text,
465 range_end - range_start);
466 md->index += range_end - range_start;
468 /* set next range_start, skipping accel marker */
469 range_start = g_utf8_next_char (p);
473 /* Don't append the accel marker (leave range_end
474 * alone); set the accel char to c; record location for
475 * underline attribute
477 if (md->accel_char == 0)
480 g_string_append_len (md->text,
482 range_end - range_start);
483 md->index += range_end - range_start;
485 /* The underline should go underneath the char
486 * we're setting as the next range_start
488 uline_index = md->index;
489 uline_len = g_utf8_next_char (p) - p;
491 /* set next range_start to include this char */
495 /* reset range_end */
498 else if (c == md->accel_marker)
503 p = g_utf8_next_char (p);
508 g_string_append_len (md->text,
510 range_end - range_start);
511 md->index += range_end - range_start;
515 g_string_append_len (md->text,
518 md->index += end - range_start;
521 if (md->attr_list != NULL && uline_index >= 0)
523 /* Add the underline indicating the accelerator */
524 PangoAttribute *attr;
526 attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
528 attr->start_index = uline_index;
529 attr->end_index = uline_index + uline_len;
531 pango_attr_list_change (md->attr_list, attr);
539 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
542 static const GMarkupParser pango_markup_parser = {
543 start_element_handler,
551 * pango_parse_markup:
552 * @markup_text: markup to parse (see <link linkend="PangoMarkupFormat">markup format</link>)
553 * @length: length of @markup_text, or -1 if nul-terminated
554 * @accel_marker: character that precedes an accelerator, or 0 for none
555 * @attr_list: address of return location for a #PangoAttrList, or %NULL
556 * @text: address of return location for text with tags stripped, or %NULL
557 * @accel_char: address of return location for accelerator char, or %NULL
558 * @error: address of return location for errors, or %NULL
560 * Parses marked-up text (see
561 * <link linkend="PangoMarkupFormat">markup format</link>) to create
562 * a plain-text string and an attribute list.
564 * If @accel_marker is nonzero, the given character will mark the
565 * character following it as an accelerator. For example, @accel_marker
566 * might be an ampersand or underscore. All characters marked
567 * as an accelerator will receive a %PANGO_UNDERLINE_LOW attribute,
568 * and the first character so marked will be returned in @accel_char.
569 * Two @accel_marker characters following each other produce a single
570 * literal @accel_marker character.
572 * If any error happens, none of the output arguments are touched except
575 * Return value: %FALSE if @error is set, otherwise %TRUE
578 pango_parse_markup (const char *markup_text,
580 gunichar accel_marker,
581 PangoAttrList **attr_list,
583 gunichar *accel_char,
586 GMarkupParseContext *context = NULL;
587 MarkupData *md = NULL;
588 gboolean needs_root = TRUE;
593 g_return_val_if_fail (markup_text != NULL, FALSE);
595 md = g_slice_new (MarkupData);
597 /* Don't bother creating these if they weren't requested;
598 * might be useful e.g. if you just want to validate
602 md->attr_list = pango_attr_list_new ();
604 md->attr_list = NULL;
606 md->text = g_string_new (NULL);
611 md->accel_marker = accel_marker;
615 md->tag_stack = NULL;
618 context = g_markup_parse_context_new (&pango_markup_parser,
622 length = strlen (markup_text);
625 end = markup_text + length;
626 while (p != end && xml_isspace (*p))
629 if (end - p >= 8 && strncmp (p, "<markup>", 8) == 0)
633 if (!g_markup_parse_context_parse (context,
640 if (!g_markup_parse_context_parse (context,
647 if (!g_markup_parse_context_parse (context,
653 if (!g_markup_parse_context_end_parse (context, error))
656 g_markup_parse_context_free (context);
660 /* The apply list has the most-recently-closed tags first;
661 * we want to apply the least-recently-closed tag last.
663 tmp_list = md->to_apply;
664 while (tmp_list != NULL)
666 PangoAttribute *attr = tmp_list->data;
668 /* Innermost tags before outermost */
669 pango_attr_list_insert (md->attr_list, attr);
671 tmp_list = g_slist_next (tmp_list);
673 g_slist_free (md->to_apply);
678 *attr_list = md->attr_list;
681 *text = g_string_free (md->text, FALSE);
683 g_string_free (md->text, TRUE);
686 *accel_char = md->accel_char;
688 g_assert (md->tag_stack == NULL);
690 g_slice_free (MarkupData, md);
695 g_slist_foreach (md->tag_stack, (GFunc) open_tag_free, NULL);
696 g_slist_free (md->tag_stack);
697 g_slist_foreach (md->to_apply, (GFunc) pango_attribute_destroy, NULL);
698 g_slist_free (md->to_apply);
699 g_string_free (md->text, TRUE);
702 pango_attr_list_unref (md->attr_list);
704 g_slice_free (MarkupData, md);
707 g_markup_parse_context_free (context);
713 set_bad_attribute (GError **error,
714 GMarkupParseContext *context,
715 const char *element_name,
716 const char *attribute_name)
718 gint line_number, char_number;
720 g_markup_parse_context_get_position (context,
721 &line_number, &char_number);
725 G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
726 _("Tag '%s' does not support attribute '%s' on line %d char %d"),
729 line_number, char_number);
733 add_attribute (OpenTag *ot,
734 PangoAttribute *attr)
737 pango_attribute_destroy (attr);
739 ot->attrs = g_slist_prepend (ot->attrs, attr);
742 #define CHECK_NO_ATTRS(elem) G_STMT_START { \
743 if (*names != NULL) { \
744 set_bad_attribute (error, context, (elem), *names); \
749 b_parse_func (MarkupData *md G_GNUC_UNUSED,
752 const gchar **values G_GNUC_UNUSED,
753 GMarkupParseContext *context,
757 add_attribute (tag, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
762 big_parse_func (MarkupData *md G_GNUC_UNUSED,
765 const gchar **values G_GNUC_UNUSED,
766 GMarkupParseContext *context,
769 CHECK_NO_ATTRS("big");
771 /* Grow text one level */
774 tag->scale_level_delta += 1;
775 tag->scale_level += 1;
782 parse_absolute_size (OpenTag *tag,
785 SizeLevel level = Medium;
788 if (strcmp (size, "xx-small") == 0)
790 else if (strcmp (size, "x-small") == 0)
792 else if (strcmp (size, "small") == 0)
794 else if (strcmp (size, "medium") == 0)
796 else if (strcmp (size, "large") == 0)
798 else if (strcmp (size, "x-large") == 0)
800 else if (strcmp (size, "xx-large") == 0)
805 /* This is "absolute" in that it's relative to the base font,
806 * but not to sizes created by any other tags
808 factor = scale_factor (level, 1.0);
809 add_attribute (tag, pango_attr_scale_new (factor));
811 open_tag_set_absolute_font_scale (tag, factor);
816 /* a string compare func that ignores '-' vs '_' differences */
818 attr_strcmp (gconstpointer pa,
835 ca = ca == '_' ? '-' : ca;
836 cb = cb == '_' ? '-' : cb;
849 span_parse_int (const char *attr_name,
850 const char *attr_val,
855 const char *end = attr_val;
857 if (!pango_scan_int (&end, val) || *end != '\0')
861 G_MARKUP_ERROR_INVALID_CONTENT,
862 _("Value of '%s' attribute on <span> tag "
863 "on line %d could not be parsed; "
864 "should be an integer, not '%s'"),
865 attr_name, line_number, attr_val);
873 span_parse_boolean (const char *attr_name,
874 const char *attr_val,
879 if (strcmp (attr_val, "true") == 0 ||
880 strcmp (attr_val, "yes") == 0 ||
881 strcmp (attr_val, "t") == 0 ||
882 strcmp (attr_val, "y") == 0)
884 else if (strcmp (attr_val, "false") == 0 ||
885 strcmp (attr_val, "no") == 0 ||
886 strcmp (attr_val, "f") == 0 ||
887 strcmp (attr_val, "n") == 0)
893 G_MARKUP_ERROR_INVALID_CONTENT,
894 _("Value of '%s' attribute on <span> tag "
895 "line %d should have one of "
896 "'true/yes/t/y' or 'false/no/f/n': '%s' is not valid"),
897 attr_name, line_number, attr_val);
905 span_parse_color (const char *attr_name,
906 const char *attr_val,
911 if (!pango_color_parse (color, attr_val))
915 G_MARKUP_ERROR_INVALID_CONTENT,
916 _("Value of '%s' attribute on <span> tag "
917 "on line %d could not be parsed; "
918 "should be a color specification, not '%s'"),
919 attr_name, line_number, attr_val);
927 span_parse_enum (const char *attr_name,
928 const char *attr_val,
934 char *possible_values = NULL;
936 if (!pango_parse_enum (type, attr_val, val, FALSE, &possible_values))
940 G_MARKUP_ERROR_INVALID_CONTENT,
941 _("'%s' is not a valid value for the '%s' "
942 "attribute on <span> tag, line %d; valid "
944 attr_val, attr_name, line_number, possible_values);
945 g_free (possible_values);
953 span_parse_func (MarkupData *md G_GNUC_UNUSED,
956 const gchar **values,
957 GMarkupParseContext *context,
960 int line_number, char_number;
963 const char *family = NULL;
964 const char *size = NULL;
965 const char *style = NULL;
966 const char *weight = NULL;
967 const char *variant = NULL;
968 const char *stretch = NULL;
969 const char *desc = NULL;
970 const char *foreground = NULL;
971 const char *background = NULL;
972 const char *underline = NULL;
973 const char *underline_color = NULL;
974 const char *strikethrough = NULL;
975 const char *strikethrough_color = NULL;
976 const char *rise = NULL;
977 const char *letter_spacing = NULL;
978 const char *lang = NULL;
979 const char *fallback = NULL;
980 const char *gravity = NULL;
981 const char *gravity_hint = NULL;
983 g_markup_parse_context_get_position (context,
984 &line_number, &char_number);
986 #define CHECK_DUPLICATE(var) G_STMT_START{ \
987 if ((var) != NULL) { \
988 g_set_error (error, G_MARKUP_ERROR, \
989 G_MARKUP_ERROR_INVALID_CONTENT, \
990 _("Attribute '%s' occurs twice on <span> tag " \
991 "on line %d char %d, may only occur once"), \
992 names[i], line_number, char_number); \
995 #define CHECK_ATTRIBUTE2(var, name) \
996 if (attr_strcmp (names[i], (name)) == 0) { \
997 CHECK_DUPLICATE (var); \
1002 #define CHECK_ATTRIBUTE(var) CHECK_ATTRIBUTE2 (var, G_STRINGIFY (var))
1007 gboolean found = FALSE;
1009 switch (names[i][0]) {
1011 CHECK_ATTRIBUTE (fallback);
1012 CHECK_ATTRIBUTE2(desc, "font");
1013 CHECK_ATTRIBUTE2(desc, "font_desc");
1014 CHECK_ATTRIBUTE2(family, "face");
1016 CHECK_ATTRIBUTE2(family, "font_family");
1017 CHECK_ATTRIBUTE2(size, "font_size");
1018 CHECK_ATTRIBUTE2(stretch, "font_stretch");
1019 CHECK_ATTRIBUTE2(style, "font_style");
1020 CHECK_ATTRIBUTE2(variant, "font_variant");
1021 CHECK_ATTRIBUTE2(weight, "font_weight");
1023 CHECK_ATTRIBUTE (foreground);
1024 CHECK_ATTRIBUTE2 (foreground, "fgcolor");
1027 CHECK_ATTRIBUTE (size);
1028 CHECK_ATTRIBUTE (stretch);
1029 CHECK_ATTRIBUTE (strikethrough);
1030 CHECK_ATTRIBUTE (strikethrough_color);
1031 CHECK_ATTRIBUTE (style);
1034 CHECK_ATTRIBUTE (gravity);
1035 CHECK_ATTRIBUTE (gravity_hint);
1038 CHECK_ATTRIBUTE (lang);
1039 CHECK_ATTRIBUTE (letter_spacing);
1042 CHECK_ATTRIBUTE (underline);
1043 CHECK_ATTRIBUTE (underline_color);
1046 CHECK_ATTRIBUTE (background);
1047 CHECK_ATTRIBUTE2 (background, "bgcolor");
1048 CHECK_ATTRIBUTE2(foreground, "color");
1049 CHECK_ATTRIBUTE (rise);
1050 CHECK_ATTRIBUTE (variant);
1051 CHECK_ATTRIBUTE (weight);
1057 g_set_error (error, G_MARKUP_ERROR,
1058 G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
1059 _("Attribute '%s' is not allowed on the <span> tag "
1060 "on line %d char %d"),
1061 names[i], line_number, char_number);
1068 /* Parse desc first, then modify it with other font-related attributes. */
1069 if (G_UNLIKELY (desc))
1071 PangoFontDescription *parsed;
1073 parsed = pango_font_description_from_string (desc);
1076 add_attribute (tag, pango_attr_font_desc_new (parsed));
1078 open_tag_set_absolute_font_size (tag, pango_font_description_get_size (parsed));
1079 pango_font_description_free (parsed);
1083 if (G_UNLIKELY (family))
1085 add_attribute (tag, pango_attr_family_new (family));
1088 if (G_UNLIKELY (size))
1090 if (g_ascii_isdigit (*size))
1095 /* cap size from the top at an arbitrary 2048 */
1096 #define MAX_SIZE (2048 * PANGO_SCALE)
1098 if ((end = size, !pango_scan_int (&end, &n)) || *end != '\0' || n < 0 || n > MAX_SIZE)
1102 G_MARKUP_ERROR_INVALID_CONTENT,
1103 _("Value of 'size' attribute on <span> tag on line %d "
1104 "could not be parsed; should be an integer less than %d, or a "
1105 "string such as 'small', not '%s'"),
1106 line_number, MAX_SIZE+1, size);
1110 add_attribute (tag, pango_attr_size_new (n));
1112 open_tag_set_absolute_font_size (tag, n);
1114 else if (strcmp (size, "smaller") == 0)
1118 tag->scale_level_delta -= 1;
1119 tag->scale_level -= 1;
1122 else if (strcmp (size, "larger") == 0)
1126 tag->scale_level_delta += 1;
1127 tag->scale_level += 1;
1130 else if (parse_absolute_size (tag, size))
1136 G_MARKUP_ERROR_INVALID_CONTENT,
1137 _("Value of 'size' attribute on <span> tag on line %d "
1138 "could not be parsed; should be an integer, or a "
1139 "string such as 'small', not '%s'"),
1145 if (G_UNLIKELY (style))
1147 PangoStyle pango_style;
1149 if (pango_parse_style (style, &pango_style, FALSE))
1150 add_attribute (tag, pango_attr_style_new (pango_style));
1155 G_MARKUP_ERROR_INVALID_CONTENT,
1156 _("'%s' is not a valid value for the 'style' attribute "
1157 "on <span> tag, line %d; valid values are "
1158 "'normal', 'oblique', 'italic'"),
1159 style, line_number);
1164 if (G_UNLIKELY (weight))
1166 PangoWeight pango_weight;
1168 if (pango_parse_weight (weight, &pango_weight, FALSE))
1170 pango_attr_weight_new (pango_weight));
1175 G_MARKUP_ERROR_INVALID_CONTENT,
1176 _("'%s' is not a valid value for the 'weight' "
1177 "attribute on <span> tag, line %d; valid "
1178 "values are for example 'light', 'ultrabold' or a number"),
1179 weight, line_number);
1184 if (G_UNLIKELY (variant))
1186 PangoVariant pango_variant;
1188 if (pango_parse_variant (variant, &pango_variant, FALSE))
1189 add_attribute (tag, pango_attr_variant_new (pango_variant));
1194 G_MARKUP_ERROR_INVALID_CONTENT,
1195 _("'%s' is not a valid value for the 'variant' "
1196 "attribute on <span> tag, line %d; valid values are "
1197 "'normal', 'smallcaps'"),
1198 variant, line_number);
1203 if (G_UNLIKELY (stretch))
1205 PangoStretch pango_stretch;
1207 if (pango_parse_stretch (stretch, &pango_stretch, FALSE))
1208 add_attribute (tag, pango_attr_stretch_new (pango_stretch));
1213 G_MARKUP_ERROR_INVALID_CONTENT,
1214 _("'%s' is not a valid value for the 'stretch' "
1215 "attribute on <span> tag, line %d; valid "
1216 "values are for example 'condensed', "
1217 "'ultraexpanded', 'normal'"),
1218 stretch, line_number);
1223 if (G_UNLIKELY (foreground))
1227 if (!span_parse_color ("foreground", foreground, &color, line_number, error))
1230 add_attribute (tag, pango_attr_foreground_new (color.red, color.green, color.blue));
1233 if (G_UNLIKELY (background))
1237 if (!span_parse_color ("background", background, &color, line_number, error))
1240 add_attribute (tag, pango_attr_background_new (color.red, color.green, color.blue));
1243 if (G_UNLIKELY (underline))
1245 PangoUnderline ul = PANGO_UNDERLINE_NONE;
1247 if (!span_parse_enum ("underline", underline, PANGO_TYPE_UNDERLINE, (int*)(void*)&ul, line_number, error))
1250 add_attribute (tag, pango_attr_underline_new (ul));
1253 if (G_UNLIKELY (underline_color))
1257 if (!span_parse_color ("underline_color", underline_color, &color, line_number, error))
1260 add_attribute (tag, pango_attr_underline_color_new (color.red, color.green, color.blue));
1263 if (G_UNLIKELY (gravity))
1265 PangoGravity gr = PANGO_GRAVITY_SOUTH;
1267 if (!span_parse_enum ("gravity", gravity, PANGO_TYPE_GRAVITY, (int*)(void*)&gr, line_number, error))
1270 add_attribute (tag, pango_attr_gravity_new (gr));
1273 if (G_UNLIKELY (gravity_hint))
1275 PangoGravityHint hint = PANGO_GRAVITY_HINT_NATURAL;
1277 if (!span_parse_enum ("gravity_hint", gravity_hint, PANGO_TYPE_GRAVITY_HINT, (int*)(void*)&hint, line_number, error))
1280 add_attribute (tag, pango_attr_gravity_hint_new (hint));
1283 if (G_UNLIKELY (strikethrough))
1287 if (!span_parse_boolean ("strikethrough", strikethrough, &b, line_number, error))
1290 add_attribute (tag, pango_attr_strikethrough_new (b));
1293 if (G_UNLIKELY (strikethrough_color))
1297 if (!span_parse_color ("strikethrough_color", strikethrough_color, &color, line_number, error))
1300 add_attribute (tag, pango_attr_strikethrough_color_new (color.red, color.green, color.blue));
1303 if (G_UNLIKELY (fallback))
1307 if (!span_parse_boolean ("fallback", fallback, &b, line_number, error))
1310 add_attribute (tag, pango_attr_fallback_new (b));
1313 if (G_UNLIKELY (rise))
1317 if (!span_parse_int ("rise", rise, &n, line_number, error))
1320 add_attribute (tag, pango_attr_rise_new (n));
1323 if (G_UNLIKELY (letter_spacing))
1327 if (!span_parse_int ("letter_spacing", letter_spacing, &n, line_number, error))
1330 add_attribute (tag, pango_attr_letter_spacing_new (n));
1333 if (G_UNLIKELY (lang))
1336 pango_attr_language_new (pango_language_from_string (lang)));
1347 i_parse_func (MarkupData *md G_GNUC_UNUSED,
1349 const gchar **names,
1350 const gchar **values G_GNUC_UNUSED,
1351 GMarkupParseContext *context,
1354 CHECK_NO_ATTRS("i");
1355 add_attribute (tag, pango_attr_style_new (PANGO_STYLE_ITALIC));
1361 markup_parse_func (MarkupData *md G_GNUC_UNUSED,
1362 OpenTag *tag G_GNUC_UNUSED,
1363 const gchar **names G_GNUC_UNUSED,
1364 const gchar **values G_GNUC_UNUSED,
1365 GMarkupParseContext *context G_GNUC_UNUSED,
1366 GError **error G_GNUC_UNUSED)
1368 /* We don't do anything with this tag at the moment. */
1374 s_parse_func (MarkupData *md G_GNUC_UNUSED,
1376 const gchar **names,
1377 const gchar **values G_GNUC_UNUSED,
1378 GMarkupParseContext *context,
1381 CHECK_NO_ATTRS("s");
1382 add_attribute (tag, pango_attr_strikethrough_new (TRUE));
1387 #define SUPERSUB_RISE 5000
1390 sub_parse_func (MarkupData *md G_GNUC_UNUSED,
1392 const gchar **names,
1393 const gchar **values G_GNUC_UNUSED,
1394 GMarkupParseContext *context,
1397 CHECK_NO_ATTRS("sub");
1399 /* Shrink font, and set a negative rise */
1402 tag->scale_level_delta -= 1;
1403 tag->scale_level -= 1;
1406 add_attribute (tag, pango_attr_rise_new (-SUPERSUB_RISE));
1412 sup_parse_func (MarkupData *md G_GNUC_UNUSED,
1414 const gchar **names,
1415 const gchar **values G_GNUC_UNUSED,
1416 GMarkupParseContext *context,
1419 CHECK_NO_ATTRS("sup");
1421 /* Shrink font, and set a positive rise */
1424 tag->scale_level_delta -= 1;
1425 tag->scale_level -= 1;
1428 add_attribute (tag, pango_attr_rise_new (SUPERSUB_RISE));
1434 small_parse_func (MarkupData *md G_GNUC_UNUSED,
1436 const gchar **names,
1437 const gchar **values G_GNUC_UNUSED,
1438 GMarkupParseContext *context,
1441 CHECK_NO_ATTRS("small");
1443 /* Shrink text one level */
1446 tag->scale_level_delta -= 1;
1447 tag->scale_level -= 1;
1454 tt_parse_func (MarkupData *md G_GNUC_UNUSED,
1456 const gchar **names,
1457 const gchar **values G_GNUC_UNUSED,
1458 GMarkupParseContext *context,
1461 CHECK_NO_ATTRS("tt");
1463 add_attribute (tag, pango_attr_family_new ("Monospace"));
1469 u_parse_func (MarkupData *md G_GNUC_UNUSED,
1471 const gchar **names,
1472 const gchar **values G_GNUC_UNUSED,
1473 GMarkupParseContext *context,
1476 CHECK_NO_ATTRS("u");
1477 add_attribute (tag, pango_attr_underline_new (PANGO_UNDERLINE_SINGLE));